mirror of
https://github.com/JakubMelka/PDF4QT.git
synced 2025-06-05 21:59:17 +02:00
Merge remote-tracking branch 'remotes/origin/signdoc'
This commit is contained in:
@@ -15,7 +15,7 @@
|
||||
# You should have received a copy of the GNU Lesser General Public License
|
||||
# along with PDF4QT. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
QT += gui widgets xml
|
||||
QT += gui widgets xml svg
|
||||
|
||||
TARGET = Pdf4QtLib
|
||||
TEMPLATE = lib
|
||||
@@ -72,6 +72,10 @@ SOURCES += \
|
||||
sources/pdfoptimizer.cpp \
|
||||
sources/pdfoptionalcontent.cpp \
|
||||
sources/pdfoutline.cpp \
|
||||
sources/pdfpagecontenteditorstylesettings.cpp \
|
||||
sources/pdfpagecontenteditortools.cpp \
|
||||
sources/pdfpagecontenteditorwidget.cpp \
|
||||
sources/pdfpagecontentelements.cpp \
|
||||
sources/pdfpagenavigation.cpp \
|
||||
sources/pdfpagetransition.cpp \
|
||||
sources/pdfpainterutils.cpp \
|
||||
@@ -87,6 +91,7 @@ SOURCES += \
|
||||
sources/pdfsignaturehandler.cpp \
|
||||
sources/pdfsnapper.cpp \
|
||||
sources/pdfstructuretree.cpp \
|
||||
sources/pdftexteditpseudowidget.cpp \
|
||||
sources/pdftextlayout.cpp \
|
||||
sources/pdftransparencyrenderer.cpp \
|
||||
sources/pdfutils.cpp \
|
||||
@@ -147,6 +152,10 @@ HEADERS += \
|
||||
sources/pdfoptimizer.h \
|
||||
sources/pdfoptionalcontent.h \
|
||||
sources/pdfoutline.h \
|
||||
sources/pdfpagecontenteditorstylesettings.h \
|
||||
sources/pdfpagecontenteditortools.h \
|
||||
sources/pdfpagecontenteditorwidget.h \
|
||||
sources/pdfpagecontentelements.h \
|
||||
sources/pdfpagenavigation.h \
|
||||
sources/pdfpagetransition.h \
|
||||
sources/pdfpainterutils.h \
|
||||
@@ -165,6 +174,7 @@ HEADERS += \
|
||||
sources/pdfsignaturehandler_impl.h \
|
||||
sources/pdfsnapper.h \
|
||||
sources/pdfstructuretree.h \
|
||||
sources/pdftexteditpseudowidget.h \
|
||||
sources/pdftextlayout.h \
|
||||
sources/pdftransparencyrenderer.h \
|
||||
sources/pdfwidgettool.h \
|
||||
@@ -194,6 +204,8 @@ HEADERS += \
|
||||
sources/pdfimage.h
|
||||
|
||||
FORMS += \
|
||||
sources/pdfpagecontenteditorstylesettings.ui \
|
||||
sources/pdfpagecontenteditorwidget.ui \
|
||||
sources/pdfrenderingerrorswidget.ui \
|
||||
sources/pdfselectpagesdialog.ui
|
||||
|
||||
|
@@ -659,7 +659,7 @@ void PDFCreateFreehandCurveTool::mousePressEvent(QWidget* widget, QMouseEvent* e
|
||||
resetTool();
|
||||
}
|
||||
|
||||
getProxy()->repaintNeeded();
|
||||
emit getProxy()->repaintNeeded();
|
||||
}
|
||||
|
||||
void PDFCreateFreehandCurveTool::mouseReleaseEvent(QWidget* widget, QMouseEvent* event)
|
||||
@@ -1063,7 +1063,7 @@ void PDFCreateHighlightTextTool::setSelection(PDFTextSelection&& textSelection)
|
||||
if (m_textSelection != textSelection)
|
||||
{
|
||||
m_textSelection = qMove(textSelection);
|
||||
getProxy()->repaintNeeded();
|
||||
emit getProxy()->repaintNeeded();
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -166,7 +166,8 @@ private:
|
||||
public:
|
||||
explicit PDFCreateEllipseTool(PDFDrawWidgetProxy* proxy, PDFToolManager* toolManager, QAction* action, QObject* parent);
|
||||
|
||||
virtual void drawPage(QPainter* painter, PDFInteger pageIndex,
|
||||
virtual void drawPage(QPainter* painter,
|
||||
PDFInteger pageIndex,
|
||||
const PDFPrecompiledPage* compiledPage,
|
||||
PDFTextLayoutGetter& layoutGetter,
|
||||
const QMatrix& pagePointToDevicePointMatrix,
|
||||
|
@@ -501,10 +501,11 @@ public:
|
||||
{
|
||||
None = 0x0000, ///< No flag
|
||||
Reset = 0x0001, ///< Whole document content is changed (for example, new document is being set)
|
||||
Annotation = 0x0002, ///< Annotations changed
|
||||
FormField = 0x0004, ///< Form field content changed
|
||||
Authorization = 0x0008, ///< Authorization has changed (for example, old document is granted user access, but for new, owner access)
|
||||
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)
|
||||
PageContents = 0x0002, ///< Page contents changed (page graphics, not annotations)
|
||||
Annotation = 0x0004, ///< Annotations changed
|
||||
FormField = 0x0008, ///< Form field content changed
|
||||
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)
|
||||
@@ -550,6 +551,7 @@ public:
|
||||
ModificationFlags getFlags() const { return m_flags; }
|
||||
|
||||
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); }
|
||||
|
||||
operator PDFDocument*() const { return m_document; }
|
||||
|
@@ -22,6 +22,8 @@
|
||||
#include "pdfobjectutils.h"
|
||||
#include "pdfnametreeloader.h"
|
||||
#include "pdfdbgheap.h"
|
||||
#include "pdfparser.h"
|
||||
#include "pdfstreamfilters.h"
|
||||
|
||||
#include <QBuffer>
|
||||
#include <QPainter>
|
||||
@@ -698,14 +700,23 @@ PDFDocument PDFDocumentBuilder::build()
|
||||
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
|
||||
{
|
||||
return { qAbs(innerRect.left() - boundingRect.left()), qAbs(boundingRect.bottom() - innerRect.bottom()), qAbs(boundingRect.right() - innerRect.right()), qAbs(boundingRect.top() - innerRect.top()) };
|
||||
}
|
||||
|
||||
PDFPageContentStreamBuilder::PDFPageContentStreamBuilder(PDFDocumentBuilder* builder) :
|
||||
PDFPageContentStreamBuilder::PDFPageContentStreamBuilder(PDFDocumentBuilder* builder,
|
||||
PDFContentStreamBuilder::CoordinateSystem coordinateSystem,
|
||||
Mode mode) :
|
||||
m_documentBuilder(builder),
|
||||
m_contentStreamBuilder(nullptr)
|
||||
m_contentStreamBuilder(nullptr),
|
||||
m_coordinateSystem(coordinateSystem),
|
||||
m_mode(mode)
|
||||
{
|
||||
|
||||
}
|
||||
@@ -733,7 +744,7 @@ QPainter* PDFPageContentStreamBuilder::begin(PDFObjectReference page)
|
||||
}
|
||||
|
||||
m_pageReference = page;
|
||||
m_contentStreamBuilder = new PDFContentStreamBuilder(mediaBox.size(), PDFContentStreamBuilder::CoordinateSystem::Qt);
|
||||
m_contentStreamBuilder = new PDFContentStreamBuilder(mediaBox.size(), m_coordinateSystem);
|
||||
return m_contentStreamBuilder->begin();
|
||||
}
|
||||
|
||||
@@ -762,21 +773,223 @@ void PDFPageContentStreamBuilder::end(QPainter* painter)
|
||||
PDFObjectReference resourcesReference = copiedObjects[0].getReference();
|
||||
PDFObjectReference contentsReference = copiedObjects[1].getReference();
|
||||
|
||||
PDFObjectFactory pageUpdateFactory;
|
||||
if (m_mode == Mode::Replace)
|
||||
{
|
||||
PDFObjectFactory pageUpdateFactory;
|
||||
|
||||
pageUpdateFactory.beginDictionary();
|
||||
pageUpdateFactory.beginDictionary();
|
||||
|
||||
pageUpdateFactory.beginDictionaryItem("Contents");
|
||||
pageUpdateFactory << contentsReference;
|
||||
pageUpdateFactory.endDictionaryItem();
|
||||
pageUpdateFactory.beginDictionaryItem("Contents");
|
||||
pageUpdateFactory << contentsReference;
|
||||
pageUpdateFactory.endDictionaryItem();
|
||||
|
||||
pageUpdateFactory.beginDictionaryItem("Resources");
|
||||
pageUpdateFactory << resourcesReference;
|
||||
pageUpdateFactory.endDictionaryItem();
|
||||
pageUpdateFactory.beginDictionaryItem("Resources");
|
||||
pageUpdateFactory << resourcesReference;
|
||||
pageUpdateFactory.endDictionaryItem();
|
||||
|
||||
pageUpdateFactory.endDictionary();
|
||||
pageUpdateFactory.endDictionary();
|
||||
|
||||
m_documentBuilder->mergeTo(m_pageReference, pageUpdateFactory.takeObject());
|
||||
m_documentBuilder->mergeTo(m_pageReference, pageUpdateFactory.takeObject());
|
||||
}
|
||||
else
|
||||
{
|
||||
std::vector<PDFObjectReference> contentReferences;
|
||||
PDFObject pageObject = m_documentBuilder->getObjectByReference(m_pageReference);
|
||||
|
||||
if (pageObject.isDictionary())
|
||||
{
|
||||
const PDFDictionary* pageDictionary = pageObject.getDictionary();
|
||||
const PDFObject& oldContents = pageDictionary->get("Contents");
|
||||
const PDFObject& oldContentsObject = m_documentBuilder->getObject(oldContents);
|
||||
|
||||
if (oldContentsObject.isStream())
|
||||
{
|
||||
if (oldContents.isReference())
|
||||
{
|
||||
contentReferences.push_back(oldContents.getReference());
|
||||
}
|
||||
}
|
||||
else if (oldContentsObject.isArray())
|
||||
{
|
||||
const PDFArray* contentsArray = oldContentsObject.getArray();
|
||||
for (const PDFObject& object : *contentsArray)
|
||||
{
|
||||
if (object.isReference())
|
||||
{
|
||||
contentReferences.push_back(object.getReference());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
PDFObject oldResourcesObject = pageDictionary->get("Resources");
|
||||
replaceResources(contentsReference, resourcesReference, oldResourcesObject);
|
||||
m_documentBuilder->mergeTo(resourcesReference, m_documentBuilder->getObject(oldResourcesObject));
|
||||
}
|
||||
|
||||
switch (m_mode)
|
||||
{
|
||||
case Mode::PlaceBefore:
|
||||
contentReferences.insert(contentReferences.begin(), contentsReference);
|
||||
break;
|
||||
|
||||
case Mode::PlaceAfter:
|
||||
contentReferences.push_back(contentsReference);
|
||||
break;
|
||||
|
||||
default:
|
||||
Q_ASSERT(false);
|
||||
break;
|
||||
}
|
||||
|
||||
PDFObjectFactory pageUpdateFactory;
|
||||
|
||||
pageUpdateFactory.beginDictionary();
|
||||
|
||||
pageUpdateFactory.beginDictionaryItem("Contents");
|
||||
pageUpdateFactory << contentReferences;
|
||||
pageUpdateFactory.endDictionaryItem();
|
||||
|
||||
pageUpdateFactory.beginDictionaryItem("Resources");
|
||||
pageUpdateFactory << resourcesReference;
|
||||
pageUpdateFactory.endDictionaryItem();
|
||||
|
||||
pageUpdateFactory.endDictionary();
|
||||
|
||||
m_documentBuilder->mergeTo(m_pageReference, pageUpdateFactory.takeObject());
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
@@ -1715,6 +1928,30 @@ PDFObjectReference PDFDocumentBuilder::appendPage(QRectF mediaBox)
|
||||
}
|
||||
|
||||
|
||||
PDFObjectReference PDFDocumentBuilder::createAcroForm(PDFObjectReferenceVector fields)
|
||||
{
|
||||
PDFObjectFactory objectBuilder;
|
||||
|
||||
objectBuilder.beginDictionary();
|
||||
objectBuilder.beginDictionaryItem("Fields");
|
||||
objectBuilder << fields;
|
||||
objectBuilder.endDictionaryItem();
|
||||
objectBuilder.beginDictionaryItem("NeedAppearances");
|
||||
objectBuilder << false;
|
||||
objectBuilder.endDictionaryItem();
|
||||
objectBuilder.beginDictionaryItem("SigFlags");
|
||||
objectBuilder << 0;
|
||||
objectBuilder.endDictionaryItem();
|
||||
objectBuilder.beginDictionaryItem("XFA");
|
||||
objectBuilder << PDFObject();
|
||||
objectBuilder.endDictionaryItem();
|
||||
objectBuilder.endDictionary();
|
||||
PDFObjectReference acroForm = addObject(objectBuilder.takeObject());
|
||||
setCatalogAcroForm(acroForm);
|
||||
return acroForm;
|
||||
}
|
||||
|
||||
|
||||
PDFObjectReference PDFDocumentBuilder::createActionGoTo(PDFDestination destination)
|
||||
{
|
||||
PDFObjectFactory objectBuilder;
|
||||
@@ -2479,88 +2716,6 @@ PDFObjectReference PDFDocumentBuilder::createAnnotationFileAttachment(PDFObjectR
|
||||
}
|
||||
|
||||
|
||||
PDFObjectReference PDFDocumentBuilder::createAnnotationFreeText(PDFObjectReference page,
|
||||
QRectF boundingRectangle,
|
||||
QRectF textRectangle,
|
||||
QString title,
|
||||
QString subject,
|
||||
QString contents,
|
||||
TextAlignment textAlignment,
|
||||
QPointF startPoint,
|
||||
QPointF endPoint,
|
||||
AnnotationLineEnding startLineType,
|
||||
AnnotationLineEnding endLineType)
|
||||
{
|
||||
PDFObjectFactory objectBuilder;
|
||||
|
||||
objectBuilder.beginDictionary();
|
||||
objectBuilder.beginDictionaryItem("Type");
|
||||
objectBuilder << WrapName("Annot");
|
||||
objectBuilder.endDictionaryItem();
|
||||
objectBuilder.beginDictionaryItem("Subtype");
|
||||
objectBuilder << WrapName("FreeText");
|
||||
objectBuilder.endDictionaryItem();
|
||||
objectBuilder.beginDictionaryItem("Rect");
|
||||
objectBuilder << boundingRectangle;
|
||||
objectBuilder.endDictionaryItem();
|
||||
objectBuilder.beginDictionaryItem("F");
|
||||
objectBuilder << 4;
|
||||
objectBuilder.endDictionaryItem();
|
||||
objectBuilder.beginDictionaryItem("P");
|
||||
objectBuilder << page;
|
||||
objectBuilder.endDictionaryItem();
|
||||
objectBuilder.beginDictionaryItem("M");
|
||||
objectBuilder << WrapCurrentDateTime();
|
||||
objectBuilder.endDictionaryItem();
|
||||
objectBuilder.beginDictionaryItem("CreationDate");
|
||||
objectBuilder << WrapCurrentDateTime();
|
||||
objectBuilder.endDictionaryItem();
|
||||
objectBuilder.beginDictionaryItem("T");
|
||||
objectBuilder << title;
|
||||
objectBuilder.endDictionaryItem();
|
||||
objectBuilder.beginDictionaryItem("Contents");
|
||||
objectBuilder << contents;
|
||||
objectBuilder.endDictionaryItem();
|
||||
objectBuilder.beginDictionaryItem("Subj");
|
||||
objectBuilder << subject;
|
||||
objectBuilder.endDictionaryItem();
|
||||
objectBuilder.beginDictionaryItem("Q");
|
||||
objectBuilder << WrapFreeTextAlignment(textAlignment);
|
||||
objectBuilder.endDictionaryItem();
|
||||
objectBuilder.beginDictionaryItem("DA");
|
||||
objectBuilder << WrapString("/Arial 10 Tf");
|
||||
objectBuilder.endDictionaryItem();
|
||||
objectBuilder.beginDictionaryItem("RD");
|
||||
objectBuilder << getAnnotationReductionRectangle(boundingRectangle, textRectangle);
|
||||
objectBuilder.endDictionaryItem();
|
||||
objectBuilder.beginDictionaryItem("CL");
|
||||
objectBuilder.beginArray();
|
||||
objectBuilder << startPoint;
|
||||
objectBuilder << endPoint;
|
||||
objectBuilder.endArray();
|
||||
objectBuilder.endDictionaryItem();
|
||||
objectBuilder.beginDictionaryItem("LE");
|
||||
objectBuilder.beginArray();
|
||||
objectBuilder << startLineType;
|
||||
objectBuilder << endLineType;
|
||||
objectBuilder.endArray();
|
||||
objectBuilder.endDictionaryItem();
|
||||
objectBuilder.endDictionary();
|
||||
PDFObjectReference annotationObject = addObject(objectBuilder.takeObject());
|
||||
objectBuilder.beginDictionary();
|
||||
objectBuilder.beginDictionaryItem("Annots");
|
||||
objectBuilder.beginArray();
|
||||
objectBuilder << annotationObject;
|
||||
objectBuilder.endArray();
|
||||
objectBuilder.endDictionaryItem();
|
||||
objectBuilder.endDictionary();
|
||||
PDFObject pageAnnots = objectBuilder.takeObject();
|
||||
appendTo(page, pageAnnots);
|
||||
updateAnnotationAppearanceStreams(annotationObject);
|
||||
return annotationObject;
|
||||
}
|
||||
|
||||
|
||||
PDFObjectReference PDFDocumentBuilder::createAnnotationFreeText(PDFObjectReference page,
|
||||
QRectF boundingRectangle,
|
||||
QRectF textRectangle,
|
||||
@@ -2707,6 +2862,88 @@ PDFObjectReference PDFDocumentBuilder::createAnnotationFreeText(PDFObjectReferen
|
||||
}
|
||||
|
||||
|
||||
PDFObjectReference PDFDocumentBuilder::createAnnotationFreeText(PDFObjectReference page,
|
||||
QRectF boundingRectangle,
|
||||
QRectF textRectangle,
|
||||
QString title,
|
||||
QString subject,
|
||||
QString contents,
|
||||
TextAlignment textAlignment,
|
||||
QPointF startPoint,
|
||||
QPointF endPoint,
|
||||
AnnotationLineEnding startLineType,
|
||||
AnnotationLineEnding endLineType)
|
||||
{
|
||||
PDFObjectFactory objectBuilder;
|
||||
|
||||
objectBuilder.beginDictionary();
|
||||
objectBuilder.beginDictionaryItem("Type");
|
||||
objectBuilder << WrapName("Annot");
|
||||
objectBuilder.endDictionaryItem();
|
||||
objectBuilder.beginDictionaryItem("Subtype");
|
||||
objectBuilder << WrapName("FreeText");
|
||||
objectBuilder.endDictionaryItem();
|
||||
objectBuilder.beginDictionaryItem("Rect");
|
||||
objectBuilder << boundingRectangle;
|
||||
objectBuilder.endDictionaryItem();
|
||||
objectBuilder.beginDictionaryItem("F");
|
||||
objectBuilder << 4;
|
||||
objectBuilder.endDictionaryItem();
|
||||
objectBuilder.beginDictionaryItem("P");
|
||||
objectBuilder << page;
|
||||
objectBuilder.endDictionaryItem();
|
||||
objectBuilder.beginDictionaryItem("M");
|
||||
objectBuilder << WrapCurrentDateTime();
|
||||
objectBuilder.endDictionaryItem();
|
||||
objectBuilder.beginDictionaryItem("CreationDate");
|
||||
objectBuilder << WrapCurrentDateTime();
|
||||
objectBuilder.endDictionaryItem();
|
||||
objectBuilder.beginDictionaryItem("T");
|
||||
objectBuilder << title;
|
||||
objectBuilder.endDictionaryItem();
|
||||
objectBuilder.beginDictionaryItem("Contents");
|
||||
objectBuilder << contents;
|
||||
objectBuilder.endDictionaryItem();
|
||||
objectBuilder.beginDictionaryItem("Subj");
|
||||
objectBuilder << subject;
|
||||
objectBuilder.endDictionaryItem();
|
||||
objectBuilder.beginDictionaryItem("Q");
|
||||
objectBuilder << WrapFreeTextAlignment(textAlignment);
|
||||
objectBuilder.endDictionaryItem();
|
||||
objectBuilder.beginDictionaryItem("DA");
|
||||
objectBuilder << WrapString("/Arial 10 Tf");
|
||||
objectBuilder.endDictionaryItem();
|
||||
objectBuilder.beginDictionaryItem("RD");
|
||||
objectBuilder << getAnnotationReductionRectangle(boundingRectangle, textRectangle);
|
||||
objectBuilder.endDictionaryItem();
|
||||
objectBuilder.beginDictionaryItem("CL");
|
||||
objectBuilder.beginArray();
|
||||
objectBuilder << startPoint;
|
||||
objectBuilder << endPoint;
|
||||
objectBuilder.endArray();
|
||||
objectBuilder.endDictionaryItem();
|
||||
objectBuilder.beginDictionaryItem("LE");
|
||||
objectBuilder.beginArray();
|
||||
objectBuilder << startLineType;
|
||||
objectBuilder << endLineType;
|
||||
objectBuilder.endArray();
|
||||
objectBuilder.endDictionaryItem();
|
||||
objectBuilder.endDictionary();
|
||||
PDFObjectReference annotationObject = addObject(objectBuilder.takeObject());
|
||||
objectBuilder.beginDictionary();
|
||||
objectBuilder.beginDictionaryItem("Annots");
|
||||
objectBuilder.beginArray();
|
||||
objectBuilder << annotationObject;
|
||||
objectBuilder.endArray();
|
||||
objectBuilder.endDictionaryItem();
|
||||
objectBuilder.endDictionary();
|
||||
PDFObject pageAnnots = objectBuilder.takeObject();
|
||||
appendTo(page, pageAnnots);
|
||||
updateAnnotationAppearanceStreams(annotationObject);
|
||||
return annotationObject;
|
||||
}
|
||||
|
||||
|
||||
PDFObjectReference PDFDocumentBuilder::createAnnotationHighlight(PDFObjectReference page,
|
||||
QRectF rectangle,
|
||||
QColor color,
|
||||
@@ -3632,6 +3869,47 @@ PDFObjectReference PDFDocumentBuilder::createAnnotationSquiggly(PDFObjectReferen
|
||||
}
|
||||
|
||||
|
||||
PDFObjectReference PDFDocumentBuilder::createAnnotationSquiggly(PDFObjectReference page,
|
||||
QPolygonF quadrilaterals,
|
||||
QColor color)
|
||||
{
|
||||
PDFObjectFactory objectBuilder;
|
||||
|
||||
objectBuilder.beginDictionary();
|
||||
objectBuilder.beginDictionaryItem("Type");
|
||||
objectBuilder << WrapName("Annot");
|
||||
objectBuilder.endDictionaryItem();
|
||||
objectBuilder.beginDictionaryItem("Subtype");
|
||||
objectBuilder << WrapName("Squiggly");
|
||||
objectBuilder.endDictionaryItem();
|
||||
objectBuilder.beginDictionaryItem("P");
|
||||
objectBuilder << page;
|
||||
objectBuilder.endDictionaryItem();
|
||||
objectBuilder.beginDictionaryItem("CreationDate");
|
||||
objectBuilder << WrapCurrentDateTime();
|
||||
objectBuilder.endDictionaryItem();
|
||||
objectBuilder.beginDictionaryItem("C");
|
||||
objectBuilder << color;
|
||||
objectBuilder.endDictionaryItem();
|
||||
objectBuilder.beginDictionaryItem("QuadPoints");
|
||||
objectBuilder << quadrilaterals;
|
||||
objectBuilder.endDictionaryItem();
|
||||
objectBuilder.endDictionary();
|
||||
PDFObjectReference annotationObject = addObject(objectBuilder.takeObject());
|
||||
objectBuilder.beginDictionary();
|
||||
objectBuilder.beginDictionaryItem("Annots");
|
||||
objectBuilder.beginArray();
|
||||
objectBuilder << annotationObject;
|
||||
objectBuilder.endArray();
|
||||
objectBuilder.endDictionaryItem();
|
||||
objectBuilder.endDictionary();
|
||||
PDFObject pageAnnots = objectBuilder.takeObject();
|
||||
appendTo(page, pageAnnots);
|
||||
updateAnnotationAppearanceStreams(annotationObject);
|
||||
return annotationObject;
|
||||
}
|
||||
|
||||
|
||||
PDFObjectReference PDFDocumentBuilder::createAnnotationSquiggly(PDFObjectReference page,
|
||||
QRectF rectangle,
|
||||
QColor color,
|
||||
@@ -3696,47 +3974,6 @@ PDFObjectReference PDFDocumentBuilder::createAnnotationSquiggly(PDFObjectReferen
|
||||
}
|
||||
|
||||
|
||||
PDFObjectReference PDFDocumentBuilder::createAnnotationSquiggly(PDFObjectReference page,
|
||||
QPolygonF quadrilaterals,
|
||||
QColor color)
|
||||
{
|
||||
PDFObjectFactory objectBuilder;
|
||||
|
||||
objectBuilder.beginDictionary();
|
||||
objectBuilder.beginDictionaryItem("Type");
|
||||
objectBuilder << WrapName("Annot");
|
||||
objectBuilder.endDictionaryItem();
|
||||
objectBuilder.beginDictionaryItem("Subtype");
|
||||
objectBuilder << WrapName("Squiggly");
|
||||
objectBuilder.endDictionaryItem();
|
||||
objectBuilder.beginDictionaryItem("P");
|
||||
objectBuilder << page;
|
||||
objectBuilder.endDictionaryItem();
|
||||
objectBuilder.beginDictionaryItem("CreationDate");
|
||||
objectBuilder << WrapCurrentDateTime();
|
||||
objectBuilder.endDictionaryItem();
|
||||
objectBuilder.beginDictionaryItem("C");
|
||||
objectBuilder << color;
|
||||
objectBuilder.endDictionaryItem();
|
||||
objectBuilder.beginDictionaryItem("QuadPoints");
|
||||
objectBuilder << quadrilaterals;
|
||||
objectBuilder.endDictionaryItem();
|
||||
objectBuilder.endDictionary();
|
||||
PDFObjectReference annotationObject = addObject(objectBuilder.takeObject());
|
||||
objectBuilder.beginDictionary();
|
||||
objectBuilder.beginDictionaryItem("Annots");
|
||||
objectBuilder.beginArray();
|
||||
objectBuilder << annotationObject;
|
||||
objectBuilder.endArray();
|
||||
objectBuilder.endDictionaryItem();
|
||||
objectBuilder.endDictionary();
|
||||
PDFObject pageAnnots = objectBuilder.takeObject();
|
||||
appendTo(page, pageAnnots);
|
||||
updateAnnotationAppearanceStreams(annotationObject);
|
||||
return annotationObject;
|
||||
}
|
||||
|
||||
|
||||
PDFObjectReference PDFDocumentBuilder::createAnnotationStamp(PDFObjectReference page,
|
||||
QRectF rectangle,
|
||||
Stamp stampType,
|
||||
@@ -4020,70 +4257,6 @@ PDFObjectReference PDFDocumentBuilder::createAnnotationText(PDFObjectReference p
|
||||
}
|
||||
|
||||
|
||||
PDFObjectReference PDFDocumentBuilder::createAnnotationUnderline(PDFObjectReference page,
|
||||
QRectF rectangle,
|
||||
QColor color,
|
||||
QString title,
|
||||
QString subject,
|
||||
QString contents)
|
||||
{
|
||||
PDFObjectFactory objectBuilder;
|
||||
|
||||
objectBuilder.beginDictionary();
|
||||
objectBuilder.beginDictionaryItem("Type");
|
||||
objectBuilder << WrapName("Annot");
|
||||
objectBuilder.endDictionaryItem();
|
||||
objectBuilder.beginDictionaryItem("Subtype");
|
||||
objectBuilder << WrapName("Underline");
|
||||
objectBuilder.endDictionaryItem();
|
||||
objectBuilder.beginDictionaryItem("Rect");
|
||||
objectBuilder << rectangle;
|
||||
objectBuilder.endDictionaryItem();
|
||||
objectBuilder.beginDictionaryItem("P");
|
||||
objectBuilder << page;
|
||||
objectBuilder.endDictionaryItem();
|
||||
objectBuilder.beginDictionaryItem("M");
|
||||
objectBuilder << WrapCurrentDateTime();
|
||||
objectBuilder.endDictionaryItem();
|
||||
objectBuilder.beginDictionaryItem("CreationDate");
|
||||
objectBuilder << WrapCurrentDateTime();
|
||||
objectBuilder.endDictionaryItem();
|
||||
objectBuilder.beginDictionaryItem("C");
|
||||
objectBuilder << color;
|
||||
objectBuilder.endDictionaryItem();
|
||||
objectBuilder.beginDictionaryItem("T");
|
||||
objectBuilder << title;
|
||||
objectBuilder.endDictionaryItem();
|
||||
objectBuilder.beginDictionaryItem("Contents");
|
||||
objectBuilder << contents;
|
||||
objectBuilder.endDictionaryItem();
|
||||
objectBuilder.beginDictionaryItem("Subj");
|
||||
objectBuilder << subject;
|
||||
objectBuilder.endDictionaryItem();
|
||||
objectBuilder.beginDictionaryItem("QuadPoints");
|
||||
objectBuilder.beginArray();
|
||||
objectBuilder << rectangle.bottomLeft();
|
||||
objectBuilder << rectangle.bottomRight();
|
||||
objectBuilder << rectangle.topLeft();
|
||||
objectBuilder << rectangle.topRight();
|
||||
objectBuilder.endArray();
|
||||
objectBuilder.endDictionaryItem();
|
||||
objectBuilder.endDictionary();
|
||||
PDFObjectReference annotationObject = addObject(objectBuilder.takeObject());
|
||||
objectBuilder.beginDictionary();
|
||||
objectBuilder.beginDictionaryItem("Annots");
|
||||
objectBuilder.beginArray();
|
||||
objectBuilder << annotationObject;
|
||||
objectBuilder.endArray();
|
||||
objectBuilder.endDictionaryItem();
|
||||
objectBuilder.endDictionary();
|
||||
PDFObject pageAnnots = objectBuilder.takeObject();
|
||||
appendTo(page, pageAnnots);
|
||||
updateAnnotationAppearanceStreams(annotationObject);
|
||||
return annotationObject;
|
||||
}
|
||||
|
||||
|
||||
PDFObjectReference PDFDocumentBuilder::createAnnotationUnderline(PDFObjectReference page,
|
||||
QRectF rectangle,
|
||||
QColor color)
|
||||
@@ -4174,6 +4347,70 @@ PDFObjectReference PDFDocumentBuilder::createAnnotationUnderline(PDFObjectRefere
|
||||
}
|
||||
|
||||
|
||||
PDFObjectReference PDFDocumentBuilder::createAnnotationUnderline(PDFObjectReference page,
|
||||
QRectF rectangle,
|
||||
QColor color,
|
||||
QString title,
|
||||
QString subject,
|
||||
QString contents)
|
||||
{
|
||||
PDFObjectFactory objectBuilder;
|
||||
|
||||
objectBuilder.beginDictionary();
|
||||
objectBuilder.beginDictionaryItem("Type");
|
||||
objectBuilder << WrapName("Annot");
|
||||
objectBuilder.endDictionaryItem();
|
||||
objectBuilder.beginDictionaryItem("Subtype");
|
||||
objectBuilder << WrapName("Underline");
|
||||
objectBuilder.endDictionaryItem();
|
||||
objectBuilder.beginDictionaryItem("Rect");
|
||||
objectBuilder << rectangle;
|
||||
objectBuilder.endDictionaryItem();
|
||||
objectBuilder.beginDictionaryItem("P");
|
||||
objectBuilder << page;
|
||||
objectBuilder.endDictionaryItem();
|
||||
objectBuilder.beginDictionaryItem("M");
|
||||
objectBuilder << WrapCurrentDateTime();
|
||||
objectBuilder.endDictionaryItem();
|
||||
objectBuilder.beginDictionaryItem("CreationDate");
|
||||
objectBuilder << WrapCurrentDateTime();
|
||||
objectBuilder.endDictionaryItem();
|
||||
objectBuilder.beginDictionaryItem("C");
|
||||
objectBuilder << color;
|
||||
objectBuilder.endDictionaryItem();
|
||||
objectBuilder.beginDictionaryItem("T");
|
||||
objectBuilder << title;
|
||||
objectBuilder.endDictionaryItem();
|
||||
objectBuilder.beginDictionaryItem("Contents");
|
||||
objectBuilder << contents;
|
||||
objectBuilder.endDictionaryItem();
|
||||
objectBuilder.beginDictionaryItem("Subj");
|
||||
objectBuilder << subject;
|
||||
objectBuilder.endDictionaryItem();
|
||||
objectBuilder.beginDictionaryItem("QuadPoints");
|
||||
objectBuilder.beginArray();
|
||||
objectBuilder << rectangle.bottomLeft();
|
||||
objectBuilder << rectangle.bottomRight();
|
||||
objectBuilder << rectangle.topLeft();
|
||||
objectBuilder << rectangle.topRight();
|
||||
objectBuilder.endArray();
|
||||
objectBuilder.endDictionaryItem();
|
||||
objectBuilder.endDictionary();
|
||||
PDFObjectReference annotationObject = addObject(objectBuilder.takeObject());
|
||||
objectBuilder.beginDictionary();
|
||||
objectBuilder.beginDictionaryItem("Annots");
|
||||
objectBuilder.beginArray();
|
||||
objectBuilder << annotationObject;
|
||||
objectBuilder.endArray();
|
||||
objectBuilder.endDictionaryItem();
|
||||
objectBuilder.endDictionary();
|
||||
PDFObject pageAnnots = objectBuilder.takeObject();
|
||||
appendTo(page, pageAnnots);
|
||||
updateAnnotationAppearanceStreams(annotationObject);
|
||||
return annotationObject;
|
||||
}
|
||||
|
||||
|
||||
PDFObjectReference PDFDocumentBuilder::createCatalog()
|
||||
{
|
||||
PDFObjectFactory objectBuilder;
|
||||
@@ -4305,6 +4542,135 @@ PDFObjectReference PDFDocumentBuilder::createFileSpecification(QString fileName)
|
||||
}
|
||||
|
||||
|
||||
PDFObjectReference PDFDocumentBuilder::createFormFieldSignature(QString fieldName,
|
||||
PDFObjectReferenceVector kids,
|
||||
PDFObjectReference signatureValue)
|
||||
{
|
||||
PDFObjectFactory objectBuilder;
|
||||
|
||||
objectBuilder.beginDictionary();
|
||||
objectBuilder.beginDictionaryItem("FT");
|
||||
objectBuilder << WrapName("Sig");
|
||||
objectBuilder.endDictionaryItem();
|
||||
objectBuilder.beginDictionaryItem("Kids");
|
||||
objectBuilder << kids;
|
||||
objectBuilder.endDictionaryItem();
|
||||
objectBuilder.beginDictionaryItem("T");
|
||||
objectBuilder << fieldName;
|
||||
objectBuilder.endDictionaryItem();
|
||||
objectBuilder.beginDictionaryItem("V");
|
||||
objectBuilder << signatureValue;
|
||||
objectBuilder.endDictionaryItem();
|
||||
objectBuilder.endDictionary();
|
||||
PDFObjectReference formFieldSignature = addObject(objectBuilder.takeObject());
|
||||
return formFieldSignature;
|
||||
}
|
||||
|
||||
|
||||
void PDFDocumentBuilder::createFormFieldWidget(PDFObjectReference formField,
|
||||
PDFObjectReference page,
|
||||
PDFObjectReference appearanceStream,
|
||||
QRectF rect)
|
||||
{
|
||||
PDFObjectFactory objectBuilder;
|
||||
|
||||
objectBuilder.beginDictionary();
|
||||
objectBuilder.beginDictionaryItem("Type");
|
||||
objectBuilder << WrapName("Annot");
|
||||
objectBuilder.endDictionaryItem();
|
||||
objectBuilder.beginDictionaryItem("Subtype");
|
||||
objectBuilder << WrapName("Widget");
|
||||
objectBuilder.endDictionaryItem();
|
||||
objectBuilder.beginDictionaryItem("P");
|
||||
objectBuilder << page;
|
||||
objectBuilder.endDictionaryItem();
|
||||
objectBuilder.beginDictionaryItem("Rect");
|
||||
objectBuilder << rect;
|
||||
objectBuilder.endDictionaryItem();
|
||||
objectBuilder.beginDictionaryItem("AP");
|
||||
objectBuilder.beginDictionary();
|
||||
objectBuilder.beginDictionaryItem("N");
|
||||
objectBuilder << appearanceStream;
|
||||
objectBuilder.endDictionaryItem();
|
||||
objectBuilder.endDictionary();
|
||||
objectBuilder.endDictionaryItem();
|
||||
objectBuilder.endDictionary();
|
||||
PDFObject widgetObject = objectBuilder.takeObject();
|
||||
objectBuilder.beginDictionary();
|
||||
objectBuilder.beginDictionaryItem("Annots");
|
||||
objectBuilder << std::array{ formField };
|
||||
objectBuilder.endDictionaryItem();
|
||||
objectBuilder.endDictionary();
|
||||
PDFObject pageObject = objectBuilder.takeObject();
|
||||
mergeTo(formField, widgetObject);
|
||||
appendTo(page, pageObject);
|
||||
}
|
||||
|
||||
|
||||
void PDFDocumentBuilder::createInvisibleFormFieldWidget(PDFObjectReference formField,
|
||||
PDFObjectReference page)
|
||||
{
|
||||
PDFObjectFactory objectBuilder;
|
||||
|
||||
objectBuilder.beginDictionary();
|
||||
objectBuilder.beginDictionaryItem("P");
|
||||
objectBuilder << page;
|
||||
objectBuilder.endDictionaryItem();
|
||||
objectBuilder.beginDictionaryItem("Rect");
|
||||
objectBuilder << std::array{ 0.0, 0.0, 0.0, 0.0 };
|
||||
objectBuilder.endDictionaryItem();
|
||||
objectBuilder.beginDictionaryItem("Subtype");
|
||||
objectBuilder << WrapName("Widget");
|
||||
objectBuilder.endDictionaryItem();
|
||||
objectBuilder.beginDictionaryItem("Type");
|
||||
objectBuilder << WrapName("Annot");
|
||||
objectBuilder.endDictionaryItem();
|
||||
objectBuilder.endDictionary();
|
||||
PDFObject widgetObject = objectBuilder.takeObject();
|
||||
objectBuilder.beginDictionary();
|
||||
objectBuilder.beginDictionaryItem("Annots");
|
||||
objectBuilder << std::array{ formField };
|
||||
objectBuilder.endDictionaryItem();
|
||||
objectBuilder.endDictionary();
|
||||
PDFObject pageObject = objectBuilder.takeObject();
|
||||
mergeTo(formField, widgetObject);
|
||||
appendTo(page, pageObject);
|
||||
}
|
||||
|
||||
|
||||
PDFObjectReference PDFDocumentBuilder::createSignatureDictionary(QByteArray filter,
|
||||
QByteArray subfilter,
|
||||
QByteArray contents,
|
||||
QDateTime signingTime,
|
||||
PDFInteger byteRangeItem)
|
||||
{
|
||||
PDFObjectFactory objectBuilder;
|
||||
|
||||
objectBuilder.beginDictionary();
|
||||
objectBuilder.beginDictionaryItem("Type");
|
||||
objectBuilder << WrapName("Sig");
|
||||
objectBuilder.endDictionaryItem();
|
||||
objectBuilder.beginDictionaryItem("Filter");
|
||||
objectBuilder << WrapName(filter);
|
||||
objectBuilder.endDictionaryItem();
|
||||
objectBuilder.beginDictionaryItem("SubFilter");
|
||||
objectBuilder << WrapName(subfilter);
|
||||
objectBuilder.endDictionaryItem();
|
||||
objectBuilder.beginDictionaryItem("ByteRange");
|
||||
objectBuilder << std::array{ byteRangeItem, byteRangeItem, byteRangeItem, byteRangeItem };
|
||||
objectBuilder.endDictionaryItem();
|
||||
objectBuilder.beginDictionaryItem("Contents");
|
||||
objectBuilder << WrapString(contents);
|
||||
objectBuilder.endDictionaryItem();
|
||||
objectBuilder.beginDictionaryItem("M");
|
||||
objectBuilder << signingTime;
|
||||
objectBuilder.endDictionaryItem();
|
||||
objectBuilder.endDictionary();
|
||||
PDFObjectReference signatureDictionary = addObject(objectBuilder.takeObject());
|
||||
return signatureDictionary;
|
||||
}
|
||||
|
||||
|
||||
PDFObject PDFDocumentBuilder::createTrailerDictionary(PDFObjectReference catalog)
|
||||
{
|
||||
PDFObjectFactory objectBuilder;
|
||||
@@ -4805,6 +5171,14 @@ void PDFDocumentBuilder::setFormFieldValue(PDFObjectReference formField,
|
||||
}
|
||||
|
||||
|
||||
void PDFDocumentBuilder::setLanguage(QLocale locale)
|
||||
{
|
||||
PDFObjectFactory objectBuilder;
|
||||
|
||||
setLanguage(locale.name());
|
||||
}
|
||||
|
||||
|
||||
void PDFDocumentBuilder::setLanguage(QString language)
|
||||
{
|
||||
PDFObjectFactory objectBuilder;
|
||||
@@ -4819,14 +5193,6 @@ void PDFDocumentBuilder::setLanguage(QString language)
|
||||
}
|
||||
|
||||
|
||||
void PDFDocumentBuilder::setLanguage(QLocale locale)
|
||||
{
|
||||
PDFObjectFactory objectBuilder;
|
||||
|
||||
setLanguage(locale.name());
|
||||
}
|
||||
|
||||
|
||||
void PDFDocumentBuilder::setOutline(PDFObjectReference outline)
|
||||
{
|
||||
PDFObjectFactory objectBuilder;
|
||||
@@ -4961,6 +5327,36 @@ void PDFDocumentBuilder::setPageUserUnit(PDFObjectReference page,
|
||||
}
|
||||
|
||||
|
||||
void PDFDocumentBuilder::setSignatureContactInfo(PDFObjectReference signatureDictionary,
|
||||
QString contactInfoText)
|
||||
{
|
||||
PDFObjectFactory objectBuilder;
|
||||
|
||||
objectBuilder.beginDictionary();
|
||||
objectBuilder.beginDictionaryItem("ContactInfo");
|
||||
objectBuilder << contactInfoText;
|
||||
objectBuilder.endDictionaryItem();
|
||||
objectBuilder.endDictionary();
|
||||
PDFObject updatedSignatureDictionary = objectBuilder.takeObject();
|
||||
mergeTo(signatureDictionary, updatedSignatureDictionary);
|
||||
}
|
||||
|
||||
|
||||
void PDFDocumentBuilder::setSignatureReason(PDFObjectReference signatureDictionary,
|
||||
QString reasonText)
|
||||
{
|
||||
PDFObjectFactory objectBuilder;
|
||||
|
||||
objectBuilder.beginDictionary();
|
||||
objectBuilder.beginDictionaryItem("Reason");
|
||||
objectBuilder << reasonText;
|
||||
objectBuilder.endDictionaryItem();
|
||||
objectBuilder.endDictionary();
|
||||
PDFObject updatedSignatureDictionary = objectBuilder.takeObject();
|
||||
mergeTo(signatureDictionary, updatedSignatureDictionary);
|
||||
}
|
||||
|
||||
|
||||
void PDFDocumentBuilder::updateTrailerDictionary(PDFInteger objectCount)
|
||||
{
|
||||
PDFObjectFactory objectBuilder;
|
||||
|
@@ -39,6 +39,12 @@ struct WrapName
|
||||
|
||||
}
|
||||
|
||||
WrapName(QByteArray name) :
|
||||
name(std::move(name))
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
QByteArray name;
|
||||
};
|
||||
|
||||
@@ -86,7 +92,7 @@ struct WrapEmptyArray { };
|
||||
|
||||
/// Factory for creating various PDF objects, such as simple objects,
|
||||
/// dictionaries, arrays etc.
|
||||
class PDFObjectFactory
|
||||
class PDF4QTLIBSHARED_EXPORT PDFObjectFactory
|
||||
{
|
||||
public:
|
||||
inline explicit PDFObjectFactory() = default;
|
||||
@@ -261,9 +267,20 @@ private:
|
||||
class PDF4QTLIBSHARED_EXPORT PDFPageContentStreamBuilder
|
||||
{
|
||||
public:
|
||||
PDFPageContentStreamBuilder(PDFDocumentBuilder* builder);
|
||||
|
||||
/// Starts painting onto the page. Old page content is erased. This
|
||||
enum class Mode
|
||||
{
|
||||
Replace,
|
||||
PlaceBefore,
|
||||
PlaceAfter
|
||||
};
|
||||
|
||||
/// Vytvoří nový builder, který vytváří obsah stránek.
|
||||
PDFPageContentStreamBuilder(PDFDocumentBuilder* builder,
|
||||
PDFContentStreamBuilder::CoordinateSystem coordinateSystem = PDFContentStreamBuilder::CoordinateSystem::Qt,
|
||||
Mode mode = Mode::Replace);
|
||||
|
||||
/// Starts painting onto the page. Old page content is erased (in Replace mode). This
|
||||
/// function returns painter, onto which can be graphics drawn. Painter
|
||||
/// uses Qt's coordinate system. Calling begin multiple times, without
|
||||
/// subsequent calls to end function, is invalid and can result
|
||||
@@ -284,9 +301,15 @@ public:
|
||||
void end(QPainter* painter);
|
||||
|
||||
private:
|
||||
void replaceResources(PDFObjectReference contentStreamReference,
|
||||
PDFObjectReference resourcesReference,
|
||||
PDFObject oldResources);
|
||||
|
||||
PDFDocumentBuilder* m_documentBuilder;
|
||||
PDFContentStreamBuilder* m_contentStreamBuilder;
|
||||
PDFObjectReference m_pageReference;
|
||||
PDFContentStreamBuilder::CoordinateSystem m_coordinateSystem;
|
||||
Mode m_mode;
|
||||
};
|
||||
|
||||
class PDF4QTLIBSHARED_EXPORT PDFDocumentBuilder
|
||||
@@ -328,6 +351,11 @@ public:
|
||||
/// is returned (no exception is thrown).
|
||||
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
|
||||
std::array<PDFReal, 4> getAnnotationReductionRectangle(const QRectF& boundingRect, const QRectF& innerRect) const;
|
||||
|
||||
@@ -446,6 +474,11 @@ public:
|
||||
PDFObjectReference appendPage(QRectF mediaBox);
|
||||
|
||||
|
||||
/// Creates AcroForm dictionary. Erases XFA form if present.
|
||||
/// \param fields Fields
|
||||
PDFObjectReference createAcroForm(PDFObjectReferenceVector fields);
|
||||
|
||||
|
||||
/// Creates GoTo action. This action changes view to a specific destination in the same document.
|
||||
/// \param destination Destination
|
||||
PDFObjectReference createActionGoTo(PDFDestination destination);
|
||||
@@ -662,36 +695,6 @@ public:
|
||||
QString description);
|
||||
|
||||
|
||||
/// Free text annotation displays text directly on a page. Text appears directly on the page, in the
|
||||
/// same way, as standard text in PDF document. Free text annotations are usually used to comment
|
||||
/// the document. Free text annotation can also have callout line, with, or without a knee. Specify
|
||||
/// start/end point parameters of this function to get callout line.
|
||||
/// \param page Page to which is annotation added
|
||||
/// \param boundingRectangle Bounding rectangle of free text annotation. It must contain both
|
||||
/// callout line and text rectangle.
|
||||
/// \param textRectangle Rectangle with text, in absolute coordinates. They are then recomputed to
|
||||
/// match bounding rectangle.
|
||||
/// \param title Title
|
||||
/// \param subject Subject
|
||||
/// \param contents Contents (text displayed)
|
||||
/// \param textAlignment Text alignment. Only horizontal alignment flags are valid.
|
||||
/// \param startPoint Start point of callout line
|
||||
/// \param endPoint End point of callout line
|
||||
/// \param startLineType Line ending at the start point
|
||||
/// \param endLineType Line ending at the end point
|
||||
PDFObjectReference createAnnotationFreeText(PDFObjectReference page,
|
||||
QRectF boundingRectangle,
|
||||
QRectF textRectangle,
|
||||
QString title,
|
||||
QString subject,
|
||||
QString contents,
|
||||
TextAlignment textAlignment,
|
||||
QPointF startPoint,
|
||||
QPointF endPoint,
|
||||
AnnotationLineEnding startLineType,
|
||||
AnnotationLineEnding endLineType);
|
||||
|
||||
|
||||
/// Free text annotation displays text directly on a page. Text appears directly on the page, in the
|
||||
/// same way, as standard text in PDF document. Free text annotations are usually used to comment
|
||||
/// the document. Free text annotation can also have callout line, with, or without a knee. Specify
|
||||
@@ -741,6 +744,36 @@ public:
|
||||
TextAlignment textAlignment);
|
||||
|
||||
|
||||
/// Free text annotation displays text directly on a page. Text appears directly on the page, in the
|
||||
/// same way, as standard text in PDF document. Free text annotations are usually used to comment
|
||||
/// the document. Free text annotation can also have callout line, with, or without a knee. Specify
|
||||
/// start/end point parameters of this function to get callout line.
|
||||
/// \param page Page to which is annotation added
|
||||
/// \param boundingRectangle Bounding rectangle of free text annotation. It must contain both
|
||||
/// callout line and text rectangle.
|
||||
/// \param textRectangle Rectangle with text, in absolute coordinates. They are then recomputed to
|
||||
/// match bounding rectangle.
|
||||
/// \param title Title
|
||||
/// \param subject Subject
|
||||
/// \param contents Contents (text displayed)
|
||||
/// \param textAlignment Text alignment. Only horizontal alignment flags are valid.
|
||||
/// \param startPoint Start point of callout line
|
||||
/// \param endPoint End point of callout line
|
||||
/// \param startLineType Line ending at the start point
|
||||
/// \param endLineType Line ending at the end point
|
||||
PDFObjectReference createAnnotationFreeText(PDFObjectReference page,
|
||||
QRectF boundingRectangle,
|
||||
QRectF textRectangle,
|
||||
QString title,
|
||||
QString subject,
|
||||
QString contents,
|
||||
TextAlignment textAlignment,
|
||||
QPointF startPoint,
|
||||
QPointF endPoint,
|
||||
AnnotationLineEnding startLineType,
|
||||
AnnotationLineEnding endLineType);
|
||||
|
||||
|
||||
/// Text markup annotation is used to highlight text. It is a markup annotation, so it can contain
|
||||
/// window to be opened (and commented). This annotation is usually used to highlight text, but can
|
||||
/// also highlight other things, such as images, or other graphics.
|
||||
@@ -1024,6 +1057,16 @@ public:
|
||||
QColor color);
|
||||
|
||||
|
||||
/// Text markup annotation is used to squiggly underline text. It is a markup annotation, so it can
|
||||
/// contain window to be opened (and commented).
|
||||
/// \param page Page to which is annotation added
|
||||
/// \param quadrilaterals Area in which is markup displayed
|
||||
/// \param color Color
|
||||
PDFObjectReference createAnnotationSquiggly(PDFObjectReference page,
|
||||
QPolygonF quadrilaterals,
|
||||
QColor color);
|
||||
|
||||
|
||||
/// Text markup annotation is used to squiggly underline text. It is a markup annotation, so it can
|
||||
/// contain window to be opened (and commented).
|
||||
/// \param page Page to which is annotation added
|
||||
@@ -1040,16 +1083,6 @@ public:
|
||||
QString contents);
|
||||
|
||||
|
||||
/// Text markup annotation is used to squiggly underline text. It is a markup annotation, so it can
|
||||
/// contain window to be opened (and commented).
|
||||
/// \param page Page to which is annotation added
|
||||
/// \param quadrilaterals Area in which is markup displayed
|
||||
/// \param color Color
|
||||
PDFObjectReference createAnnotationSquiggly(PDFObjectReference page,
|
||||
QPolygonF quadrilaterals,
|
||||
QColor color);
|
||||
|
||||
|
||||
/// Stamp annotation
|
||||
/// \param page Page to which is annotation added
|
||||
/// \param rectangle Stamp area
|
||||
@@ -1122,22 +1155,6 @@ public:
|
||||
bool open);
|
||||
|
||||
|
||||
/// Text markup annotation is used to underline text. It is a markup annotation, so it can contain
|
||||
/// window to be opened (and commented).
|
||||
/// \param page Page to which is annotation added
|
||||
/// \param rectangle Area in which is markup displayed
|
||||
/// \param color Color
|
||||
/// \param title Title
|
||||
/// \param subject Subject
|
||||
/// \param contents Contents
|
||||
PDFObjectReference createAnnotationUnderline(PDFObjectReference page,
|
||||
QRectF rectangle,
|
||||
QColor color,
|
||||
QString title,
|
||||
QString subject,
|
||||
QString contents);
|
||||
|
||||
|
||||
/// Text markup annotation is used to underline text. It is a markup annotation, so it can contain
|
||||
/// window to be opened (and commented).
|
||||
/// \param page Page to which is annotation added
|
||||
@@ -1158,6 +1175,22 @@ public:
|
||||
QColor color);
|
||||
|
||||
|
||||
/// Text markup annotation is used to underline text. It is a markup annotation, so it can contain
|
||||
/// window to be opened (and commented).
|
||||
/// \param page Page to which is annotation added
|
||||
/// \param rectangle Area in which is markup displayed
|
||||
/// \param color Color
|
||||
/// \param title Title
|
||||
/// \param subject Subject
|
||||
/// \param contents Contents
|
||||
PDFObjectReference createAnnotationUnderline(PDFObjectReference page,
|
||||
QRectF rectangle,
|
||||
QColor color,
|
||||
QString title,
|
||||
QString subject,
|
||||
QString contents);
|
||||
|
||||
|
||||
/// Creates empty catalog. This function is used, when a new document is being created. Do not call
|
||||
/// this function manually.
|
||||
PDFObjectReference createCatalog();
|
||||
@@ -1193,6 +1226,48 @@ public:
|
||||
PDFObjectReference createFileSpecification(QString fileName);
|
||||
|
||||
|
||||
/// Creates form field of type signature.
|
||||
/// \param fieldName Field name
|
||||
/// \param kids Kids of the signature field.
|
||||
/// \param signatureValue Signature value
|
||||
PDFObjectReference createFormFieldSignature(QString fieldName,
|
||||
PDFObjectReferenceVector kids,
|
||||
PDFObjectReference signatureValue);
|
||||
|
||||
|
||||
/// Creates visible form field widget without contents.
|
||||
/// \param formField Form field reference
|
||||
/// \param page Page reference
|
||||
/// \param appearanceStream Appearance stream
|
||||
/// \param rect Widget rectangle
|
||||
void createFormFieldWidget(PDFObjectReference formField,
|
||||
PDFObjectReference page,
|
||||
PDFObjectReference appearanceStream,
|
||||
QRectF rect);
|
||||
|
||||
|
||||
///
|
||||
/// \param formField Form field reference
|
||||
/// \param page Page reference
|
||||
void createInvisibleFormFieldWidget(PDFObjectReference formField,
|
||||
PDFObjectReference page);
|
||||
|
||||
|
||||
/// Creates signature dictionary used for preparation in signing process. Can define parameters of the
|
||||
/// signature.
|
||||
/// \param filter Filter (for example, Adobe.PPKLite, Entrust.PPKEF, CiCi.SignIt, ...)
|
||||
/// \param subfilter Subfilter (for example, adbe.pkcs7.detached, adbe.pkcs7.sha1,
|
||||
/// ETSI.CAdES.detached, ...)
|
||||
/// \param contents Contents (reserved data for signature).
|
||||
/// \param signingTime Signing date/time
|
||||
/// \param byteRangeItem Item which will fill byte range array.
|
||||
PDFObjectReference createSignatureDictionary(QByteArray filter,
|
||||
QByteArray subfilter,
|
||||
QByteArray contents,
|
||||
QDateTime signingTime,
|
||||
PDFInteger byteRangeItem);
|
||||
|
||||
|
||||
/// This function is used to create a new trailer dictionary, when blank document is created. Do not
|
||||
/// call this function manually.
|
||||
/// \param catalog Reference to document catalog
|
||||
@@ -1393,6 +1468,11 @@ public:
|
||||
PDFObject value);
|
||||
|
||||
|
||||
/// Set document language.
|
||||
/// \param locale Locale, from which is language determined
|
||||
void setLanguage(QLocale locale);
|
||||
|
||||
|
||||
/// Set document language.
|
||||
/// \param language Document language. It should be a language identifier, as defined in ISO 639
|
||||
/// and ISO 3166. For example, "en-US", where first two letter means language code (en =
|
||||
@@ -1400,11 +1480,6 @@ public:
|
||||
void setLanguage(QString language);
|
||||
|
||||
|
||||
/// Set document language.
|
||||
/// \param locale Locale, from which is language determined
|
||||
void setLanguage(QLocale locale);
|
||||
|
||||
|
||||
/// Set document outline.
|
||||
/// \param outline Document outline root
|
||||
void setOutline(PDFObjectReference outline);
|
||||
@@ -1470,6 +1545,20 @@ public:
|
||||
PDFReal unit);
|
||||
|
||||
|
||||
/// Sets signature contact info field.
|
||||
/// \param signatureDictionary Signature dictionary reference
|
||||
/// \param contactInfoText Contact info text
|
||||
void setSignatureContactInfo(PDFObjectReference signatureDictionary,
|
||||
QString contactInfoText);
|
||||
|
||||
|
||||
/// Sets signature reason field.
|
||||
/// \param signatureDictionary Signature dictionary reference
|
||||
/// \param reasonText Reason text
|
||||
void setSignatureReason(PDFObjectReference signatureDictionary,
|
||||
QString reasonText);
|
||||
|
||||
|
||||
/// This function is used to update trailer dictionary. Must be called each time the final document is
|
||||
/// being built.
|
||||
/// \param objectCount Number of objects (including empty ones)
|
||||
@@ -1513,6 +1602,7 @@ public:
|
||||
PDFModifiedDocument::ModificationFlags getFlags() const { return m_modificationFlags; }
|
||||
|
||||
void markReset() { m_modificationFlags.setFlag(PDFModifiedDocument::Reset); }
|
||||
void markPageContentsChanged() { m_modificationFlags.setFlag(PDFModifiedDocument::PageContents); }
|
||||
void markAnnotationsChanged() { m_modificationFlags.setFlag(PDFModifiedDocument::Annotation); }
|
||||
void markFormFieldChanged() { m_modificationFlags.setFlag(PDFModifiedDocument::FormField); }
|
||||
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);
|
||||
if (result)
|
||||
{
|
||||
file.commit();
|
||||
if (!file.commit())
|
||||
{
|
||||
return tr("File '%1' can't be opened for writing. %2").arg(fileName, file.errorString());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@@ -488,8 +488,8 @@ void PDFDrawWidgetProxy::setDocument(const PDFModifiedDocument& document)
|
||||
if (getDocument() != document)
|
||||
{
|
||||
m_cacheClearTimer->stop();
|
||||
m_compiler->stop(document.hasReset());
|
||||
m_textLayoutCompiler->stop(document.hasReset());
|
||||
m_compiler->stop(document.hasReset() || document.hasPageContentsChanged());
|
||||
m_textLayoutCompiler->stop(document.hasReset() || document.hasPageContentsChanged());
|
||||
m_controller->setDocument(document);
|
||||
|
||||
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
|
||||
// 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_realizedFontCache.clear();
|
||||
|
File diff suppressed because it is too large
Load Diff
@@ -1,4 +1,4 @@
|
||||
// Copyright (C) 2020-2021 Jakub Melka
|
||||
// Copyright (C) 2020-2022 Jakub Melka
|
||||
//
|
||||
// This file is part of PDF4QT.
|
||||
//
|
||||
|
@@ -738,7 +738,7 @@ void PDFThumbnailsItemModel::setDocument(const PDFModifiedDocument& document)
|
||||
{
|
||||
if (m_document != document)
|
||||
{
|
||||
if (document.hasReset() || document.hasFlag(PDFModifiedDocument::Annotation))
|
||||
if (document.hasReset() || document.hasPageContentsChanged() || document.hasFlag(PDFModifiedDocument::Annotation))
|
||||
{
|
||||
beginResetModel();
|
||||
m_thumbnailCache.clear();
|
||||
|
548
Pdf4QtLib/sources/pdfpagecontenteditorstylesettings.cpp
Normal file
548
Pdf4QtLib/sources/pdfpagecontenteditorstylesettings.cpp
Normal file
@@ -0,0 +1,548 @@
|
||||
// Copyright (C) 2022 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 <https://www.gnu.org/licenses/>.
|
||||
|
||||
#include "pdfpagecontenteditorstylesettings.h"
|
||||
#include "ui_pdfpagecontenteditorstylesettings.h"
|
||||
|
||||
#include "pdfwidgetutils.h"
|
||||
#include "pdfpagecontentelements.h"
|
||||
|
||||
#include <QFontDialog>
|
||||
#include <QColorDialog>
|
||||
#include <QLineEdit>
|
||||
#include <QVBoxLayout>
|
||||
#include <QGroupBox>
|
||||
#include <QDialogButtonBox>
|
||||
#include <QTextEdit>
|
||||
|
||||
namespace pdf
|
||||
{
|
||||
|
||||
PDFPageContentEditorStyleSettings::PDFPageContentEditorStyleSettings(QWidget* parent) :
|
||||
QWidget(parent),
|
||||
ui(new Ui::PDFPageContentEditorStyleSettings)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
|
||||
for (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("None"), int(Qt::NoPen));
|
||||
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->brushStyleCombo->addItem(tr("None"), int(Qt::NoBrush));
|
||||
ui->brushStyleCombo->addItem(tr("Solid"), int(Qt::SolidPattern));
|
||||
ui->brushStyleCombo->addItem(tr("Horizontal"), int(Qt::HorPattern));
|
||||
ui->brushStyleCombo->addItem(tr("Vertical"), int(Qt::VerPattern));
|
||||
ui->brushStyleCombo->addItem(tr("B-Diagonal"), int(Qt::BDiagPattern));
|
||||
ui->brushStyleCombo->addItem(tr("F-Diagonal"), int(Qt::FDiagPattern));
|
||||
ui->brushStyleCombo->addItem(tr("Cross"), int(Qt::CrossPattern));
|
||||
|
||||
connect(ui->fontComboBox, &QFontComboBox::currentFontChanged, this, &PDFPageContentEditorStyleSettings::onFontChanged);
|
||||
connect(ui->selectPenColorButton, &QToolButton::clicked, this, &PDFPageContentEditorStyleSettings::onSelectPenColorButtonClicked);
|
||||
connect(ui->selectBrushColorButton, &QToolButton::clicked, this, &PDFPageContentEditorStyleSettings::onSelectBrushColorButtonClicked);
|
||||
connect(ui->selectFontButton, &QToolButton::clicked, this, &PDFPageContentEditorStyleSettings::onSelectFontButtonClicked);
|
||||
connect(ui->penWidthEdit, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this, &PDFPageContentEditorStyleSettings::onPenWidthChanged);
|
||||
connect(ui->penStyleCombo, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &PDFPageContentEditorStyleSettings::onPenStyleChanged);
|
||||
connect(ui->brushStyleCombo, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &PDFPageContentEditorStyleSettings::onBrushStyleChanged);
|
||||
connect(ui->textAngleEdit, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this, &PDFPageContentEditorStyleSettings::onTextAngleChanged);
|
||||
connect(ui->penColorCombo->lineEdit(), &QLineEdit::editingFinished, this, &PDFPageContentEditorStyleSettings::onPenColorComboTextChanged);
|
||||
connect(ui->penColorCombo, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &PDFPageContentEditorStyleSettings::onPenColorComboIndexChanged);
|
||||
connect(ui->brushColorCombo->lineEdit(), &QLineEdit::editingFinished, this, &PDFPageContentEditorStyleSettings::onBrushColorComboTextChanged);
|
||||
connect(ui->brushColorCombo, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &PDFPageContentEditorStyleSettings::onBrushColorComboIndexChanged);
|
||||
|
||||
m_alignmentMapper.setMapping(ui->al11Button, int(Qt::AlignLeft | Qt::AlignTop));
|
||||
m_alignmentMapper.setMapping(ui->al12Button, int(Qt::AlignHCenter | Qt::AlignTop));
|
||||
m_alignmentMapper.setMapping(ui->al13Button, int(Qt::AlignRight | Qt::AlignTop));
|
||||
m_alignmentMapper.setMapping(ui->al21Button, int(Qt::AlignLeft | Qt::AlignVCenter));
|
||||
m_alignmentMapper.setMapping(ui->al22Button, int(Qt::AlignCenter));
|
||||
m_alignmentMapper.setMapping(ui->al23Button, int(Qt::AlignRight | Qt::AlignVCenter));
|
||||
m_alignmentMapper.setMapping(ui->al31Button, int(Qt::AlignLeft | Qt::AlignBottom));
|
||||
m_alignmentMapper.setMapping(ui->al32Button, int(Qt::AlignHCenter | Qt::AlignBottom));
|
||||
m_alignmentMapper.setMapping(ui->al33Button, int(Qt::AlignRight | Qt::AlignBottom));
|
||||
|
||||
for (QRadioButton* radioButton : findChildren<QRadioButton*>())
|
||||
{
|
||||
connect(radioButton, &QRadioButton::clicked, &m_alignmentMapper, QOverload<>::of(&QSignalMapper::map));
|
||||
}
|
||||
connect(&m_alignmentMapper, &QSignalMapper::mappedInt, this, &PDFPageContentEditorStyleSettings::onAlignmentRadioButtonClicked);
|
||||
|
||||
loadFromElement(nullptr, true);
|
||||
}
|
||||
|
||||
PDFPageContentEditorStyleSettings::~PDFPageContentEditorStyleSettings()
|
||||
{
|
||||
delete ui;
|
||||
}
|
||||
|
||||
void PDFPageContentEditorStyleSettings::loadFromElement(const PDFPageContentElement* element, bool forceUpdate)
|
||||
{
|
||||
const PDFPageContentStyledElement* styledElement = dynamic_cast<const PDFPageContentStyledElement*>(element);
|
||||
const PDFPageContentElementTextBox* textElement = dynamic_cast<const PDFPageContentElementTextBox*>(element);
|
||||
|
||||
StyleFeatures features = None;
|
||||
|
||||
if (styledElement)
|
||||
{
|
||||
features.setFlag(Pen);
|
||||
features.setFlag(PenColor);
|
||||
features.setFlag(Brush);
|
||||
}
|
||||
|
||||
if (textElement)
|
||||
{
|
||||
features.setFlag(PenColor);
|
||||
features.setFlag(Text);
|
||||
}
|
||||
|
||||
const bool hasPen = features.testFlag(Pen);
|
||||
const bool hasPenColor = features.testFlag(PenColor);
|
||||
const bool hasBrush = features.testFlag(Brush);
|
||||
const bool hasText = features.testFlag(Text);
|
||||
|
||||
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->fontComboBox->setEnabled(hasText);
|
||||
ui->fontLabel->setEnabled(hasText);
|
||||
ui->selectFontButton->setEnabled(hasText);
|
||||
|
||||
for (QRadioButton* radioButton : findChildren<QRadioButton*>())
|
||||
{
|
||||
radioButton->setEnabled(hasText);
|
||||
}
|
||||
ui->textAlignmentLabel->setEnabled(hasText);
|
||||
|
||||
ui->textAngleLabel->setEnabled(hasText);
|
||||
ui->textAngleEdit->setEnabled(hasText);
|
||||
|
||||
QPen pen(Qt::SolidLine);
|
||||
QBrush brush(Qt::transparent);
|
||||
QFont font = QGuiApplication::font();
|
||||
Qt::Alignment alignment = Qt::AlignCenter;
|
||||
PDFReal textAngle = 0.0;
|
||||
|
||||
if (styledElement)
|
||||
{
|
||||
pen = styledElement->getPen();
|
||||
brush = styledElement->getBrush();
|
||||
}
|
||||
|
||||
if (textElement)
|
||||
{
|
||||
font = textElement->getFont();
|
||||
alignment = textElement->getAlignment();
|
||||
textAngle = textElement->getAngle();
|
||||
}
|
||||
|
||||
setPen(pen, forceUpdate);
|
||||
setBrush(brush, forceUpdate);
|
||||
setFont(font, forceUpdate);
|
||||
setFontAlignment(alignment, forceUpdate);
|
||||
setTextAngle(textAngle, forceUpdate);
|
||||
}
|
||||
|
||||
void PDFPageContentEditorStyleSettings::setPen(const QPen& pen, bool forceUpdate)
|
||||
{
|
||||
if (m_pen != pen || forceUpdate)
|
||||
{
|
||||
const bool oldBlockSignals = blockSignals(true);
|
||||
|
||||
m_pen = pen;
|
||||
ui->penWidthEdit->setValue(pen.widthF());
|
||||
ui->penStyleCombo->setCurrentIndex(ui->penStyleCombo->findData(int(pen.style())));
|
||||
setColorToComboBox(ui->penColorCombo, pen.color());
|
||||
|
||||
blockSignals(oldBlockSignals);
|
||||
emit penChanged(m_pen);
|
||||
}
|
||||
}
|
||||
|
||||
void PDFPageContentEditorStyleSettings::setBrush(const QBrush& brush, bool forceUpdate)
|
||||
{
|
||||
if (m_brush != brush || forceUpdate)
|
||||
{
|
||||
const bool oldBlockSignals = blockSignals(true);
|
||||
|
||||
m_brush = brush;
|
||||
ui->brushStyleCombo->setCurrentIndex(ui->brushStyleCombo->findData(int(brush.style())));
|
||||
setColorToComboBox(ui->brushColorCombo, brush.color());
|
||||
|
||||
blockSignals(oldBlockSignals);
|
||||
emit brushChanged(m_brush);
|
||||
}
|
||||
}
|
||||
|
||||
void PDFPageContentEditorStyleSettings::setFont(const QFont& font, bool forceUpdate)
|
||||
{
|
||||
if (m_font != font || forceUpdate)
|
||||
{
|
||||
const bool oldBlockSignals = blockSignals(true);
|
||||
|
||||
m_font = font;
|
||||
ui->fontComboBox->setCurrentFont(m_font);
|
||||
|
||||
blockSignals(oldBlockSignals);
|
||||
emit fontChanged(m_font);
|
||||
}
|
||||
}
|
||||
|
||||
void PDFPageContentEditorStyleSettings::setFontAlignment(Qt::Alignment alignment, bool forceUpdate)
|
||||
{
|
||||
if (m_alignment != alignment || forceUpdate)
|
||||
{
|
||||
const bool oldBlockSignals = blockSignals(true);
|
||||
|
||||
for (QRadioButton* radioButton : findChildren<QRadioButton*>())
|
||||
{
|
||||
radioButton->setChecked(false);
|
||||
}
|
||||
|
||||
m_alignment = alignment;
|
||||
QRadioButton* radioButton = qobject_cast<QRadioButton*>(m_alignmentMapper.mapping(int(alignment)));
|
||||
radioButton->setChecked(true);
|
||||
|
||||
blockSignals(oldBlockSignals);
|
||||
emit alignmentChanged(m_alignment);
|
||||
}
|
||||
}
|
||||
|
||||
void PDFPageContentEditorStyleSettings::setTextAngle(PDFReal angle, bool forceUpdate)
|
||||
{
|
||||
if (ui->textAngleEdit->value() != angle || forceUpdate)
|
||||
{
|
||||
const bool oldBlockSignals = blockSignals(true);
|
||||
ui->textAngleEdit->setValue(angle);
|
||||
blockSignals(oldBlockSignals);
|
||||
emit textAngleChanged(ui->textAngleEdit->value());
|
||||
}
|
||||
}
|
||||
|
||||
bool PDFPageContentEditorStyleSettings::showEditElementStyleDialog(QWidget* parent,
|
||||
PDFPageContentElement* element)
|
||||
{
|
||||
QDialog dialog(parent);
|
||||
dialog.setWindowTitle(tr("Edit Item"));
|
||||
dialog.setLayout(new QVBoxLayout());
|
||||
|
||||
QTextEdit* textEdit = nullptr;
|
||||
PDFPageContentStyledElement* styledElement = dynamic_cast<PDFPageContentStyledElement*>(element);
|
||||
PDFPageContentElementTextBox* textElement = dynamic_cast<PDFPageContentElementTextBox*>(element);
|
||||
if (textElement)
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
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()); });
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
QIcon PDFPageContentEditorStyleSettings::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;
|
||||
}
|
||||
|
||||
void PDFPageContentEditorStyleSettings::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 PDFPageContentEditorStyleSettings::onSelectFontButtonClicked()
|
||||
{
|
||||
bool ok = false;
|
||||
QFont font = QFontDialog::getFont(&ok, m_font, this, tr("Select Font"));
|
||||
|
||||
if (ok && m_font != font)
|
||||
{
|
||||
m_font = font;
|
||||
ui->fontComboBox->setCurrentFont(m_font);
|
||||
emit fontChanged(m_font);
|
||||
}
|
||||
}
|
||||
|
||||
void PDFPageContentEditorStyleSettings::setPenColor(QColor color)
|
||||
{
|
||||
if (color.isValid() && m_pen.color() != color)
|
||||
{
|
||||
m_pen.setColor(color);
|
||||
setColorToComboBox(ui->penColorCombo, color);
|
||||
emit penChanged(m_pen);
|
||||
}
|
||||
}
|
||||
|
||||
void PDFPageContentEditorStyleSettings::onSelectPenColorButtonClicked()
|
||||
{
|
||||
QColor color = QColorDialog::getColor(m_pen.color(), this, tr("Select Color for Pen"), QColorDialog::ShowAlphaChannel);
|
||||
setPenColor(color);
|
||||
}
|
||||
|
||||
void PDFPageContentEditorStyleSettings::setBrushColor(QColor color)
|
||||
{
|
||||
if (color.isValid() && m_brush.color() != color)
|
||||
{
|
||||
m_brush.setColor(color);
|
||||
setColorToComboBox(ui->brushColorCombo, color);
|
||||
emit brushChanged(m_brush);
|
||||
}
|
||||
}
|
||||
|
||||
Qt::Alignment PDFPageContentEditorStyleSettings::getAlignment() const
|
||||
{
|
||||
return m_alignment;
|
||||
}
|
||||
|
||||
PDFReal PDFPageContentEditorStyleSettings::getTextAngle() const
|
||||
{
|
||||
return ui->textAngleEdit->value();
|
||||
}
|
||||
|
||||
const QFont& PDFPageContentEditorStyleSettings::getFont() const
|
||||
{
|
||||
return m_font;
|
||||
}
|
||||
|
||||
const QBrush& PDFPageContentEditorStyleSettings::getBrush() const
|
||||
{
|
||||
return m_brush;
|
||||
}
|
||||
|
||||
const QPen& PDFPageContentEditorStyleSettings::getPen() const
|
||||
{
|
||||
return m_pen;
|
||||
}
|
||||
|
||||
void PDFPageContentEditorStyleSettings::onSelectBrushColorButtonClicked()
|
||||
{
|
||||
QColor color = QColorDialog::getColor(m_pen.color(), this, tr("Select Color for Brush"), QColorDialog::ShowAlphaChannel);
|
||||
setBrushColor(color);
|
||||
}
|
||||
|
||||
void PDFPageContentEditorStyleSettings::onPenWidthChanged(double value)
|
||||
{
|
||||
if (m_pen.widthF() != value)
|
||||
{
|
||||
m_pen.setWidthF(value);
|
||||
emit penChanged(m_pen);
|
||||
}
|
||||
}
|
||||
|
||||
void PDFPageContentEditorStyleSettings::onTextAngleChanged(double value)
|
||||
{
|
||||
emit textAngleChanged(value);
|
||||
}
|
||||
|
||||
void PDFPageContentEditorStyleSettings::onAlignmentRadioButtonClicked(int alignment)
|
||||
{
|
||||
Qt::Alignment alignmentValue = static_cast<Qt::Alignment>(alignment);
|
||||
if (m_alignment != alignmentValue)
|
||||
{
|
||||
m_alignment = alignmentValue;
|
||||
emit alignmentChanged(m_alignment);
|
||||
}
|
||||
}
|
||||
|
||||
void PDFPageContentEditorStyleSettings::onPenStyleChanged()
|
||||
{
|
||||
Qt::PenStyle penStyle = static_cast<Qt::PenStyle>(ui->penStyleCombo->currentData().toInt());
|
||||
if (m_pen.style() != penStyle)
|
||||
{
|
||||
m_pen.setStyle(penStyle);
|
||||
emit penChanged(m_pen);
|
||||
}
|
||||
}
|
||||
|
||||
void PDFPageContentEditorStyleSettings::onBrushStyleChanged()
|
||||
{
|
||||
Qt::BrushStyle brushStyle = static_cast<Qt::BrushStyle>(ui->brushStyleCombo->currentData().toInt());
|
||||
if (m_brush.style() != brushStyle)
|
||||
{
|
||||
m_brush.setStyle(brushStyle);
|
||||
emit brushChanged(m_brush);
|
||||
}
|
||||
}
|
||||
|
||||
void PDFPageContentEditorStyleSettings::onPenColorComboTextChanged()
|
||||
{
|
||||
QColor color(ui->penColorCombo->currentText());
|
||||
if (color.isValid())
|
||||
{
|
||||
setColorToComboBox(ui->penColorCombo, color);
|
||||
|
||||
if (m_pen.color() != color)
|
||||
{
|
||||
m_pen.setColor(color);
|
||||
emit penChanged(m_pen);
|
||||
}
|
||||
}
|
||||
else if (ui->penColorCombo->currentIndex() != -1)
|
||||
{
|
||||
ui->penColorCombo->setEditText(ui->penColorCombo->itemText(ui->penColorCombo->currentIndex()));
|
||||
}
|
||||
}
|
||||
|
||||
void PDFPageContentEditorStyleSettings::onPenColorComboIndexChanged()
|
||||
{
|
||||
const int index = ui->penColorCombo->currentIndex();
|
||||
QColor color = ui->penColorCombo->itemData(index, Qt::UserRole).value<QColor>();
|
||||
if (color.isValid() && m_pen.color() != color)
|
||||
{
|
||||
m_pen.setColor(color);
|
||||
emit penChanged(m_pen);
|
||||
}
|
||||
}
|
||||
|
||||
void PDFPageContentEditorStyleSettings::onBrushColorComboTextChanged()
|
||||
{
|
||||
QColor color(ui->brushColorCombo->currentText());
|
||||
if (color.isValid())
|
||||
{
|
||||
setColorToComboBox(ui->brushColorCombo, color);
|
||||
|
||||
if (m_brush.color() != color)
|
||||
{
|
||||
m_brush.setColor(color);
|
||||
emit brushChanged(m_brush);
|
||||
}
|
||||
}
|
||||
else if (ui->brushColorCombo->currentIndex() != -1)
|
||||
{
|
||||
ui->brushColorCombo->setEditText(ui->brushColorCombo->itemText(ui->brushColorCombo->currentIndex()));
|
||||
}
|
||||
}
|
||||
|
||||
void PDFPageContentEditorStyleSettings::onBrushColorComboIndexChanged()
|
||||
{
|
||||
const int index = ui->brushColorCombo->currentIndex();
|
||||
QColor color = ui->brushColorCombo->itemData(index, Qt::UserRole).value<QColor>();
|
||||
if (color.isValid() && m_brush.color() != color)
|
||||
{
|
||||
m_brush.setColor(color);
|
||||
emit brushChanged(m_brush);
|
||||
}
|
||||
}
|
||||
|
||||
void PDFPageContentEditorStyleSettings::onFontChanged(const QFont& font)
|
||||
{
|
||||
if (m_font != font)
|
||||
{
|
||||
m_font = font;
|
||||
emit fontChanged(m_font);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace pdf
|
118
Pdf4QtLib/sources/pdfpagecontenteditorstylesettings.h
Normal file
118
Pdf4QtLib/sources/pdfpagecontenteditorstylesettings.h
Normal file
@@ -0,0 +1,118 @@
|
||||
// Copyright (C) 2022 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 <https://www.gnu.org/licenses/>.
|
||||
|
||||
#ifndef PDFPAGECONTENTEDITORSTYLESETTINGS_H
|
||||
#define PDFPAGECONTENTEDITORSTYLESETTINGS_H
|
||||
|
||||
#include "pdfglobal.h"
|
||||
|
||||
#include <QPen>
|
||||
#include <QIcon>
|
||||
#include <QFont>
|
||||
#include <QBrush>
|
||||
#include <QWidget>
|
||||
#include <QSignalMapper>
|
||||
|
||||
namespace Ui
|
||||
{
|
||||
class PDFPageContentEditorStyleSettings;
|
||||
}
|
||||
|
||||
class QComboBox;
|
||||
|
||||
namespace pdf
|
||||
{
|
||||
class PDFPageContentElement;
|
||||
|
||||
class PDF4QTLIBSHARED_EXPORT PDFPageContentEditorStyleSettings : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
|
||||
enum StyleFeature
|
||||
{
|
||||
None = 0,
|
||||
Pen = 1 << 0,
|
||||
PenColor = 1 << 1,
|
||||
Brush = 1 << 2,
|
||||
Text = 1 << 3
|
||||
};
|
||||
Q_DECLARE_FLAGS(StyleFeatures, StyleFeature)
|
||||
|
||||
explicit PDFPageContentEditorStyleSettings(QWidget* parent);
|
||||
virtual ~PDFPageContentEditorStyleSettings() override;
|
||||
|
||||
/// Loads data from element, element can be nullptr
|
||||
/// \param element Element
|
||||
void loadFromElement(const PDFPageContentElement* element, bool forceUpdate);
|
||||
|
||||
void setPen(const QPen& pen, bool forceUpdate);
|
||||
void setBrush(const QBrush& brush, bool forceUpdate);
|
||||
void setFont(const QFont& font, bool forceUpdate);
|
||||
void setFontAlignment(Qt::Alignment alignment, bool forceUpdate);
|
||||
void setTextAngle(PDFReal angle, bool forceUpdate);
|
||||
|
||||
static bool showEditElementStyleDialog(QWidget* parent, PDFPageContentElement* element);
|
||||
|
||||
const QPen& getPen() const;
|
||||
const QBrush& getBrush() const;
|
||||
const QFont& getFont() const;
|
||||
Qt::Alignment getAlignment() const;
|
||||
PDFReal getTextAngle() const;
|
||||
|
||||
signals:
|
||||
void penChanged(const QPen& pen);
|
||||
void brushChanged(const QBrush& brush);
|
||||
void fontChanged(const QFont& font);
|
||||
void alignmentChanged(Qt::Alignment alignment);
|
||||
void textAngleChanged(pdf::PDFReal angle);
|
||||
|
||||
private slots:
|
||||
void onSelectFontButtonClicked();
|
||||
void onSelectPenColorButtonClicked();
|
||||
void onSelectBrushColorButtonClicked();
|
||||
void onPenWidthChanged(double value);
|
||||
void onTextAngleChanged(double value);
|
||||
void onAlignmentRadioButtonClicked(int alignment);
|
||||
void onPenStyleChanged();
|
||||
void onBrushStyleChanged();
|
||||
void onPenColorComboTextChanged();
|
||||
void onPenColorComboIndexChanged();
|
||||
void onBrushColorComboTextChanged();
|
||||
void onBrushColorComboIndexChanged();
|
||||
|
||||
private:
|
||||
Ui::PDFPageContentEditorStyleSettings* ui;
|
||||
|
||||
void onFontChanged(const QFont& font);
|
||||
void setColorToComboBox(QComboBox* comboBox, QColor color);
|
||||
QIcon getIconForColor(QColor color) const;
|
||||
|
||||
void setPenColor(QColor color);
|
||||
void setBrushColor(QColor color);
|
||||
|
||||
QPen m_pen;
|
||||
QBrush m_brush;
|
||||
QFont m_font;
|
||||
Qt::Alignment m_alignment = Qt::AlignCenter;
|
||||
QSignalMapper m_alignmentMapper;
|
||||
};
|
||||
|
||||
} // namespace pdf
|
||||
|
||||
#endif // PDFPAGECONTENTEDITORSTYLESETTINGS_H
|
230
Pdf4QtLib/sources/pdfpagecontenteditorstylesettings.ui
Normal file
230
Pdf4QtLib/sources/pdfpagecontenteditorstylesettings.ui
Normal file
@@ -0,0 +1,230 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>PDFPageContentEditorStyleSettings</class>
|
||||
<widget class="QWidget" name="PDFPageContentEditorStyleSettings">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>344</width>
|
||||
<height>310</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Style Settings</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<item row="7" column="0">
|
||||
<widget class="QLabel" name="textAlignmentLabel">
|
||||
<property name="text">
|
||||
<string>Text Alignment</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QLabel" name="penStyleLabel">
|
||||
<property name="text">
|
||||
<string>Pen Style</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="8" column="0">
|
||||
<spacer name="verticalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeType">
|
||||
<enum>QSizePolicy::MinimumExpanding</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item row="4" column="1">
|
||||
<widget class="QComboBox" name="brushColorCombo">
|
||||
<property name="editable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1" colspan="2">
|
||||
<widget class="QComboBox" name="penStyleCombo"/>
|
||||
</item>
|
||||
<item row="0" column="1" colspan="2">
|
||||
<widget class="QDoubleSpinBox" name="penWidthEdit"/>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QComboBox" name="penColorCombo">
|
||||
<property name="editable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="penWidthLabel">
|
||||
<property name="text">
|
||||
<string>Pen Width</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="0">
|
||||
<widget class="QLabel" name="brushColorLabel">
|
||||
<property name="text">
|
||||
<string>Brush Color</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="7" column="1" colspan="2">
|
||||
<layout class="QGridLayout" name="textAlignmentLayout">
|
||||
<item row="1" column="0">
|
||||
<widget class="QRadioButton" name="al21Button">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="2">
|
||||
<widget class="QRadioButton" name="al13Button">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="QRadioButton" name="al32Button">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QRadioButton" name="al22Button">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="2">
|
||||
<widget class="QRadioButton" name="al33Button">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QRadioButton" name="al31Button">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="2">
|
||||
<widget class="QRadioButton" name="al23Button">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
<widget class="QRadioButton" name="al11Button">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QRadioButton" name="al12Button">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="3">
|
||||
<spacer name="horizontalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="6" column="1" colspan="2">
|
||||
<widget class="QDoubleSpinBox" name="textAngleEdit">
|
||||
<property name="minimum">
|
||||
<double>-90.000000000000000</double>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<double>90.000000000000000</double>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="2">
|
||||
<widget class="QToolButton" name="selectBrushColorButton">
|
||||
<property name="text">
|
||||
<string>...</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="penColorLabel">
|
||||
<property name="text">
|
||||
<string>Pen Color</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="5" column="0">
|
||||
<widget class="QLabel" name="fontLabel">
|
||||
<property name="text">
|
||||
<string>Font</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="5" column="1">
|
||||
<widget class="QFontComboBox" name="fontComboBox"/>
|
||||
</item>
|
||||
<item row="6" column="0">
|
||||
<widget class="QLabel" name="textAngleLabel">
|
||||
<property name="text">
|
||||
<string>Text Angle</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="2">
|
||||
<widget class="QToolButton" name="selectPenColorButton">
|
||||
<property name="text">
|
||||
<string>...</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="5" column="2">
|
||||
<widget class="QToolButton" name="selectFontButton">
|
||||
<property name="text">
|
||||
<string>...</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="0">
|
||||
<widget class="QLabel" name="brushStyleLabel">
|
||||
<property name="text">
|
||||
<string>Brush Style</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="1" colspan="2">
|
||||
<widget class="QComboBox" name="brushStyleCombo"/>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
968
Pdf4QtLib/sources/pdfpagecontenteditortools.cpp
Normal file
968
Pdf4QtLib/sources/pdfpagecontenteditortools.cpp
Normal file
@@ -0,0 +1,968 @@
|
||||
// Copyright (C) 2022 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 <https://www.gnu.org/licenses/>.
|
||||
|
||||
#include "pdfpagecontenteditortools.h"
|
||||
#include "pdfpagecontentelements.h"
|
||||
#include "pdfpainterutils.h"
|
||||
#include "pdftexteditpseudowidget.h"
|
||||
#include "pdfdrawwidget.h"
|
||||
|
||||
#include <QPen>
|
||||
#include <QPainter>
|
||||
#include <QMouseEvent>
|
||||
#include <QFileDialog>
|
||||
#include <QImageReader>
|
||||
#include <QGuiApplication>
|
||||
|
||||
namespace pdf
|
||||
{
|
||||
|
||||
PDFCreatePCElementTool::PDFCreatePCElementTool(PDFDrawWidgetProxy* proxy,
|
||||
PDFPageContentScene* scene,
|
||||
QAction* action,
|
||||
QObject* parent) :
|
||||
PDFWidgetTool(proxy, action, parent),
|
||||
m_scene(scene)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void PDFCreatePCElementTool::setPen(const QPen& pen)
|
||||
{
|
||||
if (PDFPageContentStyledElement* styledElement = dynamic_cast<PDFPageContentStyledElement*>(getElement()))
|
||||
{
|
||||
styledElement->setPen(pen);
|
||||
emit getProxy()->repaintNeeded();
|
||||
}
|
||||
}
|
||||
|
||||
void PDFCreatePCElementTool::setBrush(const QBrush& brush)
|
||||
{
|
||||
if (PDFPageContentStyledElement* styledElement = dynamic_cast<PDFPageContentStyledElement*>(getElement()))
|
||||
{
|
||||
styledElement->setBrush(brush);
|
||||
emit getProxy()->repaintNeeded();
|
||||
}
|
||||
}
|
||||
|
||||
void PDFCreatePCElementTool::setFont(const QFont& font)
|
||||
{
|
||||
if (PDFPageContentElementTextBox* textBoxElement = dynamic_cast<PDFPageContentElementTextBox*>(getElement()))
|
||||
{
|
||||
textBoxElement->setFont(font);
|
||||
emit getProxy()->repaintNeeded();
|
||||
}
|
||||
}
|
||||
|
||||
void PDFCreatePCElementTool::setAlignment(Qt::Alignment alignment)
|
||||
{
|
||||
if (PDFPageContentElementTextBox* textBoxElement = dynamic_cast<PDFPageContentElementTextBox*>(getElement()))
|
||||
{
|
||||
textBoxElement->setAlignment(alignment);
|
||||
emit getProxy()->repaintNeeded();
|
||||
}
|
||||
}
|
||||
|
||||
void PDFCreatePCElementTool::setTextAngle(PDFReal angle)
|
||||
{
|
||||
if (PDFPageContentElementTextBox* textBoxElement = dynamic_cast<PDFPageContentElementTextBox*>(getElement()))
|
||||
{
|
||||
textBoxElement->setAngle(angle);
|
||||
emit getProxy()->repaintNeeded();
|
||||
}
|
||||
}
|
||||
|
||||
QRectF PDFCreatePCElementTool::getRectangleFromPickTool(PDFPickTool* pickTool,
|
||||
const QMatrix& pagePointToDevicePointMatrix)
|
||||
{
|
||||
const std::vector<QPointF>& points = pickTool->getPickedPoints();
|
||||
if (points.empty())
|
||||
{
|
||||
return QRectF();
|
||||
}
|
||||
|
||||
QPointF mousePoint = pagePointToDevicePointMatrix.inverted().map(pickTool->getSnappedPoint());
|
||||
QPointF point = points.front();
|
||||
qreal xMin = qMin(point.x(), mousePoint.x());
|
||||
qreal xMax = qMax(point.x(), mousePoint.x());
|
||||
qreal yMin = qMin(point.y(), mousePoint.y());
|
||||
qreal yMax = qMax(point.y(), mousePoint.y());
|
||||
qreal width = xMax - xMin;
|
||||
qreal height = yMax - yMin;
|
||||
|
||||
if (!qFuzzyIsNull(width) && !qFuzzyIsNull(height))
|
||||
{
|
||||
return QRectF(xMin, yMin, width, height);
|
||||
}
|
||||
|
||||
return QRectF();
|
||||
}
|
||||
|
||||
PDFCreatePCElementRectangleTool::PDFCreatePCElementRectangleTool(PDFDrawWidgetProxy* proxy,
|
||||
PDFPageContentScene* scene,
|
||||
QAction* action,
|
||||
bool isRounded,
|
||||
QObject* parent) :
|
||||
BaseClass(proxy, scene, action, parent),
|
||||
m_pickTool(nullptr),
|
||||
m_element(nullptr)
|
||||
{
|
||||
m_pickTool = new PDFPickTool(proxy, PDFPickTool::Mode::Rectangles, this);
|
||||
m_pickTool->setDrawSelectionRectangle(false);
|
||||
addTool(m_pickTool);
|
||||
connect(m_pickTool, &PDFPickTool::rectanglePicked, this, &PDFCreatePCElementRectangleTool::onRectanglePicked);
|
||||
|
||||
QPen pen(Qt::SolidLine);
|
||||
pen.setWidthF(1.0);
|
||||
|
||||
m_element = new PDFPageContentElementRectangle();
|
||||
m_element->setBrush(Qt::NoBrush);
|
||||
m_element->setPen(std::move(pen));
|
||||
m_element->setRounded(isRounded);
|
||||
|
||||
updateActions();
|
||||
}
|
||||
|
||||
PDFCreatePCElementRectangleTool::~PDFCreatePCElementRectangleTool()
|
||||
{
|
||||
delete m_element;
|
||||
}
|
||||
|
||||
void PDFCreatePCElementRectangleTool::drawPage(QPainter* painter,
|
||||
PDFInteger pageIndex,
|
||||
const PDFPrecompiledPage* compiledPage,
|
||||
PDFTextLayoutGetter& layoutGetter,
|
||||
const QMatrix& pagePointToDevicePointMatrix,
|
||||
QList<PDFRenderError>& errors) const
|
||||
{
|
||||
BaseClass::drawPage(painter, pageIndex, compiledPage, layoutGetter, pagePointToDevicePointMatrix, errors);
|
||||
|
||||
if (pageIndex != m_pickTool->getPageIndex())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
QRectF rectangle = getRectangleFromPickTool(m_pickTool, pagePointToDevicePointMatrix);
|
||||
if (!rectangle.isValid())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
m_element->setPageIndex(pageIndex);
|
||||
m_element->setRectangle(rectangle);
|
||||
|
||||
m_element->drawPage(painter, pageIndex, compiledPage, layoutGetter, pagePointToDevicePointMatrix, errors);
|
||||
}
|
||||
|
||||
const PDFPageContentElement* PDFCreatePCElementRectangleTool::getElement() const
|
||||
{
|
||||
return m_element;
|
||||
}
|
||||
|
||||
PDFPageContentElement* PDFCreatePCElementRectangleTool::getElement()
|
||||
{
|
||||
return m_element;
|
||||
}
|
||||
|
||||
void PDFCreatePCElementRectangleTool::onRectanglePicked(PDFInteger pageIndex, QRectF pageRectangle)
|
||||
{
|
||||
if (pageRectangle.isEmpty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
m_element->setPageIndex(pageIndex);
|
||||
m_element->setRectangle(pageRectangle);
|
||||
m_scene->addElement(m_element->clone());
|
||||
|
||||
setActive(false);
|
||||
}
|
||||
|
||||
PDFCreatePCElementLineTool::PDFCreatePCElementLineTool(PDFDrawWidgetProxy* proxy,
|
||||
PDFPageContentScene* scene,
|
||||
QAction* action,
|
||||
bool isHorizontal,
|
||||
bool isVertical,
|
||||
QObject* parent) :
|
||||
BaseClass(proxy, scene, action, parent),
|
||||
m_pickTool(nullptr),
|
||||
m_element(nullptr)
|
||||
{
|
||||
m_pickTool = new PDFPickTool(proxy, PDFPickTool::Mode::Points, this);
|
||||
m_pickTool->setDrawSelectionRectangle(false);
|
||||
addTool(m_pickTool);
|
||||
connect(m_pickTool, &PDFPickTool::pointPicked, this, &PDFCreatePCElementLineTool::onPointPicked);
|
||||
|
||||
QPen pen(Qt::SolidLine);
|
||||
pen.setWidthF(2.0);
|
||||
pen.setCapStyle(Qt::RoundCap);
|
||||
|
||||
PDFPageContentElementLine::LineGeometry geometry = PDFPageContentElementLine::LineGeometry::General;
|
||||
|
||||
if (isHorizontal)
|
||||
{
|
||||
geometry = PDFPageContentElementLine::LineGeometry::Horizontal;
|
||||
}
|
||||
|
||||
if (isVertical)
|
||||
{
|
||||
geometry = PDFPageContentElementLine::LineGeometry::Vertical;
|
||||
}
|
||||
|
||||
m_element = new PDFPageContentElementLine();
|
||||
m_element->setBrush(Qt::NoBrush);
|
||||
m_element->setPen(std::move(pen));
|
||||
m_element->setGeometry(geometry);
|
||||
|
||||
updateActions();
|
||||
}
|
||||
|
||||
PDFCreatePCElementLineTool::~PDFCreatePCElementLineTool()
|
||||
{
|
||||
delete m_element;
|
||||
}
|
||||
|
||||
void PDFCreatePCElementLineTool::drawPage(QPainter* painter,
|
||||
PDFInteger pageIndex,
|
||||
const PDFPrecompiledPage* compiledPage,
|
||||
PDFTextLayoutGetter& layoutGetter,
|
||||
const QMatrix& pagePointToDevicePointMatrix,
|
||||
QList<PDFRenderError>& errors) const
|
||||
{
|
||||
BaseClass::drawPage(painter, pageIndex, compiledPage, layoutGetter, pagePointToDevicePointMatrix, errors);
|
||||
|
||||
if (pageIndex != m_pickTool->getPageIndex() || !m_startPoint)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
m_element->setPageIndex(pageIndex);
|
||||
|
||||
QPointF startPoint = *m_startPoint;
|
||||
QPointF endPoint = pagePointToDevicePointMatrix.inverted().map(m_pickTool->getSnappedPoint());
|
||||
QLineF line(startPoint, endPoint);
|
||||
|
||||
if (!qFuzzyIsNull(line.length()))
|
||||
{
|
||||
m_element->setLine(line);
|
||||
}
|
||||
|
||||
m_element->drawPage(painter, pageIndex, compiledPage, layoutGetter, pagePointToDevicePointMatrix, errors);
|
||||
}
|
||||
|
||||
const PDFPageContentElement* PDFCreatePCElementLineTool::getElement() const
|
||||
{
|
||||
return m_element;
|
||||
}
|
||||
|
||||
PDFPageContentElement* PDFCreatePCElementLineTool::getElement()
|
||||
{
|
||||
return m_element;
|
||||
}
|
||||
|
||||
void PDFCreatePCElementLineTool::clear()
|
||||
{
|
||||
m_startPoint = std::nullopt;
|
||||
}
|
||||
|
||||
void PDFCreatePCElementLineTool::onPointPicked(PDFInteger pageIndex, QPointF pagePoint)
|
||||
{
|
||||
if (!m_startPoint || m_element->getPageIndex() != pageIndex)
|
||||
{
|
||||
m_startPoint = pagePoint;
|
||||
m_element->setPageIndex(pageIndex);
|
||||
m_element->setLine(QLineF(pagePoint, pagePoint));
|
||||
return;
|
||||
}
|
||||
|
||||
if (qFuzzyCompare(m_startPoint.value().x(), pagePoint.x()) &&
|
||||
qFuzzyCompare(m_startPoint.value().y(), pagePoint.y()))
|
||||
{
|
||||
// Jakub Melka: Point is same as the start point
|
||||
clear();
|
||||
return;
|
||||
}
|
||||
|
||||
QLineF line = m_element->getLine();
|
||||
line.setP2(pagePoint);
|
||||
m_element->setLine(line);
|
||||
m_scene->addElement(m_element->clone());
|
||||
clear();
|
||||
|
||||
setActive(false);
|
||||
}
|
||||
|
||||
PDFCreatePCElementImageTool::PDFCreatePCElementImageTool(PDFDrawWidgetProxy* proxy,
|
||||
PDFPageContentScene* scene,
|
||||
QAction* action,
|
||||
QByteArray content,
|
||||
bool askSelectImage,
|
||||
QObject* parent) :
|
||||
BaseClass(proxy, scene, action, parent),
|
||||
m_pickTool(nullptr),
|
||||
m_element(nullptr),
|
||||
m_askSelectImage(askSelectImage)
|
||||
{
|
||||
m_pickTool = new PDFPickTool(proxy, PDFPickTool::Mode::Rectangles, this);
|
||||
m_pickTool->setDrawSelectionRectangle(false);
|
||||
addTool(m_pickTool);
|
||||
connect(m_pickTool, &PDFPickTool::rectanglePicked, this, &PDFCreatePCElementImageTool::onRectanglePicked);
|
||||
|
||||
m_element = new PDFPageContentImageElement();
|
||||
m_element->setContent(content);
|
||||
|
||||
updateActions();
|
||||
}
|
||||
|
||||
PDFCreatePCElementImageTool::~PDFCreatePCElementImageTool()
|
||||
{
|
||||
delete m_element;
|
||||
}
|
||||
|
||||
void PDFCreatePCElementImageTool::drawPage(QPainter* painter,
|
||||
PDFInteger pageIndex,
|
||||
const PDFPrecompiledPage* compiledPage,
|
||||
PDFTextLayoutGetter& layoutGetter,
|
||||
const QMatrix& pagePointToDevicePointMatrix,
|
||||
QList<PDFRenderError>& errors) const
|
||||
{
|
||||
BaseClass::drawPage(painter, pageIndex, compiledPage, layoutGetter, pagePointToDevicePointMatrix, errors);
|
||||
|
||||
if (pageIndex != m_pickTool->getPageIndex())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
QRectF rectangle = getRectangleFromPickTool(m_pickTool, pagePointToDevicePointMatrix);
|
||||
if (!rectangle.isValid())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
m_element->setPageIndex(pageIndex);
|
||||
m_element->setRectangle(rectangle);
|
||||
|
||||
{
|
||||
PDFPainterStateGuard guard(painter);
|
||||
painter->setWorldMatrix(pagePointToDevicePointMatrix, true);
|
||||
painter->setRenderHint(QPainter::Antialiasing);
|
||||
painter->setPen(Qt::DotLine);
|
||||
painter->setBrush(Qt::NoBrush);
|
||||
painter->drawRect(rectangle);
|
||||
}
|
||||
|
||||
m_element->drawPage(painter, pageIndex, compiledPage, layoutGetter, pagePointToDevicePointMatrix, errors);
|
||||
}
|
||||
|
||||
const PDFPageContentElement* PDFCreatePCElementImageTool::getElement() const
|
||||
{
|
||||
return m_element;
|
||||
}
|
||||
|
||||
PDFPageContentElement* PDFCreatePCElementImageTool::getElement()
|
||||
{
|
||||
return m_element;
|
||||
}
|
||||
|
||||
void PDFCreatePCElementImageTool::setActiveImpl(bool active)
|
||||
{
|
||||
BaseClass::setActiveImpl(active);
|
||||
|
||||
if (active && m_askSelectImage)
|
||||
{
|
||||
QTimer::singleShot(0, this, &PDFCreatePCElementImageTool::selectImage);
|
||||
}
|
||||
}
|
||||
|
||||
void PDFCreatePCElementImageTool::selectImage()
|
||||
{
|
||||
if (m_imageDirectory.isEmpty())
|
||||
{
|
||||
QStringList pictureDirectiories = QStandardPaths::standardLocations(QStandardPaths::PicturesLocation);
|
||||
if (!pictureDirectiories.isEmpty())
|
||||
{
|
||||
m_imageDirectory = pictureDirectiories.last();
|
||||
}
|
||||
else
|
||||
{
|
||||
m_imageDirectory = QDir::currentPath();
|
||||
}
|
||||
}
|
||||
|
||||
QList<QByteArray> mimeTypes = QImageReader::supportedMimeTypes();
|
||||
QStringList mimeTypeFilters;
|
||||
for (const QByteArray& mimeType : mimeTypes)
|
||||
{
|
||||
mimeTypeFilters.append(mimeType);
|
||||
}
|
||||
|
||||
QFileDialog dialog(getProxy()->getWidget(), tr("Select Image"));
|
||||
dialog.setDirectory(m_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();
|
||||
QFile file(fileName);
|
||||
if (file.open(QFile::ReadOnly))
|
||||
{
|
||||
m_element->setContent(file.readAll());
|
||||
file.close();
|
||||
}
|
||||
else
|
||||
{
|
||||
setActive(false);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
setActive(false);
|
||||
}
|
||||
}
|
||||
|
||||
void PDFCreatePCElementImageTool::onRectanglePicked(PDFInteger pageIndex, QRectF pageRectangle)
|
||||
{
|
||||
if (pageRectangle.isEmpty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
m_element->setPageIndex(pageIndex);
|
||||
m_element->setRectangle(pageRectangle);
|
||||
m_scene->addElement(m_element->clone());
|
||||
|
||||
setActive(false);
|
||||
}
|
||||
|
||||
PDFCreatePCElementDotTool::PDFCreatePCElementDotTool(PDFDrawWidgetProxy* proxy,
|
||||
PDFPageContentScene* scene,
|
||||
QAction* action,
|
||||
QObject* parent) :
|
||||
BaseClass(proxy, scene, action, parent),
|
||||
m_pickTool(nullptr),
|
||||
m_element(nullptr)
|
||||
{
|
||||
m_pickTool = new PDFPickTool(proxy, PDFPickTool::Mode::Points, this);
|
||||
m_pickTool->setDrawSelectionRectangle(false);
|
||||
addTool(m_pickTool);
|
||||
connect(m_pickTool, &PDFPickTool::pointPicked, this, &PDFCreatePCElementDotTool::onPointPicked);
|
||||
|
||||
QPen pen(Qt::SolidLine);
|
||||
pen.setWidthF(5.0);
|
||||
pen.setCapStyle(Qt::RoundCap);
|
||||
|
||||
m_element = new PDFPageContentElementDot();
|
||||
m_element->setBrush(Qt::NoBrush);
|
||||
m_element->setPen(std::move(pen));
|
||||
|
||||
updateActions();
|
||||
}
|
||||
|
||||
PDFCreatePCElementDotTool::~PDFCreatePCElementDotTool()
|
||||
{
|
||||
delete m_element;
|
||||
}
|
||||
|
||||
void PDFCreatePCElementDotTool::drawPage(QPainter* painter,
|
||||
PDFInteger pageIndex,
|
||||
const PDFPrecompiledPage* compiledPage,
|
||||
PDFTextLayoutGetter& layoutGetter,
|
||||
const QMatrix& pagePointToDevicePointMatrix,
|
||||
QList<PDFRenderError>& errors) const
|
||||
{
|
||||
BaseClass::drawPage(painter, pageIndex, compiledPage, layoutGetter, pagePointToDevicePointMatrix, errors);
|
||||
|
||||
QPointF point = pagePointToDevicePointMatrix.inverted().map(m_pickTool->getSnappedPoint());
|
||||
|
||||
PDFPainterStateGuard guard(painter);
|
||||
painter->setWorldMatrix(pagePointToDevicePointMatrix, true);
|
||||
painter->setRenderHint(QPainter::Antialiasing);
|
||||
painter->setPen(m_element->getPen());
|
||||
painter->setBrush(m_element->getBrush());
|
||||
painter->drawPoint(point);
|
||||
}
|
||||
|
||||
const PDFPageContentElement* PDFCreatePCElementDotTool::getElement() const
|
||||
{
|
||||
return m_element;
|
||||
}
|
||||
|
||||
PDFPageContentElement* PDFCreatePCElementDotTool::getElement()
|
||||
{
|
||||
return m_element;
|
||||
}
|
||||
|
||||
void PDFCreatePCElementDotTool::onPointPicked(PDFInteger pageIndex, QPointF pagePoint)
|
||||
{
|
||||
m_element->setPageIndex(pageIndex);
|
||||
m_element->setPoint(pagePoint);
|
||||
|
||||
m_scene->addElement(m_element->clone());
|
||||
m_element->setPageIndex(-1);
|
||||
|
||||
setActive(false);
|
||||
}
|
||||
|
||||
PDFCreatePCElementFreehandCurveTool::PDFCreatePCElementFreehandCurveTool(PDFDrawWidgetProxy* proxy,
|
||||
PDFPageContentScene* scene,
|
||||
QAction* action,
|
||||
QObject* parent) :
|
||||
BaseClass(proxy, scene, action, parent),
|
||||
m_element(nullptr)
|
||||
{
|
||||
QPen pen(Qt::SolidLine);
|
||||
pen.setWidthF(2.0);
|
||||
pen.setCapStyle(Qt::RoundCap);
|
||||
|
||||
m_element = new PDFPageContentElementFreehandCurve();
|
||||
m_element->setBrush(Qt::NoBrush);
|
||||
m_element->setPen(std::move(pen));
|
||||
}
|
||||
|
||||
PDFCreatePCElementFreehandCurveTool::~PDFCreatePCElementFreehandCurveTool()
|
||||
{
|
||||
delete m_element;
|
||||
}
|
||||
|
||||
void PDFCreatePCElementFreehandCurveTool::drawPage(QPainter* painter,
|
||||
PDFInteger pageIndex,
|
||||
const PDFPrecompiledPage* compiledPage,
|
||||
PDFTextLayoutGetter& layoutGetter,
|
||||
const QMatrix& pagePointToDevicePointMatrix,
|
||||
QList<PDFRenderError>& errors) const
|
||||
{
|
||||
BaseClass::drawPage(painter, pageIndex, compiledPage, layoutGetter, pagePointToDevicePointMatrix, errors);
|
||||
|
||||
if (pageIndex != m_element->getPageIndex() || m_element->isEmpty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
m_element->drawPage(painter, pageIndex, compiledPage, layoutGetter, pagePointToDevicePointMatrix, errors);
|
||||
}
|
||||
|
||||
const PDFPageContentElement* PDFCreatePCElementFreehandCurveTool::getElement() const
|
||||
{
|
||||
return m_element;
|
||||
}
|
||||
|
||||
PDFPageContentElement* PDFCreatePCElementFreehandCurveTool::getElement()
|
||||
{
|
||||
return m_element;
|
||||
}
|
||||
|
||||
void PDFCreatePCElementFreehandCurveTool::mousePressEvent(QWidget* widget, QMouseEvent* event)
|
||||
{
|
||||
Q_UNUSED(widget);
|
||||
event->accept();
|
||||
|
||||
if (event->button() == Qt::LeftButton)
|
||||
{
|
||||
// Try to perform pick point
|
||||
QPointF pagePoint;
|
||||
PDFInteger pageIndex = getProxy()->getPageUnderPoint(event->pos(), &pagePoint);
|
||||
if (pageIndex != -1 && // We have picked some point on page
|
||||
(m_element->getPageIndex() == -1 || m_element->getPageIndex() == pageIndex)) // We are under current page
|
||||
{
|
||||
m_element->setPageIndex(pageIndex);
|
||||
m_element->addStartPoint(pagePoint);
|
||||
}
|
||||
}
|
||||
else if (event->button() == Qt::RightButton)
|
||||
{
|
||||
resetTool();
|
||||
}
|
||||
|
||||
emit getProxy()->repaintNeeded();
|
||||
}
|
||||
|
||||
void PDFCreatePCElementFreehandCurveTool::mouseReleaseEvent(QWidget* widget, QMouseEvent* event)
|
||||
{
|
||||
Q_UNUSED(widget);
|
||||
event->accept();
|
||||
|
||||
if (event->button() == Qt::LeftButton)
|
||||
{
|
||||
// Try to perform pick point
|
||||
QPointF pagePoint;
|
||||
PDFInteger pageIndex = getProxy()->getPageUnderPoint(event->pos(), &pagePoint);
|
||||
if (pageIndex != -1 && // We have picked some point on page
|
||||
(m_element->getPageIndex() == pageIndex)) // We are under current page
|
||||
{
|
||||
m_element->setPageIndex(pageIndex);
|
||||
m_element->addPoint(pagePoint);
|
||||
|
||||
if (!m_element->isEmpty())
|
||||
{
|
||||
m_scene->addElement(m_element->clone());
|
||||
}
|
||||
}
|
||||
|
||||
resetTool();
|
||||
}
|
||||
|
||||
emit getProxy()->repaintNeeded();
|
||||
}
|
||||
|
||||
void PDFCreatePCElementFreehandCurveTool::mouseMoveEvent(QWidget* widget, QMouseEvent* event)
|
||||
{
|
||||
Q_UNUSED(widget);
|
||||
event->accept();
|
||||
|
||||
if (event->buttons() & Qt::LeftButton && m_element->getPageIndex() != -1)
|
||||
{
|
||||
// Try to add point to the path
|
||||
QPointF pagePoint;
|
||||
PDFInteger pageIndex = getProxy()->getPageUnderPoint(event->pos(), &pagePoint);
|
||||
if (pageIndex == m_element->getPageIndex())
|
||||
{
|
||||
m_element->addPoint(pagePoint);
|
||||
}
|
||||
|
||||
emit getProxy()->repaintNeeded();
|
||||
}
|
||||
}
|
||||
|
||||
void PDFCreatePCElementFreehandCurveTool::setActiveImpl(bool active)
|
||||
{
|
||||
BaseClass::setActiveImpl(active);
|
||||
|
||||
if (!active)
|
||||
{
|
||||
resetTool();
|
||||
}
|
||||
}
|
||||
|
||||
void PDFCreatePCElementFreehandCurveTool::resetTool()
|
||||
{
|
||||
m_element->clear();
|
||||
}
|
||||
|
||||
PDFCreatePCElementTextTool::PDFCreatePCElementTextTool(PDFDrawWidgetProxy* proxy,
|
||||
PDFPageContentScene* scene,
|
||||
QAction* action,
|
||||
QObject* parent) :
|
||||
BaseClass(proxy, scene, action, parent),
|
||||
m_pickTool(nullptr),
|
||||
m_element(nullptr),
|
||||
m_textEditWidget(nullptr)
|
||||
{
|
||||
m_pickTool = new PDFPickTool(proxy, PDFPickTool::Mode::Rectangles, this);
|
||||
m_pickTool->setDrawSelectionRectangle(true);
|
||||
connect(m_pickTool, &PDFPickTool::rectanglePicked, this, &PDFCreatePCElementTextTool::onRectanglePicked);
|
||||
|
||||
QFont font = QGuiApplication::font();
|
||||
font.setPixelSize(16.0);
|
||||
|
||||
m_element = new PDFPageContentElementTextBox();
|
||||
m_element->setBrush(Qt::NoBrush);
|
||||
m_element->setPen(QPen(Qt::SolidLine));
|
||||
m_element->setFont(font);
|
||||
|
||||
m_textEditWidget = new PDFTextEditPseudowidget(PDFFormField::Multiline);
|
||||
}
|
||||
|
||||
PDFCreatePCElementTextTool::~PDFCreatePCElementTextTool()
|
||||
{
|
||||
delete m_textEditWidget;
|
||||
delete m_element;
|
||||
}
|
||||
|
||||
void PDFCreatePCElementTextTool::drawPage(QPainter* painter,
|
||||
PDFInteger pageIndex,
|
||||
const PDFPrecompiledPage* compiledPage,
|
||||
PDFTextLayoutGetter& layoutGetter,
|
||||
const QMatrix& pagePointToDevicePointMatrix,
|
||||
QList<PDFRenderError>& errors) const
|
||||
{
|
||||
BaseClass::drawPage(painter, pageIndex, compiledPage, layoutGetter, pagePointToDevicePointMatrix, errors);
|
||||
|
||||
if (pageIndex != m_element->getPageIndex())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (isEditing())
|
||||
{
|
||||
PDFPainterStateGuard guard(painter);
|
||||
AnnotationDrawParameters parameters;
|
||||
parameters.painter = painter;
|
||||
parameters.boundingRectangle = m_element->getRectangle();
|
||||
parameters.key.first = PDFAppeareanceStreams::Appearance::Normal;
|
||||
parameters.invertColors = getProxy()->getFeatures().testFlag(PDFRenderer::InvertColors);
|
||||
|
||||
painter->setWorldMatrix(pagePointToDevicePointMatrix, true);
|
||||
m_textEditWidget->draw(parameters, true);
|
||||
}
|
||||
}
|
||||
|
||||
const PDFPageContentElement* PDFCreatePCElementTextTool::getElement() const
|
||||
{
|
||||
return m_element;
|
||||
}
|
||||
|
||||
PDFPageContentElement* PDFCreatePCElementTextTool::getElement()
|
||||
{
|
||||
return m_element;
|
||||
}
|
||||
|
||||
void PDFCreatePCElementTextTool::resetTool()
|
||||
{
|
||||
m_textEditWidget->setText(QString());
|
||||
m_element->setText(QString());
|
||||
m_element->setPageIndex(-1);
|
||||
|
||||
if (getTopToolstackTool())
|
||||
{
|
||||
removeTool();
|
||||
}
|
||||
}
|
||||
|
||||
void PDFCreatePCElementTextTool::setActiveImpl(bool active)
|
||||
{
|
||||
BaseClass::setActiveImpl(active);
|
||||
|
||||
if (active)
|
||||
{
|
||||
Q_ASSERT(!getTopToolstackTool());
|
||||
addTool(m_pickTool);
|
||||
}
|
||||
else
|
||||
{
|
||||
resetTool();
|
||||
}
|
||||
|
||||
m_pickTool->setActive(active);
|
||||
}
|
||||
|
||||
void PDFCreatePCElementTextTool::onRectanglePicked(PDFInteger pageIndex, QRectF pageRectangle)
|
||||
{
|
||||
if (pageRectangle.isEmpty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
m_element->setPageIndex(pageIndex);
|
||||
m_element->setRectangle(pageRectangle);
|
||||
|
||||
m_textEditWidget->setAppearance(m_element->getFont(),
|
||||
m_element->getAlignment(),
|
||||
m_element->getRectangle(),
|
||||
std::numeric_limits<int>::max(),
|
||||
m_element->getPen().color());
|
||||
|
||||
removeTool();
|
||||
}
|
||||
|
||||
void PDFCreatePCElementTextTool::finishEditing()
|
||||
{
|
||||
m_element->setText(m_textEditWidget->getText());
|
||||
|
||||
if (!m_element->getText().isEmpty())
|
||||
{
|
||||
m_scene->addElement(m_element->clone());
|
||||
}
|
||||
|
||||
resetTool();
|
||||
setActive(false);
|
||||
}
|
||||
|
||||
std::optional<QPointF> PDFCreatePCElementTextTool::getPagePointUnderMouse(QMouseEvent* event) const
|
||||
{
|
||||
QPointF pagePoint;
|
||||
PDFInteger pageIndex = getProxy()->getPageUnderPoint(event->pos(), &pagePoint);
|
||||
if (pageIndex == m_element->getPageIndex() &&
|
||||
m_element->getRectangle().contains(pagePoint))
|
||||
{
|
||||
return pagePoint;
|
||||
}
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
bool PDFCreatePCElementTextTool::isEditing() const
|
||||
{
|
||||
return isActive() && !getTopToolstackTool();
|
||||
}
|
||||
|
||||
void PDFCreatePCElementTextTool::shortcutOverrideEvent(QWidget* widget, QKeyEvent* event)
|
||||
{
|
||||
Q_UNUSED(widget);
|
||||
|
||||
if (isEditing())
|
||||
{
|
||||
m_textEditWidget->shortcutOverrideEvent(widget, event);
|
||||
}
|
||||
}
|
||||
|
||||
void PDFCreatePCElementTextTool::keyPressEvent(QWidget* widget, QKeyEvent* event)
|
||||
{
|
||||
event->ignore();
|
||||
|
||||
if (!isEditing())
|
||||
{
|
||||
BaseClass::keyPressEvent(widget, event);
|
||||
return;
|
||||
}
|
||||
|
||||
if (event->key() == Qt::Key_Escape)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!m_textEditWidget->isMultiline() && (event->key() == Qt::Key_Enter || event->key() == Qt::Key_Return))
|
||||
{
|
||||
// Commit the editor and create element
|
||||
finishEditing();
|
||||
event->accept();
|
||||
return;
|
||||
}
|
||||
|
||||
m_textEditWidget->keyPressEvent(widget, event);
|
||||
|
||||
if (event->isAccepted())
|
||||
{
|
||||
widget->update();
|
||||
}
|
||||
}
|
||||
|
||||
void PDFCreatePCElementTextTool::mousePressEvent(QWidget* widget, QMouseEvent* event)
|
||||
{
|
||||
if (isEditing())
|
||||
{
|
||||
if (event->button() == Qt::LeftButton)
|
||||
{
|
||||
std::optional<QPointF> pagePoint = getPagePointUnderMouse(event);
|
||||
if (pagePoint)
|
||||
{
|
||||
const int cursorPosition = m_textEditWidget->getCursorPositionFromWidgetPosition(pagePoint.value(), true);
|
||||
m_textEditWidget->setCursorPosition(cursorPosition, event->modifiers() & Qt::ShiftModifier);
|
||||
}
|
||||
else
|
||||
{
|
||||
finishEditing();
|
||||
}
|
||||
|
||||
event->accept();
|
||||
widget->update();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
BaseClass::mousePressEvent(widget, event);
|
||||
}
|
||||
}
|
||||
|
||||
void PDFCreatePCElementTextTool::mouseDoubleClickEvent(QWidget* widget, QMouseEvent* event)
|
||||
{
|
||||
if (isEditing())
|
||||
{
|
||||
if (event->button() == Qt::LeftButton)
|
||||
{
|
||||
std::optional<QPointF> pagePoint = getPagePointUnderMouse(event);
|
||||
if (pagePoint)
|
||||
{
|
||||
const int cursorPosition = m_textEditWidget->getCursorPositionFromWidgetPosition(pagePoint.value(), true);
|
||||
m_textEditWidget->setCursorPosition(cursorPosition, false);
|
||||
m_textEditWidget->setCursorPosition(m_textEditWidget->getCursorWordBackward(), false);
|
||||
m_textEditWidget->setCursorPosition(m_textEditWidget->getCursorWordForward(), true);
|
||||
}
|
||||
else
|
||||
{
|
||||
finishEditing();
|
||||
}
|
||||
|
||||
event->accept();
|
||||
widget->update();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
BaseClass::mousePressEvent(widget, event);
|
||||
}
|
||||
}
|
||||
|
||||
void PDFCreatePCElementTextTool::mouseMoveEvent(QWidget* widget, QMouseEvent* event)
|
||||
{
|
||||
if (isEditing())
|
||||
{
|
||||
std::optional<QPointF> pagePoint = getPagePointUnderMouse(event);
|
||||
if (pagePoint)
|
||||
{
|
||||
// We must test, if left mouse button is pressed while
|
||||
// we are moving the mouse - if yes, then select the text.
|
||||
if (event->buttons() & Qt::LeftButton)
|
||||
{
|
||||
const int cursorPosition = m_textEditWidget->getCursorPositionFromWidgetPosition(pagePoint.value(), true);
|
||||
m_textEditWidget->setCursorPosition(cursorPosition, true);
|
||||
|
||||
event->accept();
|
||||
widget->update();
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
BaseClass::mouseMoveEvent(widget, event);
|
||||
}
|
||||
}
|
||||
|
||||
void PDFCreatePCElementTextTool::wheelEvent(QWidget* widget, QWheelEvent* event)
|
||||
{
|
||||
if (isEditing())
|
||||
{
|
||||
event->ignore();
|
||||
}
|
||||
else
|
||||
{
|
||||
BaseClass::wheelEvent(widget, event);
|
||||
}
|
||||
}
|
||||
|
||||
void PDFCreatePCElementTextTool::setFont(const QFont& font)
|
||||
{
|
||||
BaseClass::setFont(font);
|
||||
m_textEditWidget->setAppearance(font, m_element->getAlignment(), m_element->getRectangle(), std::numeric_limits<int>::max(), m_element->getPen().color());
|
||||
emit getProxy()->repaintNeeded();
|
||||
}
|
||||
|
||||
void PDFCreatePCElementTextTool::setAlignment(Qt::Alignment alignment)
|
||||
{
|
||||
BaseClass::setAlignment(alignment);
|
||||
m_textEditWidget->setAppearance(m_element->getFont(), alignment, m_element->getRectangle(), std::numeric_limits<int>::max(), m_element->getPen().color());
|
||||
emit getProxy()->repaintNeeded();
|
||||
}
|
||||
|
||||
void PDFCreatePCElementTextTool::setPen(const QPen& pen)
|
||||
{
|
||||
BaseClass::setPen(pen);
|
||||
|
||||
QFont font = m_element->getFont();
|
||||
font.setHintingPreference(QFont::PreferNoHinting);
|
||||
if (font.pointSizeF() > 0.0)
|
||||
{
|
||||
font.setPixelSize(qRound(font.pointSizeF()));
|
||||
}
|
||||
|
||||
m_textEditWidget->setAppearance(font, m_element->getAlignment(), m_element->getRectangle(), std::numeric_limits<int>::max(), pen.color());
|
||||
emit getProxy()->repaintNeeded();
|
||||
}
|
||||
|
||||
} // namespace pdf
|
292
Pdf4QtLib/sources/pdfpagecontenteditortools.h
Normal file
292
Pdf4QtLib/sources/pdfpagecontenteditortools.h
Normal file
@@ -0,0 +1,292 @@
|
||||
// Copyright (C) 2022 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 <https://www.gnu.org/licenses/>.
|
||||
|
||||
#ifndef PDFPAGECONTENTEDITORTOOLS_H
|
||||
#define PDFPAGECONTENTEDITORTOOLS_H
|
||||
|
||||
#include "pdfwidgettool.h"
|
||||
|
||||
namespace pdf
|
||||
{
|
||||
|
||||
class PDFPageContentScene;
|
||||
class PDFPageContentElement;
|
||||
class PDFPageContentImageElement;
|
||||
class PDFPageContentElementDot;
|
||||
class PDFPageContentElementLine;
|
||||
class PDFPageContentElementTextBox;
|
||||
class PDFPageContentElementRectangle;
|
||||
class PDFPageContentElementFreehandCurve;
|
||||
class PDFTextEditPseudowidget;
|
||||
|
||||
class PDF4QTLIBSHARED_EXPORT PDFCreatePCElementTool : public PDFWidgetTool
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
PDFCreatePCElementTool(PDFDrawWidgetProxy* proxy,
|
||||
PDFPageContentScene* scene,
|
||||
QAction* action,
|
||||
QObject* parent);
|
||||
|
||||
virtual const PDFPageContentElement* getElement() const = 0;
|
||||
virtual PDFPageContentElement* getElement() = 0;
|
||||
|
||||
virtual void setPen(const QPen& pen);
|
||||
virtual void setBrush(const QBrush& brush);
|
||||
virtual void setFont(const QFont& font);
|
||||
virtual void setAlignment(Qt::Alignment alignment);
|
||||
virtual void setTextAngle(pdf::PDFReal angle);
|
||||
|
||||
protected:
|
||||
static QRectF getRectangleFromPickTool(PDFPickTool* pickTool, const QMatrix& pagePointToDevicePointMatrix);
|
||||
|
||||
PDFPageContentScene* m_scene;
|
||||
};
|
||||
|
||||
/// Tool that creates rectangle element.
|
||||
class PDF4QTLIBSHARED_EXPORT PDFCreatePCElementRectangleTool : public PDFCreatePCElementTool
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
private:
|
||||
using BaseClass = PDFCreatePCElementTool;
|
||||
|
||||
public:
|
||||
explicit PDFCreatePCElementRectangleTool(PDFDrawWidgetProxy* proxy,
|
||||
PDFPageContentScene* scene,
|
||||
QAction* action,
|
||||
bool isRounded,
|
||||
QObject* parent);
|
||||
virtual ~PDFCreatePCElementRectangleTool() override;
|
||||
|
||||
virtual void drawPage(QPainter* painter,
|
||||
PDFInteger pageIndex,
|
||||
const PDFPrecompiledPage* compiledPage,
|
||||
PDFTextLayoutGetter& layoutGetter,
|
||||
const QMatrix& pagePointToDevicePointMatrix,
|
||||
QList<PDFRenderError>& errors) const override;
|
||||
|
||||
virtual const PDFPageContentElement* getElement() const override;
|
||||
virtual PDFPageContentElement* getElement() override;
|
||||
|
||||
private:
|
||||
void onRectanglePicked(pdf::PDFInteger pageIndex, QRectF pageRectangle);
|
||||
|
||||
PDFPickTool* m_pickTool;
|
||||
PDFPageContentElementRectangle* m_element;
|
||||
};
|
||||
|
||||
/// Tool that displays SVG image (or raster image)
|
||||
class PDF4QTLIBSHARED_EXPORT PDFCreatePCElementImageTool : public PDFCreatePCElementTool
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
private:
|
||||
using BaseClass = PDFCreatePCElementTool;
|
||||
|
||||
public:
|
||||
explicit PDFCreatePCElementImageTool(PDFDrawWidgetProxy* proxy,
|
||||
PDFPageContentScene* scene,
|
||||
QAction* action,
|
||||
QByteArray content,
|
||||
bool askSelectImage,
|
||||
QObject* parent);
|
||||
virtual ~PDFCreatePCElementImageTool() override;
|
||||
|
||||
virtual void drawPage(QPainter* painter,
|
||||
PDFInteger pageIndex,
|
||||
const PDFPrecompiledPage* compiledPage,
|
||||
PDFTextLayoutGetter& layoutGetter,
|
||||
const QMatrix& pagePointToDevicePointMatrix,
|
||||
QList<PDFRenderError>& errors) const override;
|
||||
|
||||
virtual const PDFPageContentElement* getElement() const override;
|
||||
virtual PDFPageContentElement* getElement() override;
|
||||
|
||||
protected:
|
||||
virtual void setActiveImpl(bool active) override;
|
||||
|
||||
private:
|
||||
void selectImage();
|
||||
void onRectanglePicked(pdf::PDFInteger pageIndex, QRectF pageRectangle);
|
||||
|
||||
PDFPickTool* m_pickTool;
|
||||
PDFPageContentImageElement* m_element;
|
||||
bool m_askSelectImage;
|
||||
QString m_imageDirectory;
|
||||
};
|
||||
|
||||
/// Tool that creates line element.
|
||||
class PDF4QTLIBSHARED_EXPORT PDFCreatePCElementLineTool : public PDFCreatePCElementTool
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
private:
|
||||
using BaseClass = PDFCreatePCElementTool;
|
||||
|
||||
public:
|
||||
explicit PDFCreatePCElementLineTool(PDFDrawWidgetProxy* proxy,
|
||||
PDFPageContentScene* scene,
|
||||
QAction* action,
|
||||
bool isHorizontal,
|
||||
bool isVertical,
|
||||
QObject* parent);
|
||||
virtual ~PDFCreatePCElementLineTool() override;
|
||||
|
||||
virtual void drawPage(QPainter* painter,
|
||||
PDFInteger pageIndex,
|
||||
const PDFPrecompiledPage* compiledPage,
|
||||
PDFTextLayoutGetter& layoutGetter,
|
||||
const QMatrix& pagePointToDevicePointMatrix,
|
||||
QList<PDFRenderError>& errors) const override;
|
||||
|
||||
virtual const PDFPageContentElement* getElement() const override;
|
||||
virtual PDFPageContentElement* getElement() override;
|
||||
|
||||
private:
|
||||
void clear();
|
||||
void onPointPicked(pdf::PDFInteger pageIndex, QPointF pagePoint);
|
||||
|
||||
PDFPickTool* m_pickTool;
|
||||
PDFPageContentElementLine* m_element;
|
||||
std::optional<QPointF> m_startPoint;
|
||||
};
|
||||
|
||||
/// Tool that creates dot element.
|
||||
class PDF4QTLIBSHARED_EXPORT PDFCreatePCElementDotTool : public PDFCreatePCElementTool
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
private:
|
||||
using BaseClass = PDFCreatePCElementTool;
|
||||
|
||||
public:
|
||||
explicit PDFCreatePCElementDotTool(PDFDrawWidgetProxy* proxy,
|
||||
PDFPageContentScene* scene,
|
||||
QAction* action,
|
||||
QObject* parent);
|
||||
virtual ~PDFCreatePCElementDotTool() override;
|
||||
|
||||
virtual void drawPage(QPainter* painter,
|
||||
PDFInteger pageIndex,
|
||||
const PDFPrecompiledPage* compiledPage,
|
||||
PDFTextLayoutGetter& layoutGetter,
|
||||
const QMatrix& pagePointToDevicePointMatrix,
|
||||
QList<PDFRenderError>& errors) const override;
|
||||
|
||||
virtual const PDFPageContentElement* getElement() const override;
|
||||
virtual PDFPageContentElement* getElement() override;
|
||||
|
||||
private:
|
||||
void onPointPicked(pdf::PDFInteger pageIndex, QPointF pagePoint);
|
||||
|
||||
PDFPickTool* m_pickTool;
|
||||
PDFPageContentElementDot* m_element;
|
||||
};
|
||||
|
||||
/// Tool that creates freehand curve element.
|
||||
class PDF4QTLIBSHARED_EXPORT PDFCreatePCElementFreehandCurveTool : public PDFCreatePCElementTool
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
private:
|
||||
using BaseClass = PDFCreatePCElementTool;
|
||||
|
||||
public:
|
||||
explicit PDFCreatePCElementFreehandCurveTool(PDFDrawWidgetProxy* proxy,
|
||||
PDFPageContentScene* scene,
|
||||
QAction* action,
|
||||
QObject* parent);
|
||||
virtual ~PDFCreatePCElementFreehandCurveTool() override;
|
||||
|
||||
virtual void drawPage(QPainter* painter,
|
||||
PDFInteger pageIndex,
|
||||
const PDFPrecompiledPage* compiledPage,
|
||||
PDFTextLayoutGetter& layoutGetter,
|
||||
const QMatrix& pagePointToDevicePointMatrix,
|
||||
QList<PDFRenderError>& errors) const override;
|
||||
|
||||
virtual const PDFPageContentElement* getElement() const override;
|
||||
virtual PDFPageContentElement* getElement() override;
|
||||
|
||||
virtual void mousePressEvent(QWidget* widget, QMouseEvent* event) override;
|
||||
virtual void mouseReleaseEvent(QWidget* widget, QMouseEvent* event) override;
|
||||
virtual void mouseMoveEvent(QWidget* widget, QMouseEvent* event) override;
|
||||
|
||||
protected:
|
||||
virtual void setActiveImpl(bool active);
|
||||
|
||||
private:
|
||||
void resetTool();
|
||||
|
||||
PDFPageContentElementFreehandCurve* m_element;
|
||||
};
|
||||
|
||||
/// Tool that displays SVG image
|
||||
class PDF4QTLIBSHARED_EXPORT PDFCreatePCElementTextTool : public PDFCreatePCElementTool
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
private:
|
||||
using BaseClass = PDFCreatePCElementTool;
|
||||
|
||||
public:
|
||||
explicit PDFCreatePCElementTextTool(PDFDrawWidgetProxy* proxy,
|
||||
PDFPageContentScene* scene,
|
||||
QAction* action,
|
||||
QObject* parent);
|
||||
virtual ~PDFCreatePCElementTextTool() override;
|
||||
|
||||
virtual void drawPage(QPainter* painter,
|
||||
PDFInteger pageIndex,
|
||||
const PDFPrecompiledPage* compiledPage,
|
||||
PDFTextLayoutGetter& layoutGetter,
|
||||
const QMatrix& pagePointToDevicePointMatrix,
|
||||
QList<PDFRenderError>& errors) const override;
|
||||
|
||||
virtual const PDFPageContentElement* getElement() const override;
|
||||
virtual PDFPageContentElement* getElement() override;
|
||||
|
||||
virtual void setPen(const QPen& pen);
|
||||
virtual void setFont(const QFont& font) override;
|
||||
virtual void setAlignment(Qt::Alignment alignment) override;
|
||||
|
||||
virtual void setActiveImpl(bool active) override;
|
||||
virtual void shortcutOverrideEvent(QWidget* widget, QKeyEvent* event) override;
|
||||
virtual void keyPressEvent(QWidget* widget, QKeyEvent* event) override;
|
||||
virtual void mousePressEvent(QWidget* widget, QMouseEvent* event) override;
|
||||
virtual void mouseDoubleClickEvent(QWidget* widget, QMouseEvent* event) override;
|
||||
virtual void mouseMoveEvent(QWidget* widget, QMouseEvent* event) override;
|
||||
virtual void wheelEvent(QWidget* widget, QWheelEvent* event) override;
|
||||
|
||||
private:
|
||||
void onRectanglePicked(pdf::PDFInteger pageIndex, QRectF pageRectangle);
|
||||
|
||||
void finishEditing();
|
||||
void resetTool();
|
||||
std::optional<QPointF> getPagePointUnderMouse(QMouseEvent* event) const;
|
||||
|
||||
bool isEditing() const;
|
||||
|
||||
PDFPickTool* m_pickTool;
|
||||
PDFPageContentElementTextBox* m_element;
|
||||
PDFTextEditPseudowidget* m_textEditWidget;
|
||||
};
|
||||
|
||||
} // namespace pdf
|
||||
|
||||
#endif // PDFPAGECONTENTEDITORTOOLS_H
|
273
Pdf4QtLib/sources/pdfpagecontenteditorwidget.cpp
Normal file
273
Pdf4QtLib/sources/pdfpagecontenteditorwidget.cpp
Normal file
@@ -0,0 +1,273 @@
|
||||
// Copyright (C) 2022 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 <https://www.gnu.org/licenses/>.
|
||||
|
||||
#include "pdfpagecontenteditorwidget.h"
|
||||
#include "pdfpagecontenteditorstylesettings.h"
|
||||
#include "ui_pdfpagecontenteditorwidget.h"
|
||||
#include "pdfwidgetutils.h"
|
||||
#include "pdfpagecontentelements.h"
|
||||
#include "pdfutils.h"
|
||||
|
||||
#include <QAction>
|
||||
#include <QToolButton>
|
||||
|
||||
namespace pdf
|
||||
{
|
||||
|
||||
PDFPageContentEditorWidget::PDFPageContentEditorWidget(QWidget* parent) :
|
||||
QDockWidget(parent),
|
||||
ui(new Ui::PDFPageContentEditorWidget),
|
||||
m_toolBoxColumnCount(6),
|
||||
m_scene(nullptr),
|
||||
m_selectionChangeEnabled(true),
|
||||
m_updatesEnabled(true)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
|
||||
m_toolButtonIconSize = PDFWidgetUtils::scaleDPI(this, QSize(32, 32));
|
||||
|
||||
for (QToolButton* button : findChildren<QToolButton*>())
|
||||
{
|
||||
button->setIconSize(m_toolButtonIconSize);
|
||||
}
|
||||
|
||||
m_operationMapper.setMapping(ui->alignVertTopButton, static_cast<int>(PDFPageContentElementManipulator::Operation::AlignTop));
|
||||
m_operationMapper.setMapping(ui->alignVertMiddleButton, static_cast<int>(PDFPageContentElementManipulator::Operation::AlignCenterVertically));
|
||||
m_operationMapper.setMapping(ui->alignVertBottomButton, static_cast<int>(PDFPageContentElementManipulator::Operation::AlignBottom));
|
||||
m_operationMapper.setMapping(ui->alignHorLeftButton, static_cast<int>(PDFPageContentElementManipulator::Operation::AlignLeft));
|
||||
m_operationMapper.setMapping(ui->alignHorMiddleButton, static_cast<int>(PDFPageContentElementManipulator::Operation::AlignCenterHorizontally));
|
||||
m_operationMapper.setMapping(ui->alignHorRightButton, static_cast<int>(PDFPageContentElementManipulator::Operation::AlignRight));
|
||||
m_operationMapper.setMapping(ui->setSameWidthButton, static_cast<int>(PDFPageContentElementManipulator::Operation::SetSameWidth));
|
||||
m_operationMapper.setMapping(ui->setSameHeightButton, static_cast<int>(PDFPageContentElementManipulator::Operation::SetSameHeight));
|
||||
m_operationMapper.setMapping(ui->setSameSizeButton, static_cast<int>(PDFPageContentElementManipulator::Operation::SetSameSize));
|
||||
m_operationMapper.setMapping(ui->centerHorizontallyButton, static_cast<int>(PDFPageContentElementManipulator::Operation::CenterHorizontally));
|
||||
m_operationMapper.setMapping(ui->centerVerticallyButton, static_cast<int>(PDFPageContentElementManipulator::Operation::CenterVertically));
|
||||
m_operationMapper.setMapping(ui->centerRectButton, static_cast<int>(PDFPageContentElementManipulator::Operation::CenterHorAndVert));
|
||||
m_operationMapper.setMapping(ui->layoutHorizontallyButton, static_cast<int>(PDFPageContentElementManipulator::Operation::LayoutHorizontally));
|
||||
m_operationMapper.setMapping(ui->layoutVerticallyButton, static_cast<int>(PDFPageContentElementManipulator::Operation::LayoutVertically));
|
||||
m_operationMapper.setMapping(ui->layoutFormButton, static_cast<int>(PDFPageContentElementManipulator::Operation::LayoutForm));
|
||||
m_operationMapper.setMapping(ui->layoutGridButton, static_cast<int>(PDFPageContentElementManipulator::Operation::LayoutGrid));
|
||||
|
||||
connect(ui->alignVertTopButton, &QToolButton::clicked, &m_operationMapper, QOverload<>::of(&QSignalMapper::map));
|
||||
connect(ui->alignVertMiddleButton, &QToolButton::clicked, &m_operationMapper, QOverload<>::of(&QSignalMapper::map));
|
||||
connect(ui->alignVertBottomButton, &QToolButton::clicked, &m_operationMapper, QOverload<>::of(&QSignalMapper::map));
|
||||
connect(ui->alignHorLeftButton, &QToolButton::clicked, &m_operationMapper, QOverload<>::of(&QSignalMapper::map));
|
||||
connect(ui->alignHorMiddleButton, &QToolButton::clicked, &m_operationMapper, QOverload<>::of(&QSignalMapper::map));
|
||||
connect(ui->alignHorRightButton, &QToolButton::clicked, &m_operationMapper, QOverload<>::of(&QSignalMapper::map));
|
||||
connect(ui->setSameWidthButton, &QToolButton::clicked, &m_operationMapper, QOverload<>::of(&QSignalMapper::map));
|
||||
connect(ui->setSameHeightButton, &QToolButton::clicked, &m_operationMapper, QOverload<>::of(&QSignalMapper::map));
|
||||
connect(ui->setSameSizeButton, &QToolButton::clicked, &m_operationMapper, QOverload<>::of(&QSignalMapper::map));
|
||||
connect(ui->centerHorizontallyButton, &QToolButton::clicked, &m_operationMapper, QOverload<>::of(&QSignalMapper::map));
|
||||
connect(ui->centerVerticallyButton, &QToolButton::clicked, &m_operationMapper, QOverload<>::of(&QSignalMapper::map));
|
||||
connect(ui->centerRectButton, &QToolButton::clicked, &m_operationMapper, QOverload<>::of(&QSignalMapper::map));
|
||||
connect(ui->layoutHorizontallyButton, &QToolButton::clicked, &m_operationMapper, QOverload<>::of(&QSignalMapper::map));
|
||||
connect(ui->layoutVerticallyButton, &QToolButton::clicked, &m_operationMapper, QOverload<>::of(&QSignalMapper::map));
|
||||
connect(ui->layoutFormButton, &QToolButton::clicked, &m_operationMapper, QOverload<>::of(&QSignalMapper::map));
|
||||
connect(ui->layoutGridButton, &QToolButton::clicked, &m_operationMapper, QOverload<>::of(&QSignalMapper::map));
|
||||
|
||||
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->appearanceSettingsWidget, &PDFPageContentEditorStyleSettings::penChanged, this, &PDFPageContentEditorWidget::penChanged);
|
||||
connect(ui->appearanceSettingsWidget, &PDFPageContentEditorStyleSettings::brushChanged, this, &PDFPageContentEditorWidget::brushChanged);
|
||||
connect(ui->appearanceSettingsWidget, &PDFPageContentEditorStyleSettings::fontChanged, this, &PDFPageContentEditorWidget::fontChanged);
|
||||
connect(ui->appearanceSettingsWidget, &PDFPageContentEditorStyleSettings::alignmentChanged, this, &PDFPageContentEditorWidget::alignmentChanged);
|
||||
connect(ui->appearanceSettingsWidget, &PDFPageContentEditorStyleSettings::textAngleChanged, this, &PDFPageContentEditorWidget::textAngleChanged);
|
||||
}
|
||||
|
||||
PDFPageContentEditorWidget::~PDFPageContentEditorWidget()
|
||||
{
|
||||
delete ui;
|
||||
}
|
||||
|
||||
void PDFPageContentEditorWidget::addAction(QAction* action)
|
||||
{
|
||||
// First, find position for our action
|
||||
int row = 0;
|
||||
int column = 0;
|
||||
|
||||
while (true)
|
||||
{
|
||||
if (!ui->toolGroupBoxLayout->itemAtPosition(row, column))
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
++column;
|
||||
|
||||
if (column == m_toolBoxColumnCount)
|
||||
{
|
||||
column = 0;
|
||||
++row;
|
||||
}
|
||||
}
|
||||
|
||||
QToolButton* button = new QToolButton(this);
|
||||
button->setIcon(action->icon());
|
||||
button->setText(action->text());
|
||||
button->setToolTip(action->toolTip());
|
||||
button->setCheckable(action->isCheckable());
|
||||
button->setChecked(action->isChecked());
|
||||
button->setEnabled(action->isEnabled());
|
||||
button->setShortcut(action->shortcut());
|
||||
button->setIconSize(m_toolButtonIconSize);
|
||||
m_actionMapper.setMapping(button, action);
|
||||
connect(button, &QToolButton::clicked, &m_actionMapper, QOverload<>::of(&QSignalMapper::map));
|
||||
connect(action, &QAction::changed, this, &PDFPageContentEditorWidget::onActionChanged);
|
||||
|
||||
ui->toolGroupBoxLayout->addWidget(button, row, column, Qt::AlignCenter);
|
||||
}
|
||||
|
||||
QToolButton* PDFPageContentEditorWidget::getToolButtonForOperation(int operation) const
|
||||
{
|
||||
return qobject_cast<QToolButton*>(m_operationMapper.mapping(operation));
|
||||
}
|
||||
|
||||
void PDFPageContentEditorWidget::updateItemsInListWidget()
|
||||
{
|
||||
if (!m_updatesEnabled)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
pdf::PDFTemporaryValueChange guard(&m_updatesEnabled, false);
|
||||
ui->itemsListWidget->setUpdatesEnabled(false);
|
||||
|
||||
if (m_scene)
|
||||
{
|
||||
std::set<PDFInteger> presentElementIds;
|
||||
std::set<PDFInteger> elementIds = m_scene->getElementIds();
|
||||
|
||||
// Remove items which are not here
|
||||
for (int i = 0; i < ui->itemsListWidget->count();)
|
||||
{
|
||||
QListWidgetItem* item = ui->itemsListWidget->item(i);
|
||||
const PDFInteger elementId = item->data(Qt::UserRole).toLongLong();
|
||||
if (!elementIds.count(elementId))
|
||||
{
|
||||
delete ui->itemsListWidget->takeItem(i);
|
||||
}
|
||||
else
|
||||
{
|
||||
presentElementIds.insert(elementId);
|
||||
++i;
|
||||
}
|
||||
}
|
||||
|
||||
// Add items which are here
|
||||
for (PDFInteger elementId : elementIds)
|
||||
{
|
||||
if (presentElementIds.count(elementId))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
const PDFPageContentElement* element = m_scene->getElementById(elementId);
|
||||
Q_ASSERT(element);
|
||||
|
||||
QListWidgetItem* item = new QListWidgetItem(element->getDescription());
|
||||
item->setData(Qt::UserRole, elementId);
|
||||
|
||||
ui->itemsListWidget->addItem(item);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ui->itemsListWidget->clear();
|
||||
}
|
||||
|
||||
ui->itemsListWidget->setUpdatesEnabled(true);
|
||||
}
|
||||
|
||||
void PDFPageContentEditorWidget::onActionTriggerRequest(QObject* actionObject)
|
||||
{
|
||||
QAction* action = qobject_cast<QAction*>(actionObject);
|
||||
Q_ASSERT(action);
|
||||
|
||||
action->trigger();
|
||||
}
|
||||
|
||||
void PDFPageContentEditorWidget::onActionChanged()
|
||||
{
|
||||
QAction* action = qobject_cast<QAction*>(sender());
|
||||
QToolButton* button = qobject_cast<QToolButton*>(m_actionMapper.mapping(action));
|
||||
|
||||
Q_ASSERT(action);
|
||||
Q_ASSERT(button);
|
||||
|
||||
button->setChecked(action->isChecked());
|
||||
button->setEnabled(action->isEnabled());
|
||||
}
|
||||
|
||||
void PDFPageContentEditorWidget::onItemSelectionChanged()
|
||||
{
|
||||
if (m_selectionChangeEnabled)
|
||||
{
|
||||
emit itemSelectionChangedByUser();
|
||||
}
|
||||
}
|
||||
|
||||
PDFPageContentScene* PDFPageContentEditorWidget::scene() const
|
||||
{
|
||||
return m_scene;
|
||||
}
|
||||
|
||||
void PDFPageContentEditorWidget::setScene(PDFPageContentScene* newScene)
|
||||
{
|
||||
if (m_scene != newScene)
|
||||
{
|
||||
m_scene = newScene;
|
||||
updateItemsInListWidget();
|
||||
}
|
||||
}
|
||||
|
||||
std::set<PDFInteger> PDFPageContentEditorWidget::getSelection() const
|
||||
{
|
||||
std::set<PDFInteger> result;
|
||||
|
||||
for (int i = 0; i < ui->itemsListWidget->count(); ++i)
|
||||
{
|
||||
QListWidgetItem* item = ui->itemsListWidget->item(i);
|
||||
if (item->isSelected())
|
||||
{
|
||||
const PDFInteger elementId = item->data(Qt::UserRole).toLongLong();
|
||||
result.insert(elementId);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void PDFPageContentEditorWidget::setSelection(const std::set<PDFInteger>& selection)
|
||||
{
|
||||
pdf::PDFTemporaryValueChange guard(&m_selectionChangeEnabled, false);
|
||||
|
||||
for (int i = 0; i < ui->itemsListWidget->count(); ++i)
|
||||
{
|
||||
QListWidgetItem* item = ui->itemsListWidget->item(i);
|
||||
const PDFInteger elementId = item->data(Qt::UserRole).toLongLong();
|
||||
item->setSelected(selection.count(elementId));
|
||||
}
|
||||
}
|
||||
|
||||
void PDFPageContentEditorWidget::loadStyleFromElement(const PDFPageContentElement* element)
|
||||
{
|
||||
ui->appearanceSettingsWidget->loadFromElement(element, false);
|
||||
}
|
||||
|
||||
} // namespace pdf
|
93
Pdf4QtLib/sources/pdfpagecontenteditorwidget.h
Normal file
93
Pdf4QtLib/sources/pdfpagecontenteditorwidget.h
Normal file
@@ -0,0 +1,93 @@
|
||||
// Copyright (C) 2022 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 <https://www.gnu.org/licenses/>.
|
||||
|
||||
#ifndef PDFPAGECONTENTEDITORWIDGET_H
|
||||
#define PDFPAGECONTENTEDITORWIDGET_H
|
||||
|
||||
#include "pdfglobal.h"
|
||||
|
||||
#include <QDockWidget>
|
||||
#include <QSignalMapper>
|
||||
|
||||
#include <set>
|
||||
|
||||
class QToolButton;
|
||||
|
||||
namespace Ui
|
||||
{
|
||||
class PDFPageContentEditorWidget;
|
||||
}
|
||||
|
||||
namespace pdf
|
||||
{
|
||||
class PDFPageContentScene;
|
||||
class PDFPageContentElement;
|
||||
|
||||
class PDF4QTLIBSHARED_EXPORT PDFPageContentEditorWidget : public QDockWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit PDFPageContentEditorWidget(QWidget* parent);
|
||||
virtual ~PDFPageContentEditorWidget() override;
|
||||
|
||||
/// Adds external action to the tool box
|
||||
void addAction(QAction* action);
|
||||
|
||||
QToolButton* getToolButtonForOperation(int operation) const;
|
||||
|
||||
/// Update items in list widget
|
||||
void updateItemsInListWidget();
|
||||
|
||||
PDFPageContentScene* scene() const;
|
||||
void setScene(PDFPageContentScene* newScene);
|
||||
|
||||
std::set<PDFInteger> getSelection() const;
|
||||
void setSelection(const std::set<PDFInteger>& selection);
|
||||
|
||||
/// Loads style from element, element can be nullptr
|
||||
/// \param element Element
|
||||
void loadStyleFromElement(const PDFPageContentElement* element);
|
||||
|
||||
signals:
|
||||
void operationTriggered(int operation);
|
||||
void itemSelectionChangedByUser();
|
||||
|
||||
void penChanged(const QPen& pen);
|
||||
void brushChanged(const QBrush& brush);
|
||||
void fontChanged(const QFont& font);
|
||||
void alignmentChanged(Qt::Alignment alignment);
|
||||
void textAngleChanged(pdf::PDFReal angle);
|
||||
|
||||
private:
|
||||
void onActionTriggerRequest(QObject* actionObject);
|
||||
void onActionChanged();
|
||||
void onItemSelectionChanged();
|
||||
|
||||
Ui::PDFPageContentEditorWidget* ui;
|
||||
QSignalMapper m_actionMapper;
|
||||
QSignalMapper m_operationMapper;
|
||||
int m_toolBoxColumnCount;
|
||||
QSize m_toolButtonIconSize;
|
||||
PDFPageContentScene* m_scene;
|
||||
bool m_selectionChangeEnabled;
|
||||
bool m_updatesEnabled;
|
||||
};
|
||||
|
||||
} // namespace pdf
|
||||
|
||||
#endif // PDFPAGECONTENTEDITORWIDGET_H
|
215
Pdf4QtLib/sources/pdfpagecontenteditorwidget.ui
Normal file
215
Pdf4QtLib/sources/pdfpagecontenteditorwidget.ui
Normal file
@@ -0,0 +1,215 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>PDFPageContentEditorWidget</class>
|
||||
<widget class="QDockWidget" name="PDFPageContentEditorWidget">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>333</width>
|
||||
<height>607</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="allowedAreas">
|
||||
<set>Qt::LeftDockWidgetArea|Qt::RightDockWidgetArea</set>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Content editor</string>
|
||||
</property>
|
||||
<widget class="QWidget" name="dockWidgetContents">
|
||||
<layout class="QVBoxLayout" name="verticalLayout" stretch="0,0,0,0,1">
|
||||
<item>
|
||||
<widget class="QGroupBox" name="toolGroupBox">
|
||||
<property name="title">
|
||||
<string>Toolbox</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="toolGroupBoxLayout"/>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="adjustBox">
|
||||
<property name="title">
|
||||
<string>Geometry Tools</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="geometryToolsGroupBoxLayout">
|
||||
<item row="0" column="0">
|
||||
<widget class="QToolButton" name="alignVertTopButton">
|
||||
<property name="toolTip">
|
||||
<string>Align to Top</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QToolButton" name="alignVertMiddleButton">
|
||||
<property name="toolTip">
|
||||
<string>Align to Vertical Center</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="2">
|
||||
<widget class="QToolButton" name="alignVertBottomButton">
|
||||
<property name="toolTip">
|
||||
<string>Align to Bottom</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="3">
|
||||
<widget class="QToolButton" name="setSameWidthButton">
|
||||
<property name="toolTip">
|
||||
<string>Set Same Width</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="4">
|
||||
<widget class="QToolButton" name="setSameHeightButton">
|
||||
<property name="toolTip">
|
||||
<string>Set Same Height</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="5">
|
||||
<widget class="QToolButton" name="setSameSizeButton">
|
||||
<property name="toolTip">
|
||||
<string>Set Same Size</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QToolButton" name="alignHorLeftButton">
|
||||
<property name="toolTip">
|
||||
<string>Align to Left</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QToolButton" name="alignHorMiddleButton">
|
||||
<property name="toolTip">
|
||||
<string>Align to Horizontal Center</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="2">
|
||||
<widget class="QToolButton" name="alignHorRightButton">
|
||||
<property name="toolTip">
|
||||
<string>Align to Right</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="3">
|
||||
<widget class="QToolButton" name="centerHorizontallyButton">
|
||||
<property name="toolTip">
|
||||
<string>Center Horizontally</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="4">
|
||||
<widget class="QToolButton" name="centerVerticallyButton">
|
||||
<property name="toolTip">
|
||||
<string>Center Vertically</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="5">
|
||||
<widget class="QToolButton" name="centerRectButton">
|
||||
<property name="toolTip">
|
||||
<string>Center to Page Media Box</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="layoutGroupBox">
|
||||
<property name="title">
|
||||
<string>Layout Tools</string>
|
||||
</property>
|
||||
<layout class="QHBoxLayout" name="layoutToolBoxLayout">
|
||||
<item>
|
||||
<widget class="QToolButton" name="layoutHorizontallyButton">
|
||||
<property name="toolTip">
|
||||
<string>Make Horizontal Layout</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QToolButton" name="layoutVerticallyButton">
|
||||
<property name="toolTip">
|
||||
<string>Make Vertical Layout</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QToolButton" name="layoutFormButton">
|
||||
<property name="toolTip">
|
||||
<string>Make Form Layout</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QToolButton" name="layoutGridButton">
|
||||
<property name="toolTip">
|
||||
<string>Make Grid Layout</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="appearanceGroupBox">
|
||||
<property name="title">
|
||||
<string>Appearance</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="appearanceLayout">
|
||||
<property name="spacing">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="pdf::PDFPageContentEditorStyleSettings" name="appearanceSettingsWidget" native="true"/>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="contentGroupBox">
|
||||
<property name="title">
|
||||
<string>Content</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||
<item>
|
||||
<widget class="QListWidget" name="itemsListWidget">
|
||||
<property name="selectionMode">
|
||||
<enum>QAbstractItemView::ExtendedSelection</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</widget>
|
||||
<customwidgets>
|
||||
<customwidget>
|
||||
<class>pdf::PDFPageContentEditorStyleSettings</class>
|
||||
<extends>QWidget</extends>
|
||||
<header>pdfpagecontenteditorstylesettings.h</header>
|
||||
<container>1</container>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
2535
Pdf4QtLib/sources/pdfpagecontentelements.cpp
Normal file
2535
Pdf4QtLib/sources/pdfpagecontentelements.cpp
Normal file
File diff suppressed because it is too large
Load Diff
626
Pdf4QtLib/sources/pdfpagecontentelements.h
Normal file
626
Pdf4QtLib/sources/pdfpagecontentelements.h
Normal file
@@ -0,0 +1,626 @@
|
||||
// Copyright (C) 2022 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 <https://www.gnu.org/licenses/>.
|
||||
|
||||
#ifndef PDFPAGECONTENTELEMENTS_H
|
||||
#define PDFPAGECONTENTELEMENTS_H
|
||||
|
||||
#include "pdfdocumentdrawinterface.h"
|
||||
|
||||
#include <QPen>
|
||||
#include <QFont>
|
||||
#include <QBrush>
|
||||
#include <QCursor>
|
||||
#include <QPainterPath>
|
||||
|
||||
#include <set>
|
||||
|
||||
class QSvgRenderer;
|
||||
|
||||
namespace pdf
|
||||
{
|
||||
class PDFWidget;
|
||||
class PDFDocument;
|
||||
class PDFPageContentScene;
|
||||
|
||||
class PDF4QTLIBSHARED_EXPORT PDFPageContentElement
|
||||
{
|
||||
public:
|
||||
explicit PDFPageContentElement() = default;
|
||||
virtual ~PDFPageContentElement() = default;
|
||||
|
||||
virtual PDFPageContentElement* clone() const = 0;
|
||||
|
||||
virtual void drawPage(QPainter* painter,
|
||||
PDFInteger pageIndex,
|
||||
const PDFPrecompiledPage* compiledPage,
|
||||
PDFTextLayoutGetter& layoutGetter,
|
||||
const QMatrix& pagePointToDevicePointMatrix,
|
||||
QList<PDFRenderError>& errors) const = 0;
|
||||
|
||||
/// Returns manipulation mode. If manipulation mode is zero, then element
|
||||
/// cannot be manipulated. If it is nonzero, then element can be manipulated
|
||||
/// in some way.
|
||||
/// \param point Point on page
|
||||
/// \param snapPointDistanceTreshold Snap point threshold
|
||||
virtual uint getManipulationMode(const QPointF& point, PDFReal snapPointDistanceThreshold) const = 0;
|
||||
|
||||
/// Performs manipulation of given mode. Mode must be the one returned
|
||||
/// from function getManipulationMode. \sa getManipulationMode
|
||||
/// \param mode Mode
|
||||
/// \param offset Offset
|
||||
virtual void performManipulation(uint mode, const QPointF& offset) = 0;
|
||||
|
||||
/// Returns bounding box of the element
|
||||
virtual QRectF getBoundingBox() const = 0;
|
||||
|
||||
/// Sets size to the elements that supports it. Does
|
||||
/// nothing for elements, which does not support it.
|
||||
virtual void setSize(QSizeF size) = 0;
|
||||
|
||||
/// Returns description of the element
|
||||
virtual QString getDescription() const = 0;
|
||||
|
||||
PDFInteger getPageIndex() const;
|
||||
void setPageIndex(PDFInteger newPageIndex);
|
||||
|
||||
PDFInteger getElementId() const;
|
||||
void setElementId(PDFInteger newElementId);
|
||||
|
||||
/// Returns cursor shape for manipulation mode
|
||||
/// \param mode Manipulation mode
|
||||
static Qt::CursorShape getCursorShapeForManipulationMode(uint mode);
|
||||
|
||||
enum ManipulationModes : uint
|
||||
{
|
||||
None = 0,
|
||||
Translate,
|
||||
Top,
|
||||
Left,
|
||||
Right,
|
||||
Bottom,
|
||||
TopLeft,
|
||||
TopRight,
|
||||
BottomLeft,
|
||||
BottomRight,
|
||||
Pt1,
|
||||
Pt2
|
||||
};
|
||||
|
||||
protected:
|
||||
uint getRectangleManipulationMode(const QRectF& rectangle,
|
||||
const QPointF& point,
|
||||
PDFReal snapPointDistanceThreshold) const;
|
||||
|
||||
void performRectangleManipulation(QRectF& rectangle,
|
||||
uint mode,
|
||||
const QPointF& offset);
|
||||
|
||||
void performRectangleSetSize(QRectF& rectangle, QSizeF size);
|
||||
|
||||
QString formatDescription(const QString& description) const;
|
||||
|
||||
PDFInteger m_elementId = -1;
|
||||
PDFInteger m_pageIndex = -1;
|
||||
};
|
||||
|
||||
class PDF4QTLIBSHARED_EXPORT PDFPageContentStyledElement : public PDFPageContentElement
|
||||
{
|
||||
public:
|
||||
explicit PDFPageContentStyledElement() = default;
|
||||
virtual ~PDFPageContentStyledElement() = default;
|
||||
|
||||
const QPen& getPen() const;
|
||||
void setPen(const QPen& newPen);
|
||||
|
||||
const QBrush& getBrush() const;
|
||||
void setBrush(const QBrush& newBrush);
|
||||
|
||||
protected:
|
||||
QPen m_pen;
|
||||
QBrush m_brush;
|
||||
};
|
||||
|
||||
class PDF4QTLIBSHARED_EXPORT PDFPageContentElementRectangle : public PDFPageContentStyledElement
|
||||
{
|
||||
public:
|
||||
virtual ~PDFPageContentElementRectangle() = default;
|
||||
|
||||
virtual PDFPageContentElementRectangle* clone() const override;
|
||||
|
||||
bool isRounded() const;
|
||||
void setRounded(bool newRounded);
|
||||
|
||||
const QRectF& getRectangle() const;
|
||||
void setRectangle(const QRectF& newRectangle);
|
||||
|
||||
virtual void drawPage(QPainter* painter,
|
||||
PDFInteger pageIndex,
|
||||
const PDFPrecompiledPage* compiledPage,
|
||||
PDFTextLayoutGetter& layoutGetter,
|
||||
const QMatrix& pagePointToDevicePointMatrix,
|
||||
QList<PDFRenderError>& 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);
|
||||
virtual QString getDescription() const override;
|
||||
|
||||
private:
|
||||
bool m_rounded = false;
|
||||
QRectF m_rectangle;
|
||||
};
|
||||
|
||||
class PDF4QTLIBSHARED_EXPORT PDFPageContentElementLine : public PDFPageContentStyledElement
|
||||
{
|
||||
public:
|
||||
virtual ~PDFPageContentElementLine() = default;
|
||||
|
||||
virtual PDFPageContentElementLine* clone() const override;
|
||||
|
||||
enum class LineGeometry
|
||||
{
|
||||
General,
|
||||
Horizontal,
|
||||
Vertical
|
||||
};
|
||||
|
||||
virtual void drawPage(QPainter* painter,
|
||||
PDFInteger pageIndex,
|
||||
const PDFPrecompiledPage* compiledPage,
|
||||
PDFTextLayoutGetter& layoutGetter,
|
||||
const QMatrix& pagePointToDevicePointMatrix,
|
||||
QList<PDFRenderError>& 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);
|
||||
virtual QString getDescription() const override;
|
||||
|
||||
LineGeometry getGeometry() const;
|
||||
void setGeometry(LineGeometry newGeometry);
|
||||
|
||||
const QLineF& getLine() const;
|
||||
void setLine(const QLineF& newLine);
|
||||
|
||||
private:
|
||||
LineGeometry m_geometry = LineGeometry::General;
|
||||
QLineF m_line;
|
||||
};
|
||||
|
||||
class PDF4QTLIBSHARED_EXPORT PDFPageContentElementDot : public PDFPageContentStyledElement
|
||||
{
|
||||
public:
|
||||
virtual ~PDFPageContentElementDot() = default;
|
||||
|
||||
virtual PDFPageContentElementDot* clone() const override;
|
||||
|
||||
virtual void drawPage(QPainter* painter,
|
||||
PDFInteger pageIndex,
|
||||
const PDFPrecompiledPage* compiledPage,
|
||||
PDFTextLayoutGetter& layoutGetter,
|
||||
const QMatrix& pagePointToDevicePointMatrix,
|
||||
QList<PDFRenderError>& 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);
|
||||
virtual QString getDescription() const override;
|
||||
|
||||
QPointF getPoint() const;
|
||||
void setPoint(QPointF newPoint);
|
||||
|
||||
private:
|
||||
QPointF m_point;
|
||||
};
|
||||
|
||||
class PDF4QTLIBSHARED_EXPORT PDFPageContentElementFreehandCurve : public PDFPageContentStyledElement
|
||||
{
|
||||
public:
|
||||
virtual ~PDFPageContentElementFreehandCurve() = default;
|
||||
|
||||
virtual PDFPageContentElementFreehandCurve* clone() const override;
|
||||
|
||||
virtual void drawPage(QPainter* painter,
|
||||
PDFInteger pageIndex,
|
||||
const PDFPrecompiledPage* compiledPage,
|
||||
PDFTextLayoutGetter& layoutGetter,
|
||||
const QMatrix& pagePointToDevicePointMatrix,
|
||||
QList<PDFRenderError>& 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);
|
||||
virtual QString getDescription() const override;
|
||||
|
||||
QPainterPath getCurve() const;
|
||||
void setCurve(QPainterPath newCurve);
|
||||
|
||||
bool isEmpty() const { return m_curve.isEmpty(); }
|
||||
void addStartPoint(const QPointF& point);
|
||||
void addPoint(const QPointF& point);
|
||||
void clear();
|
||||
|
||||
private:
|
||||
QPainterPath m_curve;
|
||||
};
|
||||
|
||||
class PDF4QTLIBSHARED_EXPORT PDFPageContentImageElement : public PDFPageContentElement
|
||||
{
|
||||
public:
|
||||
PDFPageContentImageElement();
|
||||
virtual ~PDFPageContentImageElement();
|
||||
|
||||
virtual PDFPageContentImageElement* clone() const override;
|
||||
|
||||
virtual void drawPage(QPainter* painter,
|
||||
PDFInteger pageIndex,
|
||||
const PDFPrecompiledPage* compiledPage,
|
||||
PDFTextLayoutGetter& layoutGetter,
|
||||
const QMatrix& pagePointToDevicePointMatrix,
|
||||
QList<PDFRenderError>& 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);
|
||||
virtual QString getDescription() const override;
|
||||
|
||||
const QByteArray& getContent() const;
|
||||
void setContent(const QByteArray& newContent);
|
||||
|
||||
const QRectF& getRectangle() const;
|
||||
void setRectangle(const QRectF& newRectangle);
|
||||
|
||||
private:
|
||||
QRectF m_rectangle;
|
||||
QByteArray m_content;
|
||||
QImage m_image;
|
||||
std::unique_ptr<QSvgRenderer> m_renderer;
|
||||
};
|
||||
|
||||
class PDF4QTLIBSHARED_EXPORT PDFPageContentElementTextBox : public PDFPageContentStyledElement
|
||||
{
|
||||
public:
|
||||
virtual ~PDFPageContentElementTextBox() = default;
|
||||
|
||||
virtual PDFPageContentElementTextBox* clone() const override;
|
||||
|
||||
const QRectF& getRectangle() const { return m_rectangle; }
|
||||
void setRectangle(const QRectF& newRectangle) { m_rectangle = newRectangle; }
|
||||
|
||||
virtual void drawPage(QPainter* painter,
|
||||
PDFInteger pageIndex,
|
||||
const PDFPrecompiledPage* compiledPage,
|
||||
PDFTextLayoutGetter& layoutGetter,
|
||||
const QMatrix& pagePointToDevicePointMatrix,
|
||||
QList<PDFRenderError>& 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);
|
||||
virtual QString getDescription() const override;
|
||||
|
||||
const QString& getText() const;
|
||||
void setText(const QString& newText);
|
||||
|
||||
const QFont& getFont() const;
|
||||
void setFont(const QFont& newFont);
|
||||
|
||||
PDFReal getAngle() const;
|
||||
void setAngle(PDFReal newAngle);
|
||||
|
||||
const Qt::Alignment& getAlignment() const;
|
||||
void setAlignment(const Qt::Alignment& newAlignment);
|
||||
|
||||
private:
|
||||
QString m_text;
|
||||
QRectF m_rectangle;
|
||||
QFont m_font;
|
||||
PDFReal m_angle = 0.0;
|
||||
Qt::Alignment m_alignment = Qt::AlignCenter;
|
||||
};
|
||||
|
||||
class PDF4QTLIBSHARED_EXPORT PDFPageContentElementManipulator : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
PDFPageContentElementManipulator(PDFPageContentScene* scene, QObject* parent);
|
||||
|
||||
enum class Operation
|
||||
{
|
||||
AlignTop,
|
||||
AlignCenterVertically,
|
||||
AlignBottom,
|
||||
AlignLeft,
|
||||
AlignCenterHorizontally,
|
||||
AlignRight,
|
||||
SetSameHeight,
|
||||
SetSameWidth,
|
||||
SetSameSize,
|
||||
CenterHorizontally,
|
||||
CenterVertically,
|
||||
CenterHorAndVert,
|
||||
LayoutVertically,
|
||||
LayoutHorizontally,
|
||||
LayoutForm,
|
||||
LayoutGrid
|
||||
};
|
||||
|
||||
enum SelectionMode
|
||||
{
|
||||
NoUpdate = 0x0000,
|
||||
Clear = 0x0001, ///< Clears current selection
|
||||
Select = 0x0002, ///< Selects item
|
||||
Deselect = 0x0004, ///< Deselects item
|
||||
Toggle = 0x0008, ///< Toggles selection of the item
|
||||
};
|
||||
Q_DECLARE_FLAGS(SelectionModes, SelectionMode)
|
||||
|
||||
/// Returns true, if element with given id is selected
|
||||
/// \param id Element id
|
||||
bool isSelected(PDFInteger id) const;
|
||||
|
||||
/// Returns true, if all elements are selected
|
||||
/// \param ids Element ids
|
||||
bool isAllSelected(const std::set<PDFInteger>& elementIds) const;
|
||||
|
||||
/// Returns true, if selection is empty
|
||||
bool isSelectionEmpty() const { return m_selection.empty(); }
|
||||
|
||||
/// Clear all selection, stop manipulation
|
||||
void reset();
|
||||
|
||||
void update(PDFInteger id, SelectionModes modes);
|
||||
void update(const std::set<PDFInteger>& ids, SelectionModes modes);
|
||||
void select(PDFInteger id);
|
||||
void select(const std::set<PDFInteger>& ids);
|
||||
void selectNew(PDFInteger id);
|
||||
void selectNew(const std::set<PDFInteger>& ids);
|
||||
void deselect(PDFInteger id);
|
||||
void deselect(const std::set<PDFInteger>& ids);
|
||||
void selectAll();
|
||||
void deselectAll();
|
||||
|
||||
bool isManipulationAllowed(PDFInteger pageIndex) const;
|
||||
bool isManipulationInProgress() const { return m_isManipulationInProgress; }
|
||||
|
||||
void performOperation(Operation operation);
|
||||
void performDeleteSelection();
|
||||
|
||||
void startManipulation(PDFInteger pageIndex,
|
||||
const QPointF& startPoint,
|
||||
const QPointF& currentPoint,
|
||||
PDFReal snapPointDistanceThreshold);
|
||||
|
||||
void updateManipulation(PDFInteger pageIndex,
|
||||
const QPointF& startPoint,
|
||||
const QPointF& currentPoint);
|
||||
|
||||
void finishManipulation(PDFInteger pageIndex,
|
||||
const QPointF& startPoint,
|
||||
const QPointF& currentPoint,
|
||||
bool createCopy);
|
||||
|
||||
void cancelManipulation();
|
||||
|
||||
void drawPage(QPainter* painter,
|
||||
PDFInteger pageIndex,
|
||||
const PDFPrecompiledPage* compiledPage,
|
||||
PDFTextLayoutGetter& layoutGetter,
|
||||
const QMatrix& pagePointToDevicePointMatrix,
|
||||
QList<PDFRenderError>& errors) const;
|
||||
|
||||
/// Returns bounding box of whole selection
|
||||
QRectF getSelectionBoundingRect() const;
|
||||
|
||||
/// Returns page rectangle for the page
|
||||
QRectF getPageMediaBox(PDFInteger pageIndex) const;
|
||||
|
||||
signals:
|
||||
void selectionChanged();
|
||||
void stateChanged();
|
||||
|
||||
private:
|
||||
void eraseSelectedElementById(PDFInteger id);
|
||||
|
||||
PDFPageContentScene* m_scene;
|
||||
std::vector<PDFInteger> m_selection;
|
||||
bool m_isManipulationInProgress;
|
||||
std::vector<std::unique_ptr<PDFPageContentElement>> m_manipulatedElements;
|
||||
std::map<PDFInteger, uint> m_manipulationModes;
|
||||
QPointF m_lastUpdatedPoint;
|
||||
};
|
||||
|
||||
class PDF4QTLIBSHARED_EXPORT PDFPageContentScene : public QObject,
|
||||
public IDocumentDrawInterface,
|
||||
public IDrawWidgetInputInterface
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit PDFPageContentScene(QObject* parent);
|
||||
virtual ~PDFPageContentScene();
|
||||
|
||||
static constexpr PDFInteger INVALID_ELEMENT_ID = 0;
|
||||
|
||||
/// Add new element to page content scene, scene
|
||||
/// takes ownership over the element.
|
||||
/// \param element Element
|
||||
void addElement(PDFPageContentElement* element);
|
||||
|
||||
/// Replaces element in the page content scene, scene
|
||||
/// takes ownership over the element.
|
||||
/// \param element Element
|
||||
void replaceElement(PDFPageContentElement* element);
|
||||
|
||||
/// Returns element by its id (identifier number)
|
||||
/// \param id Element id
|
||||
PDFPageContentElement* getElementById(PDFInteger id) const;
|
||||
|
||||
/// Clear whole scene - remove all page content elements
|
||||
void clear();
|
||||
|
||||
/// Returns true, if scene is empty
|
||||
bool isEmpty() const { return m_elements.empty(); }
|
||||
|
||||
bool isActive() const;
|
||||
void setActive(bool newIsActive);
|
||||
|
||||
/// Returns set of all element ids
|
||||
std::set<PDFInteger> getElementIds() const;
|
||||
|
||||
/// Returns set of selected element ids
|
||||
std::set<PDFInteger> getSelectedElementIds() const;
|
||||
|
||||
/// Returns set of involved pages
|
||||
std::set<PDFInteger> getPageIndices() const;
|
||||
|
||||
/// Returns bounding box of elements on page
|
||||
QRectF getBoundingBox(PDFInteger pageIndex) const;
|
||||
|
||||
/// Set selected items
|
||||
void setSelectedElementIds(const std::set<PDFInteger>& selectedElementIds);
|
||||
|
||||
/// Removes elements specified in selection
|
||||
/// \param selection Items to be removed
|
||||
void removeElementsById(const std::vector<PDFInteger>& selection);
|
||||
|
||||
/// Performs manipulation of selected elements with manipulator
|
||||
void performOperation(int operation);
|
||||
|
||||
/// Returns document (or nullptr)
|
||||
const PDFDocument* getDocument() const;
|
||||
|
||||
// IDrawWidgetInputInterface interface
|
||||
public:
|
||||
virtual void shortcutOverrideEvent(QWidget* widget, QKeyEvent* event) override;
|
||||
virtual void keyPressEvent(QWidget* widget, QKeyEvent* event) override;
|
||||
virtual void keyReleaseEvent(QWidget* widget, QKeyEvent* event) override;
|
||||
virtual void mousePressEvent(QWidget* widget, QMouseEvent* event) override;
|
||||
virtual void mouseDoubleClickEvent(QWidget* widget, QMouseEvent* event) override;
|
||||
virtual void mouseReleaseEvent(QWidget* widget, QMouseEvent* event) override;
|
||||
virtual void mouseMoveEvent(QWidget* widget, QMouseEvent* event) override;
|
||||
virtual void wheelEvent(QWidget* widget, QWheelEvent* event) override;
|
||||
virtual QString getTooltip() const override;
|
||||
virtual const std::optional<QCursor>& getCursor() const override;
|
||||
virtual int getInputPriority() const override;
|
||||
|
||||
virtual void drawPage(QPainter* painter,
|
||||
PDFInteger pageIndex,
|
||||
const PDFPrecompiledPage* compiledPage,
|
||||
PDFTextLayoutGetter& layoutGetter,
|
||||
const QMatrix& pagePointToDevicePointMatrix,
|
||||
QList<PDFRenderError>& errors) const override;
|
||||
|
||||
PDFWidget* widget() const;
|
||||
void setWidget(PDFWidget* newWidget);
|
||||
|
||||
void drawElements(QPainter* painter,
|
||||
PDFInteger pageIndex,
|
||||
PDFTextLayoutGetter& layoutGetter,
|
||||
const QMatrix& pagePointToDevicePointMatrix,
|
||||
const PDFPrecompiledPage* compiledPage,
|
||||
QList<PDFRenderError>& errors) const;
|
||||
|
||||
signals:
|
||||
/// This signal is emitted when scene has changed (including graphics)
|
||||
void sceneChanged(bool graphicsOnly);
|
||||
|
||||
void selectionChanged();
|
||||
|
||||
/// Request to edit the elements
|
||||
void editElementRequest(const std::set<PDFInteger>& elements);
|
||||
|
||||
private:
|
||||
|
||||
struct MouseEventInfo
|
||||
{
|
||||
std::set<PDFInteger> hoveredElementIds;
|
||||
QPoint widgetMouseStartPos;
|
||||
QPoint widgetMouseCurrentPos;
|
||||
QElapsedTimer timer;
|
||||
PDFInteger pageIndex = -1;
|
||||
QPointF pagePos;
|
||||
|
||||
bool isValid() const { return !hoveredElementIds.empty(); }
|
||||
};
|
||||
|
||||
MouseEventInfo getMouseEventInfo(QWidget* widget, QPoint point);
|
||||
|
||||
struct MouseGrabInfo
|
||||
{
|
||||
MouseEventInfo info;
|
||||
int mouseGrabNesting = 0;
|
||||
|
||||
bool isMouseGrabbed() const { return mouseGrabNesting > 0; }
|
||||
};
|
||||
|
||||
PDFReal getSnapPointDistanceThreshold() const;
|
||||
|
||||
bool isMouseGrabbed() const { return m_mouseGrabInfo.isMouseGrabbed(); }
|
||||
|
||||
/// Grabs mouse input, if mouse is already grabbed, or if event
|
||||
/// is accepted.
|
||||
/// \param info Mouse event info
|
||||
/// \param event Mouse event
|
||||
void grabMouse(const MouseEventInfo& info, QMouseEvent* event);
|
||||
|
||||
/// Release mouse input
|
||||
/// \param info Mouse event info
|
||||
/// \param event Mouse event
|
||||
void ungrabMouse(const MouseEventInfo& info, QMouseEvent* event);
|
||||
|
||||
/// Updates mouse cursor
|
||||
/// \param info Mouse event info
|
||||
/// \param snapPointDistanceTreshold Snap point threshold
|
||||
void updateMouseCursor(const MouseEventInfo& info, PDFReal snapPointDistanceThreshold);
|
||||
|
||||
/// Reaction on selection changed
|
||||
void onSelectionChanged();
|
||||
|
||||
PDFInteger m_firstFreeId;
|
||||
bool m_isActive;
|
||||
PDFWidget* m_widget;
|
||||
std::vector<std::unique_ptr<PDFPageContentElement>> m_elements;
|
||||
std::optional<QCursor> m_cursor;
|
||||
PDFPageContentElementManipulator m_manipulator;
|
||||
MouseGrabInfo m_mouseGrabInfo;
|
||||
};
|
||||
|
||||
} // namespace pdf
|
||||
|
||||
Q_DECLARE_OPERATORS_FOR_FLAGS(pdf::PDFPageContentElementManipulator::SelectionModes)
|
||||
|
||||
#endif // PDFPAGECONTENTELEMENTS_H
|
@@ -348,6 +348,15 @@ void PDFSignatureVerificationResult::addCertificateQualifiedStatementNotVerified
|
||||
}
|
||||
}
|
||||
|
||||
void PDFSignatureVerificationResult::addCertificateUnableToGetCRLWarning()
|
||||
{
|
||||
if (!m_flags.testFlag(Warning_Certificate_UnableToGetCRL))
|
||||
{
|
||||
m_flags.setFlag(Warning_Certificate_UnableToGetCRL);
|
||||
m_warnings << PDFTranslationContext::tr("Unable to get CRL.");
|
||||
}
|
||||
}
|
||||
|
||||
void PDFSignatureVerificationResult::setSignatureFieldQualifiedName(const QString& signatureFieldQualifiedName)
|
||||
{
|
||||
m_signatureFieldQualifiedName = signatureFieldQualifiedName;
|
||||
@@ -977,6 +986,15 @@ int PDFSignatureHandler_ETSI_base::verifyCallback(int ok, X509_STORE_CTX* contex
|
||||
return 1;
|
||||
}
|
||||
|
||||
case X509_V_ERR_UNABLE_TO_GET_CRL:
|
||||
{
|
||||
// We will treat this as only warning. It means that
|
||||
// CRL cannot be downloaded or other error occured.
|
||||
s_ETSI_currentResult->addCertificateUnableToGetCRLWarning();
|
||||
X509_STORE_CTX_set_error(context, X509_V_OK);
|
||||
return 1;
|
||||
}
|
||||
|
||||
case X509_V_ERR_UNHANDLED_CRITICAL_EXTENSION:
|
||||
{
|
||||
// We must handle all critical extensions manually
|
||||
|
@@ -319,6 +319,7 @@ public:
|
||||
Warning_Signature_NotCoveredBytes = 0x00200000, ///< Some bytes in source data are not covered by signature
|
||||
Warning_Certificate_CRLValidityTimeExpired = 0x00400000, ///< Certificate revocation list was not checked, because it's validity expired
|
||||
Warning_Certificate_QualifiedStatement = 0x00800000, ///< Qualified certificate statement not verified
|
||||
Warning_Certificate_UnableToGetCRL = 0x01000000, ///< Unable to get CRL
|
||||
|
||||
Error_Certificates_Mask = Error_Certificate_Invalid | Error_Certificate_NoSignatures | Error_Certificate_Missing | Error_Certificate_Generic |
|
||||
Error_Certificate_Expired | Error_Certificate_SelfSigned | Error_Certificate_SelfSignedChain | Error_Certificate_TrustedNotFound |
|
||||
@@ -327,7 +328,7 @@ public:
|
||||
Error_Signatures_Mask = Error_Signature_Invalid | Error_Signature_SourceCertificateMissing | Error_Signature_NoSignaturesFound |
|
||||
Error_Signature_DigestFailure | Error_Signature_DataOther | Error_Signature_DataCoveredBySignatureMissing,
|
||||
|
||||
Warning_Certificates_Mask = Warning_Certificate_CRLValidityTimeExpired | Warning_Certificate_QualifiedStatement,
|
||||
Warning_Certificates_Mask = Warning_Certificate_CRLValidityTimeExpired | Warning_Certificate_QualifiedStatement | Warning_Certificate_UnableToGetCRL,
|
||||
Warning_Signatures_Mask = Warning_Signature_NotCoveredBytes,
|
||||
|
||||
Warnings_Mask = Warning_Certificates_Mask | Warning_Signatures_Mask
|
||||
@@ -361,6 +362,7 @@ public:
|
||||
void addSignatureNotCoveredBytesWarning(PDFInteger count);
|
||||
void addCertificateCRLValidityTimeExpiredWarning();
|
||||
void addCertificateQualifiedStatementNotVerifiedWarning();
|
||||
void addCertificateUnableToGetCRLWarning();
|
||||
|
||||
bool isValid() const { return hasFlag(OK); }
|
||||
bool isCertificateValid() const { return hasFlag(Certificate_OK); }
|
||||
|
@@ -383,10 +383,9 @@ QByteArray PDFFlateDecodeFilter::apply(const QByteArray& data,
|
||||
return predictor.apply(uncompress(data));
|
||||
}
|
||||
|
||||
QByteArray PDFFlateDecodeFilter::recompress(const QByteArray& data)
|
||||
QByteArray PDFFlateDecodeFilter::compress(const QByteArray& decompressedData)
|
||||
{
|
||||
QByteArray result;
|
||||
QByteArray decompressedData = uncompress(data);
|
||||
|
||||
z_stream stream = { };
|
||||
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);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
QByteArray PDFFlateDecodeFilter::recompress(const QByteArray& data)
|
||||
{
|
||||
QByteArray decompressedData = uncompress(data);
|
||||
return compress(decompressedData);
|
||||
}
|
||||
|
||||
PDFInteger PDFFlateDecodeFilter::getStreamDataLength(const QByteArray& data, PDFInteger offset) const
|
||||
{
|
||||
if (offset < 0 || offset >= data.size())
|
||||
|
@@ -215,6 +215,10 @@ public:
|
||||
|
||||
/// Recompresses data. So, first, data are decompressed, and then
|
||||
/// 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
|
||||
static QByteArray recompress(const QByteArray& data);
|
||||
|
||||
|
949
Pdf4QtLib/sources/pdftexteditpseudowidget.cpp
Normal file
949
Pdf4QtLib/sources/pdftexteditpseudowidget.cpp
Normal file
@@ -0,0 +1,949 @@
|
||||
// Copyright (C) 2022 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 <https://www.gnu.org/licenses/>.
|
||||
|
||||
#include "pdftexteditpseudowidget.h"
|
||||
#include "pdfpainterutils.h"
|
||||
|
||||
#include <QStyle>
|
||||
#include <QKeyEvent>
|
||||
#include <QMouseEvent>
|
||||
#include <QApplication>
|
||||
#include <QClipboard>
|
||||
|
||||
namespace pdf
|
||||
{
|
||||
|
||||
PDFTextEditPseudowidget::PDFTextEditPseudowidget(PDFFormField::FieldFlags flags) :
|
||||
m_flags(flags),
|
||||
m_selectionStart(0),
|
||||
m_selectionEnd(0),
|
||||
m_positionCursor(0),
|
||||
m_maxTextLength(0)
|
||||
{
|
||||
m_textLayout.setCacheEnabled(true);
|
||||
m_passwordReplacementCharacter = QApplication::style()->styleHint(QStyle::SH_LineEdit_PasswordCharacter);
|
||||
}
|
||||
|
||||
void PDFTextEditPseudowidget::shortcutOverrideEvent(QWidget* widget, QKeyEvent* event)
|
||||
{
|
||||
Q_UNUSED(widget);
|
||||
constexpr QKeySequence::StandardKey acceptedKeys[] = { QKeySequence::Delete, QKeySequence::Cut, QKeySequence::Copy, QKeySequence::Paste,
|
||||
QKeySequence::SelectAll, QKeySequence::MoveToNextChar, QKeySequence::MoveToPreviousChar,
|
||||
QKeySequence::MoveToNextWord, QKeySequence::MoveToPreviousWord, QKeySequence::MoveToNextLine,
|
||||
QKeySequence::MoveToPreviousLine, QKeySequence::MoveToStartOfLine, QKeySequence::MoveToEndOfLine,
|
||||
QKeySequence::MoveToStartOfBlock, QKeySequence::MoveToEndOfBlock, QKeySequence::MoveToStartOfDocument,
|
||||
QKeySequence::MoveToEndOfDocument, QKeySequence::SelectNextChar, QKeySequence::SelectPreviousChar,
|
||||
QKeySequence::SelectNextWord, QKeySequence::SelectPreviousWord, QKeySequence::SelectNextLine,
|
||||
QKeySequence::SelectPreviousLine, QKeySequence::SelectStartOfLine, QKeySequence::SelectEndOfLine,
|
||||
QKeySequence::SelectStartOfBlock, QKeySequence::SelectEndOfBlock, QKeySequence::SelectStartOfDocument,
|
||||
QKeySequence::SelectEndOfDocument, QKeySequence::DeleteStartOfWord, QKeySequence::DeleteEndOfWord,
|
||||
QKeySequence::DeleteEndOfLine, QKeySequence::Deselect, QKeySequence::DeleteCompleteLine, QKeySequence::Backspace };
|
||||
|
||||
if (std::any_of(std::begin(acceptedKeys), std::end(acceptedKeys), [event](QKeySequence::StandardKey standardKey) { return event == standardKey; }))
|
||||
{
|
||||
event->accept();
|
||||
return;
|
||||
}
|
||||
|
||||
switch (event->key())
|
||||
{
|
||||
case Qt::Key_Direction_L:
|
||||
case Qt::Key_Direction_R:
|
||||
case Qt::Key_Up:
|
||||
case Qt::Key_Down:
|
||||
case Qt::Key_Left:
|
||||
case Qt::Key_Right:
|
||||
event->accept();
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (!event->text().isEmpty())
|
||||
{
|
||||
event->accept();
|
||||
for (const QChar& character : event->text())
|
||||
{
|
||||
if (!character.isPrint())
|
||||
{
|
||||
event->ignore();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PDFTextEditPseudowidget::keyPressEvent(QWidget* widget, QKeyEvent* event)
|
||||
{
|
||||
Q_UNUSED(widget);
|
||||
|
||||
/*
|
||||
We will support following key sequences:
|
||||
Delete
|
||||
Cut,
|
||||
Copy,
|
||||
Paste,
|
||||
SelectAll,
|
||||
MoveToNextChar,
|
||||
MoveToPreviousChar,
|
||||
MoveToNextWord,
|
||||
MoveToPreviousWord,
|
||||
MoveToNextLine,
|
||||
MoveToPreviousLine,
|
||||
MoveToStartOfLine,
|
||||
MoveToEndOfLine,
|
||||
MoveToStartOfBlock,
|
||||
MoveToEndOfBlock,
|
||||
MoveToStartOfDocument,
|
||||
MoveToEndOfDocument,
|
||||
SelectNextChar,
|
||||
SelectPreviousChar,
|
||||
SelectNextWord,
|
||||
SelectPreviousWord,
|
||||
SelectNextLine,
|
||||
SelectPreviousLine,
|
||||
SelectStartOfLine,
|
||||
SelectEndOfLine,
|
||||
SelectStartOfBlock,
|
||||
SelectEndOfBlock,
|
||||
SelectStartOfDocument,
|
||||
SelectEndOfDocument,
|
||||
DeleteStartOfWord,
|
||||
DeleteEndOfWord,
|
||||
DeleteEndOfLine,
|
||||
Deselect,
|
||||
DeleteCompleteLine,
|
||||
Backspace,
|
||||
* */
|
||||
|
||||
event->accept();
|
||||
|
||||
if (event == QKeySequence::Delete)
|
||||
{
|
||||
performDelete();
|
||||
}
|
||||
else if (event == QKeySequence::Cut)
|
||||
{
|
||||
performCut();
|
||||
}
|
||||
else if (event == QKeySequence::Copy)
|
||||
{
|
||||
performCopy();
|
||||
}
|
||||
else if (event == QKeySequence::Paste)
|
||||
{
|
||||
performPaste();
|
||||
}
|
||||
else if (event == QKeySequence::SelectAll)
|
||||
{
|
||||
setSelection(0, getTextLength());
|
||||
}
|
||||
else if (event == QKeySequence::MoveToNextChar)
|
||||
{
|
||||
setCursorPosition(getCursorCharacterForward(), false);
|
||||
}
|
||||
else if (event == QKeySequence::MoveToPreviousChar)
|
||||
{
|
||||
setCursorPosition(getCursorCharacterBackward(), false);
|
||||
}
|
||||
else if (event == QKeySequence::MoveToNextWord)
|
||||
{
|
||||
setCursorPosition(getCursorWordForward(), false);
|
||||
}
|
||||
else if (event == QKeySequence::MoveToPreviousWord)
|
||||
{
|
||||
setCursorPosition(getCursorWordBackward(), false);
|
||||
}
|
||||
else if (event == QKeySequence::MoveToNextLine)
|
||||
{
|
||||
setCursorPosition(getCursorLineDown(), false);
|
||||
}
|
||||
else if (event == QKeySequence::MoveToPreviousLine)
|
||||
{
|
||||
setCursorPosition(getCursorLineUp(), false);
|
||||
}
|
||||
else if (event == QKeySequence::MoveToStartOfLine || event == QKeySequence::MoveToStartOfBlock)
|
||||
{
|
||||
setCursorPosition(getCursorLineStart(), false);
|
||||
}
|
||||
else if (event == QKeySequence::MoveToEndOfLine || event == QKeySequence::MoveToEndOfBlock)
|
||||
{
|
||||
setCursorPosition(getCursorLineEnd(), false);
|
||||
}
|
||||
else if (event == QKeySequence::MoveToStartOfDocument)
|
||||
{
|
||||
setCursorPosition(getCursorDocumentStart(), false);
|
||||
}
|
||||
else if (event == QKeySequence::MoveToEndOfDocument)
|
||||
{
|
||||
setCursorPosition(getCursorDocumentEnd(), false);
|
||||
}
|
||||
else if (event == QKeySequence::SelectNextChar)
|
||||
{
|
||||
setCursorPosition(getCursorCharacterForward(), true);
|
||||
}
|
||||
else if (event == QKeySequence::SelectPreviousChar)
|
||||
{
|
||||
setCursorPosition(getCursorCharacterBackward(), true);
|
||||
}
|
||||
else if (event == QKeySequence::SelectNextWord)
|
||||
{
|
||||
setCursorPosition(getCursorWordForward(), true);
|
||||
}
|
||||
else if (event == QKeySequence::SelectPreviousWord)
|
||||
{
|
||||
setCursorPosition(getCursorWordBackward(), true);
|
||||
}
|
||||
else if (event == QKeySequence::SelectNextLine)
|
||||
{
|
||||
setCursorPosition(getCursorLineDown(), true);
|
||||
}
|
||||
else if (event == QKeySequence::SelectPreviousLine)
|
||||
{
|
||||
setCursorPosition(getCursorLineUp(), true);
|
||||
}
|
||||
else if (event == QKeySequence::SelectStartOfLine || event == QKeySequence::SelectStartOfBlock)
|
||||
{
|
||||
setCursorPosition(getCursorLineStart(), true);
|
||||
}
|
||||
else if (event == QKeySequence::SelectEndOfLine || event == QKeySequence::SelectEndOfBlock)
|
||||
{
|
||||
setCursorPosition(getCursorLineEnd(), true);
|
||||
}
|
||||
else if (event == QKeySequence::SelectStartOfDocument)
|
||||
{
|
||||
setCursorPosition(getCursorDocumentStart(), true);
|
||||
}
|
||||
else if (event == QKeySequence::SelectEndOfDocument)
|
||||
{
|
||||
setCursorPosition(getCursorDocumentEnd(), true);
|
||||
}
|
||||
else if (event == QKeySequence::DeleteStartOfWord)
|
||||
{
|
||||
if (!isReadonly())
|
||||
{
|
||||
setCursorPosition(getCursorWordBackward(), true);
|
||||
performRemoveSelectedText();
|
||||
}
|
||||
}
|
||||
else if (event == QKeySequence::DeleteEndOfWord)
|
||||
{
|
||||
if (!isReadonly())
|
||||
{
|
||||
setCursorPosition(getCursorWordForward(), true);
|
||||
performRemoveSelectedText();
|
||||
}
|
||||
}
|
||||
else if (event == QKeySequence::DeleteEndOfLine)
|
||||
{
|
||||
if (!isReadonly())
|
||||
{
|
||||
setCursorPosition(getCursorLineEnd(), true);
|
||||
performRemoveSelectedText();
|
||||
}
|
||||
}
|
||||
else if (event == QKeySequence::Deselect)
|
||||
{
|
||||
clearSelection();
|
||||
}
|
||||
else if (event == QKeySequence::DeleteCompleteLine)
|
||||
{
|
||||
if (!isReadonly())
|
||||
{
|
||||
m_selectionStart = getCurrentLineTextStart();
|
||||
m_selectionEnd = getCurrentLineTextEnd();
|
||||
performRemoveSelectedText();
|
||||
}
|
||||
}
|
||||
else if (event == QKeySequence::Backspace || event->key() == Qt::Key_Backspace)
|
||||
{
|
||||
performBackspace();
|
||||
}
|
||||
else if (event->key() == Qt::Key_Direction_L)
|
||||
{
|
||||
QTextOption option = m_textLayout.textOption();
|
||||
option.setTextDirection(Qt::LeftToRight);
|
||||
m_textLayout.setTextOption(qMove(option));
|
||||
updateTextLayout();
|
||||
}
|
||||
else if (event->key() == Qt::Key_Direction_R)
|
||||
{
|
||||
QTextOption option = m_textLayout.textOption();
|
||||
option.setTextDirection(Qt::LeftToRight);
|
||||
m_textLayout.setTextOption(qMove(option));
|
||||
updateTextLayout();
|
||||
}
|
||||
else if (event->key() == Qt::Key_Up)
|
||||
{
|
||||
setCursorPosition(getCursorLineUp(), event->modifiers().testFlag(Qt::ShiftModifier));
|
||||
}
|
||||
else if (event->key() == Qt::Key_Down)
|
||||
{
|
||||
setCursorPosition(getCursorLineDown(), event->modifiers().testFlag(Qt::ShiftModifier));
|
||||
}
|
||||
else if (event->key() == Qt::Key_Left)
|
||||
{
|
||||
const int position = (event->modifiers().testFlag(Qt::ControlModifier)) ? getCursorWordBackward() : getCursorCharacterBackward();
|
||||
setCursorPosition(position, event->modifiers().testFlag(Qt::ShiftModifier));
|
||||
}
|
||||
else if (event->key() == Qt::Key_Right)
|
||||
{
|
||||
const int position = (event->modifiers().testFlag(Qt::ControlModifier)) ? getCursorWordForward() : getCursorCharacterForward();
|
||||
setCursorPosition(position, event->modifiers().testFlag(Qt::ShiftModifier));
|
||||
}
|
||||
else if (isMultiline() && (event->key() == Qt::Key_Enter || event->key() == Qt::Key_Return))
|
||||
{
|
||||
performInsertText(QString::fromUtf16(u"\u2028"));
|
||||
}
|
||||
else
|
||||
{
|
||||
QString text = event->text();
|
||||
if (!text.isEmpty())
|
||||
{
|
||||
performInsertText(text);
|
||||
}
|
||||
else
|
||||
{
|
||||
event->ignore();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PDFTextEditPseudowidget::setSelection(int startPosition, int selectionLength)
|
||||
{
|
||||
if (selectionLength > 0)
|
||||
{
|
||||
// We are selecting to the right
|
||||
m_selectionStart = startPosition;
|
||||
m_selectionEnd = qMin(startPosition + selectionLength, getTextLength());
|
||||
m_positionCursor = m_selectionEnd;
|
||||
}
|
||||
else if (selectionLength < 0)
|
||||
{
|
||||
// We are selecting to the left
|
||||
m_selectionStart = qMax(startPosition + selectionLength, 0);
|
||||
m_selectionEnd = startPosition;
|
||||
m_positionCursor = m_selectionStart;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Clear the selection
|
||||
m_selectionStart = 0;
|
||||
m_selectionEnd = 0;
|
||||
m_positionCursor = startPosition;
|
||||
}
|
||||
}
|
||||
|
||||
void PDFTextEditPseudowidget::setCursorPosition(int position, bool select)
|
||||
{
|
||||
if (select)
|
||||
{
|
||||
const bool isTextSelected = this->isTextSelected();
|
||||
const bool isCursorAtStartOfSelection = isTextSelected && m_selectionStart == m_positionCursor;
|
||||
const bool isCursorAtEndOfSelection = isTextSelected && m_selectionEnd == m_positionCursor;
|
||||
|
||||
// Do we have selected text, and cursor is at the end of selected text?
|
||||
// In this case, we must preserve start of the selection (we are manipulating
|
||||
// with the end of selection.
|
||||
if (isCursorAtEndOfSelection)
|
||||
{
|
||||
m_selectionStart = qMin(m_selectionStart, position);
|
||||
m_selectionEnd = qMax(m_selectionStart, position);
|
||||
}
|
||||
else if (isCursorAtStartOfSelection)
|
||||
{
|
||||
// We must preserve end of the text selection, because we are manipulating
|
||||
// with start of text selection.
|
||||
m_selectionStart = qMin(m_selectionEnd, position);
|
||||
m_selectionEnd = qMax(m_selectionEnd, position);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Otherwise we are manipulating with cursor
|
||||
m_selectionStart = qMin(m_positionCursor, position);
|
||||
m_selectionEnd = qMax(m_positionCursor, position);
|
||||
}
|
||||
}
|
||||
|
||||
// Why we are clearing text selection, even if we doesn't have it?
|
||||
// We can have, for example m_selectionStart == m_selectionEnd == 3,
|
||||
// and we want to have it both zero.
|
||||
if (!select || !isTextSelected())
|
||||
{
|
||||
clearSelection();
|
||||
}
|
||||
|
||||
m_positionCursor = position;
|
||||
}
|
||||
|
||||
void PDFTextEditPseudowidget::setText(const QString& text)
|
||||
{
|
||||
clearSelection();
|
||||
m_editText = text;
|
||||
setCursorPosition(getPositionEnd(), false);
|
||||
updateTextLayout();
|
||||
}
|
||||
|
||||
void PDFTextEditPseudowidget::setAppearance(const PDFAnnotationDefaultAppearance& appearance, Qt::Alignment textAlignment, QRectF rect, int maxTextLength)
|
||||
{
|
||||
// Set appearance
|
||||
qreal fontSize = appearance.getFontSize();
|
||||
if (qFuzzyIsNull(fontSize))
|
||||
{
|
||||
fontSize = rect.height();
|
||||
}
|
||||
|
||||
QFont font(appearance.getFontName());
|
||||
font.setHintingPreference(QFont::PreferNoHinting);
|
||||
font.setPixelSize(qCeil(fontSize));
|
||||
font.setStyleStrategy(QFont::ForceOutline);
|
||||
m_textLayout.setFont(font);
|
||||
|
||||
QTextOption option = m_textLayout.textOption();
|
||||
option.setWrapMode(isMultiline() ? QTextOption::WrapAtWordBoundaryOrAnywhere : QTextOption::NoWrap);
|
||||
option.setAlignment(textAlignment);
|
||||
option.setUseDesignMetrics(true);
|
||||
m_textLayout.setTextOption(option);
|
||||
|
||||
m_textColor = appearance.getFontColor();
|
||||
if (!m_textColor.isValid())
|
||||
{
|
||||
m_textColor = Qt::black;
|
||||
}
|
||||
|
||||
m_maxTextLength = maxTextLength;
|
||||
m_widgetRect = rect;
|
||||
updateTextLayout();
|
||||
}
|
||||
|
||||
void PDFTextEditPseudowidget::setAppearance(const QFont& font,
|
||||
Qt::Alignment textAlignment,
|
||||
QRectF rect,
|
||||
int maxTextLength,
|
||||
QColor textColor)
|
||||
{
|
||||
// Set appearance
|
||||
m_textLayout.setFont(font);
|
||||
|
||||
QTextOption option = m_textLayout.textOption();
|
||||
option.setWrapMode(isMultiline() ? QTextOption::WrapAtWordBoundaryOrAnywhere : QTextOption::NoWrap);
|
||||
option.setAlignment(textAlignment);
|
||||
option.setUseDesignMetrics(true);
|
||||
m_textLayout.setTextOption(option);
|
||||
|
||||
m_textColor = textColor;
|
||||
if (!m_textColor.isValid())
|
||||
{
|
||||
m_textColor = Qt::black;
|
||||
}
|
||||
|
||||
m_maxTextLength = maxTextLength;
|
||||
m_widgetRect = rect;
|
||||
updateTextLayout();
|
||||
}
|
||||
|
||||
void PDFTextEditPseudowidget::performCut()
|
||||
{
|
||||
if (isReadonly())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
performCopy();
|
||||
performRemoveSelectedText();
|
||||
}
|
||||
|
||||
void PDFTextEditPseudowidget::performCopy()
|
||||
{
|
||||
if (isTextSelected() && !isPassword())
|
||||
{
|
||||
QApplication::clipboard()->setText(getSelectedText(), QClipboard::Clipboard);
|
||||
}
|
||||
}
|
||||
|
||||
void PDFTextEditPseudowidget::performPaste()
|
||||
{
|
||||
// We always insert text, even if it is empty. Because we want
|
||||
// to erase selected text.
|
||||
performInsertText(QApplication::clipboard()->text(QClipboard::Clipboard));
|
||||
}
|
||||
|
||||
void PDFTextEditPseudowidget::performClear()
|
||||
{
|
||||
if (isReadonly())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
performSelectAll();
|
||||
performRemoveSelectedText();
|
||||
}
|
||||
|
||||
void PDFTextEditPseudowidget::performSelectAll()
|
||||
{
|
||||
m_selectionStart = getPositionStart();
|
||||
m_selectionEnd = getPositionEnd();
|
||||
}
|
||||
|
||||
void PDFTextEditPseudowidget::performBackspace()
|
||||
{
|
||||
if (isReadonly())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// If we have selection, then delete selected text. If we do not have
|
||||
// selection, then we delete previous character.
|
||||
if (!isTextSelected())
|
||||
{
|
||||
setCursorPosition(m_textLayout.previousCursorPosition(m_positionCursor, QTextLayout::SkipCharacters), true);
|
||||
}
|
||||
|
||||
performRemoveSelectedText();
|
||||
}
|
||||
|
||||
void PDFTextEditPseudowidget::performDelete()
|
||||
{
|
||||
if (isReadonly())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// If we have selection, then delete selected text. If we do not have
|
||||
// selection, then we delete previous character.
|
||||
if (!isTextSelected())
|
||||
{
|
||||
setCursorPosition(m_textLayout.nextCursorPosition(m_positionCursor, QTextLayout::SkipCharacters), true);
|
||||
}
|
||||
|
||||
performRemoveSelectedText();
|
||||
}
|
||||
|
||||
void PDFTextEditPseudowidget::performRemoveSelectedText()
|
||||
{
|
||||
if (isTextSelected())
|
||||
{
|
||||
m_editText.remove(m_selectionStart, getSelectionLength());
|
||||
setCursorPosition(m_selectionStart, false);
|
||||
clearSelection();
|
||||
updateTextLayout();
|
||||
}
|
||||
}
|
||||
|
||||
void PDFTextEditPseudowidget::performInsertText(const QString& text)
|
||||
{
|
||||
if (isReadonly())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Insert text at the cursor
|
||||
performRemoveSelectedText();
|
||||
m_editText.insert(m_positionCursor, text);
|
||||
setCursorPosition(m_positionCursor + text.length(), false);
|
||||
updateTextLayout();
|
||||
}
|
||||
|
||||
QMatrix PDFTextEditPseudowidget::createTextBoxTransformMatrix(bool edit) const
|
||||
{
|
||||
QMatrix matrix;
|
||||
|
||||
matrix.translate(m_widgetRect.left(), m_widgetRect.bottom());
|
||||
matrix.scale(1.0, -1.0);
|
||||
|
||||
if (edit && !isComb() && m_textLayout.isValidCursorPosition(m_positionCursor))
|
||||
{
|
||||
// Jakub Melka: we must scroll the control, so cursor is always
|
||||
// visible in the widget area. If we are not editing, this not the
|
||||
// case, because we always show the text from the beginning.
|
||||
|
||||
const QTextLine line = m_textLayout.lineForTextPosition(m_positionCursor);
|
||||
if (line.isValid())
|
||||
{
|
||||
const qreal xCursorPosition = line.cursorToX(m_positionCursor);
|
||||
if (xCursorPosition >= m_widgetRect.width())
|
||||
{
|
||||
const qreal delta = xCursorPosition - m_widgetRect.width();
|
||||
matrix.translate(-delta, 0.0);
|
||||
}
|
||||
|
||||
// Check, if we aren't outside of y position
|
||||
const qreal lineSpacing = line.leadingIncluded() ? line.height() : line.leading() + line.height();
|
||||
const qreal lineBottom = lineSpacing * (line.lineNumber() + 1);
|
||||
|
||||
if (lineBottom >= m_widgetRect.height())
|
||||
{
|
||||
const qreal delta = lineBottom - m_widgetRect.height();
|
||||
matrix.translate(0.0, -delta);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!isMultiline() && !isComb())
|
||||
{
|
||||
// If text is single line, then adjust text position to the vertical center
|
||||
QTextLine textLine = m_textLayout.lineAt(0);
|
||||
if (textLine.isValid())
|
||||
{
|
||||
const qreal lineSpacing = textLine.leadingIncluded() ? textLine.height() : textLine.leading() + textLine.height();
|
||||
const qreal textBoxHeight = m_widgetRect.height();
|
||||
|
||||
if (lineSpacing < textBoxHeight)
|
||||
{
|
||||
const qreal delta = (textBoxHeight - lineSpacing) * 0.5;
|
||||
matrix.translate(0.0, delta);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return matrix;
|
||||
}
|
||||
|
||||
std::vector<int> PDFTextEditPseudowidget::getCursorPositions() const
|
||||
{
|
||||
std::vector<int> result;
|
||||
result.reserve(m_editText.length());
|
||||
result.push_back(0);
|
||||
|
||||
int currentPos = 0;
|
||||
while (currentPos < m_editText.length())
|
||||
{
|
||||
currentPos = m_textLayout.nextCursorPosition(currentPos, QTextLayout::SkipCharacters);
|
||||
result.push_back(currentPos);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void PDFTextEditPseudowidget::draw(AnnotationDrawParameters& parameters, bool edit) const
|
||||
{
|
||||
pdf::PDFPainterStateGuard guard(parameters.painter);
|
||||
|
||||
if (parameters.annotation)
|
||||
{
|
||||
parameters.boundingRectangle = parameters.annotation->getRectangle();
|
||||
}
|
||||
|
||||
QPalette palette = QApplication::palette();
|
||||
|
||||
auto getAdjustedColor = [¶meters](QColor color)
|
||||
{
|
||||
if (parameters.invertColors)
|
||||
{
|
||||
return invertColor(color);
|
||||
}
|
||||
|
||||
return color;
|
||||
};
|
||||
|
||||
QPainter* painter = parameters.painter;
|
||||
|
||||
if (edit)
|
||||
{
|
||||
pdf::PDFPainterStateGuard guard(painter);
|
||||
painter->setPen(getAdjustedColor(Qt::black));
|
||||
painter->setBrush(Qt::NoBrush);
|
||||
painter->drawRect(parameters.boundingRectangle);
|
||||
}
|
||||
|
||||
painter->setClipRect(parameters.boundingRectangle, Qt::IntersectClip);
|
||||
painter->setWorldMatrix(createTextBoxTransformMatrix(edit), true);
|
||||
painter->setPen(getAdjustedColor(Qt::black));
|
||||
|
||||
if (isComb())
|
||||
{
|
||||
const qreal combCount = qMax(m_maxTextLength, 1);
|
||||
qreal combWidth = m_widgetRect.width() / combCount;
|
||||
QRectF combRect(0.0, 0.0, combWidth, m_widgetRect.height());
|
||||
painter->setFont(m_textLayout.font());
|
||||
|
||||
QColor textColor = getAdjustedColor(m_textColor);
|
||||
QColor highlightTextColor = getAdjustedColor(palette.color(QPalette::HighlightedText));
|
||||
QColor highlightColor = getAdjustedColor(palette.color(QPalette::Highlight));
|
||||
|
||||
std::vector<int> positions = getCursorPositions();
|
||||
for (size_t i = 1; i < positions.size(); ++i)
|
||||
{
|
||||
if (positions[i - 1] >= m_selectionStart && positions[i] - 1 < m_selectionEnd)
|
||||
{
|
||||
// We are in text selection
|
||||
painter->fillRect(combRect, highlightColor);
|
||||
painter->setPen(highlightTextColor);
|
||||
}
|
||||
else
|
||||
{
|
||||
// We are not in text selection
|
||||
painter->setPen(textColor);
|
||||
}
|
||||
|
||||
int length = positions[i] - positions[i - 1];
|
||||
QString text = m_displayText.mid(positions[i - 1], length);
|
||||
painter->drawText(combRect, Qt::AlignCenter, text);
|
||||
|
||||
// Draw the cursor?
|
||||
if (edit && m_positionCursor >= positions[i - 1] && m_positionCursor < positions[i])
|
||||
{
|
||||
QRectF cursorRect(combRect.left(), combRect.bottom() - 1, combRect.width(), 1);
|
||||
painter->fillRect(cursorRect, textColor);
|
||||
}
|
||||
|
||||
combRect.translate(combWidth, 0.0);
|
||||
}
|
||||
|
||||
// Draw the cursor onto next unfilled cell?
|
||||
if (edit && m_positionCursor == getPositionEnd())
|
||||
{
|
||||
QRectF cursorRect(combRect.left(), combRect.bottom() - 1, combRect.width(), 1);
|
||||
painter->fillRect(cursorRect, textColor);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
QVector<QTextLayout::FormatRange> selections;
|
||||
|
||||
QTextLayout::FormatRange defaultFormat;
|
||||
defaultFormat.start = getPositionStart();
|
||||
defaultFormat.length = getTextLength();
|
||||
defaultFormat.format.clearBackground();
|
||||
defaultFormat.format.setForeground(QBrush(getAdjustedColor(m_textColor), Qt::SolidPattern));
|
||||
|
||||
// If we are editing, draw selections
|
||||
if (edit && isTextSelected())
|
||||
{
|
||||
QTextLayout::FormatRange before = defaultFormat;
|
||||
QTextLayout::FormatRange after = defaultFormat;
|
||||
|
||||
before.start = getPositionStart();
|
||||
before.length = m_selectionStart;
|
||||
after.start = m_selectionEnd;
|
||||
after.length = getTextLength() - m_selectionEnd;
|
||||
|
||||
QTextLayout::FormatRange selectedFormat = defaultFormat;
|
||||
selectedFormat.start = m_selectionStart;
|
||||
selectedFormat.length = getSelectionLength();
|
||||
selectedFormat.format.setForeground(QBrush(getAdjustedColor(palette.color(QPalette::HighlightedText)), Qt::SolidPattern));
|
||||
selectedFormat.format.setBackground(QBrush(getAdjustedColor(palette.color(QPalette::Highlight)), Qt::SolidPattern));
|
||||
|
||||
selections = { before, selectedFormat, after};
|
||||
}
|
||||
else
|
||||
{
|
||||
selections.push_back(defaultFormat);
|
||||
}
|
||||
|
||||
// Draw text
|
||||
m_textLayout.draw(painter, QPointF(0.0, 0.0), selections, QRectF());
|
||||
|
||||
// If we are editing, also draw text
|
||||
if (edit && !isReadonly())
|
||||
{
|
||||
m_textLayout.drawCursor(painter, QPointF(0.0, 0.0), m_positionCursor);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int PDFTextEditPseudowidget::getCursorPositionFromWidgetPosition(const QPointF& point, bool edit) const
|
||||
{
|
||||
QMatrix textBoxSpaceToPageSpace = createTextBoxTransformMatrix(edit);
|
||||
QMatrix pageSpaceToTextBoxSpace = textBoxSpaceToPageSpace.inverted();
|
||||
|
||||
QPointF textBoxPoint = pageSpaceToTextBoxSpace.map(point);
|
||||
|
||||
if (isComb())
|
||||
{
|
||||
// If it is comb, then characters are spaced equidistantly
|
||||
const qreal x = qBound(0.0, textBoxPoint.x(), m_widgetRect.width());
|
||||
const size_t position = qFloor(x * qreal(m_maxTextLength) / qreal(m_widgetRect.width()));
|
||||
std::vector<int> positions = getCursorPositions();
|
||||
if (position < positions.size())
|
||||
{
|
||||
return positions[position];
|
||||
}
|
||||
|
||||
return positions.back();
|
||||
}
|
||||
else if (m_textLayout.lineCount() > 0)
|
||||
{
|
||||
QTextLine line;
|
||||
qreal yPos = 0.0;
|
||||
|
||||
// Find line under cursor
|
||||
for (int i = 0; i < m_textLayout.lineCount(); ++i)
|
||||
{
|
||||
QTextLine currentLine = m_textLayout.lineAt(i);
|
||||
const qreal lineSpacing = currentLine.leadingIncluded() ? currentLine.height() : currentLine.leading() + currentLine.height();
|
||||
const qreal yNextPos = yPos + lineSpacing;
|
||||
|
||||
if (textBoxPoint.y() >= yPos && textBoxPoint.y() < yNextPos)
|
||||
{
|
||||
line = currentLine;
|
||||
break;
|
||||
}
|
||||
|
||||
yPos = yNextPos;
|
||||
}
|
||||
|
||||
// If no line is found, select last line
|
||||
if (!line.isValid())
|
||||
{
|
||||
if (textBoxPoint.y() < 0.0)
|
||||
{
|
||||
line = m_textLayout.lineAt(0);
|
||||
}
|
||||
else
|
||||
{
|
||||
line = m_textLayout.lineAt(m_textLayout.lineCount() - 1);
|
||||
}
|
||||
}
|
||||
|
||||
return line.xToCursor(textBoxPoint.x(), QTextLine::CursorBetweenCharacters);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void PDFTextEditPseudowidget::updateTextLayout()
|
||||
{
|
||||
// Prepare display text
|
||||
if (isPassword())
|
||||
{
|
||||
m_displayText.resize(m_editText.length(), m_passwordReplacementCharacter);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_displayText = m_editText;
|
||||
}
|
||||
|
||||
// Perform text layout
|
||||
m_textLayout.clearLayout();
|
||||
m_textLayout.setText(m_displayText);
|
||||
m_textLayout.beginLayout();
|
||||
|
||||
QPointF textLinePosition(0.0, 0.0);
|
||||
|
||||
while (true)
|
||||
{
|
||||
QTextLine textLine = m_textLayout.createLine();
|
||||
if (!textLine.isValid())
|
||||
{
|
||||
// We are finished with layout
|
||||
break;
|
||||
}
|
||||
|
||||
textLinePosition.ry() += textLine.leading();
|
||||
textLine.setLineWidth(m_widgetRect.width());
|
||||
textLine.setPosition(textLinePosition);
|
||||
textLinePosition.ry() += textLine.height();
|
||||
}
|
||||
m_textLayout.endLayout();
|
||||
|
||||
// Check length
|
||||
if (m_maxTextLength > 0)
|
||||
{
|
||||
int length = 0;
|
||||
int currentPos = 0;
|
||||
|
||||
while (currentPos < m_editText.length())
|
||||
{
|
||||
currentPos = m_textLayout.nextCursorPosition(currentPos, QTextLayout::SkipCharacters);
|
||||
++length;
|
||||
|
||||
if (length == m_maxTextLength)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (currentPos < m_editText.length())
|
||||
{
|
||||
m_editText = m_editText.left(currentPos);
|
||||
m_positionCursor = qBound(getPositionStart(), m_positionCursor, getPositionEnd());
|
||||
updateTextLayout();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int PDFTextEditPseudowidget::getSingleStepForward() const
|
||||
{
|
||||
// If direction is right-to-left, then move backward (because
|
||||
// text is painted from right to left.
|
||||
return (m_textLayout.textOption().textDirection() == Qt::RightToLeft) ? -1 : 1;
|
||||
}
|
||||
|
||||
int PDFTextEditPseudowidget::getNextPrevCursorPosition(int referencePosition, int steps, QTextLayout::CursorMode mode) const
|
||||
{
|
||||
int cursor = referencePosition;
|
||||
|
||||
if (steps > 0)
|
||||
{
|
||||
for (int i = 0; i < steps; ++i)
|
||||
{
|
||||
cursor = m_textLayout.nextCursorPosition(cursor, mode);
|
||||
}
|
||||
}
|
||||
else if (steps < 0)
|
||||
{
|
||||
for (int i = 0; i < -steps; ++i)
|
||||
{
|
||||
cursor = m_textLayout.previousCursorPosition(cursor, mode);
|
||||
}
|
||||
}
|
||||
|
||||
return cursor;
|
||||
}
|
||||
|
||||
int PDFTextEditPseudowidget::getCurrentLineTextStart() const
|
||||
{
|
||||
return m_textLayout.lineForTextPosition(m_positionCursor).textStart();
|
||||
}
|
||||
|
||||
int PDFTextEditPseudowidget::getCurrentLineTextEnd() const
|
||||
{
|
||||
QTextLine textLine = m_textLayout.lineForTextPosition(m_positionCursor);
|
||||
return textLine.textStart() + textLine.textLength();
|
||||
}
|
||||
|
||||
int PDFTextEditPseudowidget::getCursorLineUp() const
|
||||
{
|
||||
QTextLine line = m_textLayout.lineForTextPosition(m_positionCursor);
|
||||
const int lineIndex = line.lineNumber() - 1;
|
||||
|
||||
if (lineIndex >= 0)
|
||||
{
|
||||
QTextLine upLine = m_textLayout.lineAt(lineIndex);
|
||||
return upLine.xToCursor(line.cursorToX(m_positionCursor), QTextLine::CursorBetweenCharacters);
|
||||
}
|
||||
|
||||
return m_positionCursor;
|
||||
}
|
||||
|
||||
int PDFTextEditPseudowidget::getCursorLineDown() const
|
||||
{
|
||||
QTextLine line = m_textLayout.lineForTextPosition(m_positionCursor);
|
||||
const int lineIndex = line.lineNumber() + 1;
|
||||
|
||||
if (lineIndex < m_textLayout.lineCount())
|
||||
{
|
||||
QTextLine downLine = m_textLayout.lineAt(lineIndex);
|
||||
return downLine.xToCursor(line.cursorToX(m_positionCursor), QTextLine::CursorBetweenCharacters);
|
||||
}
|
||||
|
||||
return m_positionCursor;
|
||||
}
|
||||
|
||||
} // namespace pdf
|
217
Pdf4QtLib/sources/pdftexteditpseudowidget.h
Normal file
217
Pdf4QtLib/sources/pdftexteditpseudowidget.h
Normal file
@@ -0,0 +1,217 @@
|
||||
// Copyright (C) 2022 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 <https://www.gnu.org/licenses/>.
|
||||
|
||||
#ifndef PDFTEXTEDITPSEUDOWIDGET_H
|
||||
#define PDFTEXTEDITPSEUDOWIDGET_H
|
||||
|
||||
#include "pdfglobal.h"
|
||||
#include "pdfform.h"
|
||||
|
||||
#include <QRectF>
|
||||
#include <QColor>
|
||||
|
||||
class QWidget;
|
||||
class QKeyEvent;
|
||||
|
||||
namespace pdf
|
||||
{
|
||||
|
||||
/// "Pseudo" widget, which is emulating text editor, which can be single line, or multiline.
|
||||
/// Passwords can also be edited and editor can be read only.
|
||||
class PDFTextEditPseudowidget
|
||||
{
|
||||
public:
|
||||
explicit PDFTextEditPseudowidget(PDFFormField::FieldFlags flags);
|
||||
|
||||
void shortcutOverrideEvent(QWidget* widget, QKeyEvent* event);
|
||||
void keyPressEvent(QWidget* widget, QKeyEvent* event);
|
||||
|
||||
inline bool isReadonly() const { return m_flags.testFlag(PDFFormField::ReadOnly); }
|
||||
inline bool isMultiline() const { return m_flags.testFlag(PDFFormField::Multiline); }
|
||||
inline bool isPassword() const { return m_flags.testFlag(PDFFormField::Password); }
|
||||
inline bool isFileSelect() const { return m_flags.testFlag(PDFFormField::FileSelect); }
|
||||
inline bool isComb() const { return m_flags.testFlag(PDFFormField::Comb); }
|
||||
|
||||
inline bool isEmpty() const { return m_editText.isEmpty(); }
|
||||
inline bool isTextSelected() const { return !isEmpty() && getSelectionLength() > 0; }
|
||||
inline bool isWholeTextSelected() const { return !isEmpty() && getSelectionLength() == m_editText.length(); }
|
||||
|
||||
inline int getTextLength() const { return m_editText.length(); }
|
||||
inline int getSelectionLength() const { return m_selectionEnd - m_selectionStart; }
|
||||
|
||||
inline int getPositionCursor() const { return m_positionCursor; }
|
||||
inline int getPositionStart() const { return 0; }
|
||||
inline int getPositionEnd() const { return getTextLength(); }
|
||||
|
||||
inline void clearSelection() { m_selectionStart = m_selectionEnd = 0; }
|
||||
|
||||
inline const QString& getText() const { return m_editText; }
|
||||
inline QString getSelectedText() const { return m_editText.mid(m_selectionStart, getSelectionLength()); }
|
||||
|
||||
/// Sets (updates) text selection
|
||||
/// \param startPosition From where we are selecting text
|
||||
/// \param selectionLength Selection length (positive - to the right, negative - to the left)
|
||||
void setSelection(int startPosition, int selectionLength);
|
||||
|
||||
/// Moves cursor position. It behaves as usual in text boxes,
|
||||
/// when user moves the cursor. If \p select is true, then
|
||||
/// selection is updated.
|
||||
/// \param position New position of the cursor
|
||||
/// \param select Select text when moving the cursor?
|
||||
void setCursorPosition(int position, bool select);
|
||||
|
||||
/// Sets text content of the widget. This functions sets the text,
|
||||
/// even if widget is readonly.
|
||||
/// \param text Text to be set
|
||||
void setText(const QString& text);
|
||||
|
||||
/// Sets widget appearance, such as font, font size, color, text alignment,
|
||||
/// and rectangle, in which widget resides on page (in page coordinates)
|
||||
/// \param appearance Appearance
|
||||
/// \param textAlignment Text alignment
|
||||
/// \param rect Widget rectangle in page coordinates
|
||||
/// \param maxTextLength Maximal text length
|
||||
void setAppearance(const PDFAnnotationDefaultAppearance& appearance,
|
||||
Qt::Alignment textAlignment,
|
||||
QRectF rect,
|
||||
int maxTextLength);
|
||||
|
||||
/// Sets widget appearance, such as font, font size, color, text alignment,
|
||||
/// and rectangle, in which widget resides on page (in page coordinates)
|
||||
/// \param font Font
|
||||
/// \param textAlignment Text alignment
|
||||
/// \param rect Widget rectangle in page coordinates
|
||||
/// \param maxTextLength Maximal text length
|
||||
/// \param textColor Color
|
||||
void setAppearance(const QFont& font,
|
||||
Qt::Alignment textAlignment,
|
||||
QRectF rect,
|
||||
int maxTextLength,
|
||||
QColor textColor);
|
||||
|
||||
void performCut();
|
||||
void performCopy();
|
||||
void performPaste();
|
||||
void performClear();
|
||||
void performSelectAll();
|
||||
void performBackspace();
|
||||
void performDelete();
|
||||
void performRemoveSelectedText();
|
||||
void performInsertText(const QString& text);
|
||||
|
||||
/// Draw text edit using given parameters
|
||||
/// \param parameters Parameters
|
||||
void draw(AnnotationDrawParameters& parameters, bool edit) const;
|
||||
|
||||
/// Returns valid cursor position retrieved from position in the widget.
|
||||
/// \param point Point in page coordinate space
|
||||
/// \param edit Are we using edit transformations?
|
||||
/// \returns Cursor position
|
||||
int getCursorPositionFromWidgetPosition(const QPointF& point, bool edit) const;
|
||||
|
||||
inline int getCursorForward(QTextLayout::CursorMode mode) const { return getNextPrevCursorPosition(getSingleStepForward(), mode); }
|
||||
inline int getCursorBackward(QTextLayout::CursorMode mode) const { return getNextPrevCursorPosition(getSingleStepBackward(), mode); }
|
||||
inline int getCursorCharacterForward() const { return getCursorForward(QTextLayout::SkipCharacters); }
|
||||
inline int getCursorCharacterBackward() const { return getCursorBackward(QTextLayout::SkipCharacters); }
|
||||
inline int getCursorWordForward() const { return getCursorForward(QTextLayout::SkipWords); }
|
||||
inline int getCursorWordBackward() const { return getCursorBackward(QTextLayout::SkipWords); }
|
||||
inline int getCursorDocumentStart() const { return (getSingleStepForward() > 0) ? getPositionStart() : getPositionEnd(); }
|
||||
inline int getCursorDocumentEnd() const { return (getSingleStepForward() > 0) ? getPositionEnd() : getPositionStart(); }
|
||||
inline int getCursorLineStart() const { return (getSingleStepForward() > 0) ? getCurrentLineTextStart() : getCurrentLineTextEnd(); }
|
||||
inline int getCursorLineEnd() const { return (getSingleStepForward() > 0) ? getCurrentLineTextEnd() : getCurrentLineTextStart(); }
|
||||
inline int getCursorNextLine() const { return getCurrentLineTextEnd(); }
|
||||
inline int getCursorPreviousLine() const { return getNextPrevCursorPosition(getCurrentLineTextStart(), -1, QTextLayout::SkipCharacters); }
|
||||
|
||||
const QRectF& getWidgetRect() const { return m_widgetRect; }
|
||||
QFont getFont() const { return m_textLayout.font(); }
|
||||
QColor getFontColor() const { return m_textColor; }
|
||||
|
||||
private:
|
||||
/// This function does following things:
|
||||
/// 1) Clamps edit text to fit maximum length
|
||||
/// 2) Creates display string from edit string
|
||||
/// 3) Updates text layout
|
||||
void updateTextLayout();
|
||||
|
||||
/// Returns single step forward, which is determined
|
||||
/// by cursor move style and layout direction.
|
||||
int getSingleStepForward() const;
|
||||
|
||||
/// Returns single step backward, which is determined
|
||||
/// by cursor move style and layout direction.
|
||||
int getSingleStepBackward() const { return -getSingleStepForward(); }
|
||||
|
||||
/// Returns next/previous position, by number of steps,
|
||||
/// using given cursor mode (skipping characters or whole words).
|
||||
/// \param steps Number of steps to proceed (can be negative number)
|
||||
/// \param mode Skip mode - letters or words?
|
||||
int getNextPrevCursorPosition(int steps, QTextLayout::CursorMode mode) const { return getNextPrevCursorPosition(m_positionCursor, steps, mode); }
|
||||
|
||||
/// Returns next/previous position from reference cursor position, by number of steps,
|
||||
/// using given cursor mode (skipping characters or whole words).
|
||||
/// \param referencePosition Reference cursor position
|
||||
/// \param steps Number of steps to proceed (can be negative number)
|
||||
/// \param mode Skip mode - letters or words?
|
||||
int getNextPrevCursorPosition(int referencePosition, int steps, QTextLayout::CursorMode mode) const;
|
||||
|
||||
/// Returns current line text start position
|
||||
int getCurrentLineTextStart() const;
|
||||
|
||||
/// Returns current line text end position
|
||||
int getCurrentLineTextEnd() const;
|
||||
|
||||
/// Creates text box transform matrix, which transforms from
|
||||
/// widget space to page space.
|
||||
/// \param edit Create matrix for text editing?
|
||||
QMatrix createTextBoxTransformMatrix(bool edit) const;
|
||||
|
||||
/// Returns vector of cursor positions (which may be not equal
|
||||
/// to edit string length, because edit string can contain surrogates,
|
||||
/// or graphemes, which are single glyphs, but represented by more
|
||||
/// 16-bit unicode codes).
|
||||
std::vector<int> getCursorPositions() const;
|
||||
|
||||
int getCursorLineUp() const;
|
||||
int getCursorLineDown() const;
|
||||
|
||||
PDFFormField::FieldFlags m_flags;
|
||||
|
||||
/// Text edited by the user
|
||||
QString m_editText;
|
||||
|
||||
/// Text, which is displayed. It can differ from text
|
||||
/// edited by user, in case password is being entered.
|
||||
QString m_displayText;
|
||||
|
||||
/// Text layout
|
||||
QTextLayout m_textLayout;
|
||||
|
||||
/// Character for displaying passwords
|
||||
QChar m_passwordReplacementCharacter;
|
||||
|
||||
int m_selectionStart;
|
||||
int m_selectionEnd;
|
||||
int m_positionCursor;
|
||||
int m_maxTextLength;
|
||||
|
||||
QRectF m_widgetRect;
|
||||
QColor m_textColor;
|
||||
};
|
||||
|
||||
} // namespace pdf
|
||||
|
||||
#endif // PDFTEXTEDITPSEUDOWIDGET_H
|
@@ -465,6 +465,11 @@ constexpr bool isRectangleHorizontallyOverlapped(const QRectF& r1, const QRectF&
|
||||
return isIntervalOverlap(r1.left(), r1.right(), r2.left(), r2.right());
|
||||
}
|
||||
|
||||
constexpr bool isRectangleVerticallyOverlapped(const QRectF& r1, const QRectF& r2)
|
||||
{
|
||||
return isIntervalOverlap(r1.top(), r1.bottom(), r2.top(), r2.bottom());
|
||||
}
|
||||
|
||||
inline QColor invertColor(QColor color)
|
||||
{
|
||||
qreal r = 0.0;
|
||||
|
@@ -101,11 +101,19 @@ void PDFWidgetTool::setActive(bool active)
|
||||
setActiveImpl(active);
|
||||
updateActions();
|
||||
|
||||
m_proxy->repaintNeeded();
|
||||
emit m_proxy->repaintNeeded();
|
||||
emit toolActivityChanged(active);
|
||||
}
|
||||
}
|
||||
|
||||
void PDFWidgetTool::shortcutOverrideEvent(QWidget* widget, QKeyEvent* event)
|
||||
{
|
||||
if (PDFWidgetTool* tool = getTopToolstackTool())
|
||||
{
|
||||
tool->shortcutOverrideEvent(widget, event);
|
||||
}
|
||||
}
|
||||
|
||||
void PDFWidgetTool::keyPressEvent(QWidget* widget, QKeyEvent* event)
|
||||
{
|
||||
if (PDFWidgetTool* tool = getTopToolstackTool())
|
||||
@@ -130,6 +138,14 @@ void PDFWidgetTool::mousePressEvent(QWidget* widget, QMouseEvent* event)
|
||||
}
|
||||
}
|
||||
|
||||
void PDFWidgetTool::mouseDoubleClickEvent(QWidget* widget, QMouseEvent* event)
|
||||
{
|
||||
if (PDFWidgetTool* tool = getTopToolstackTool())
|
||||
{
|
||||
tool->mouseDoubleClickEvent(widget, event);
|
||||
}
|
||||
}
|
||||
|
||||
void PDFWidgetTool::mouseReleaseEvent(QWidget* widget, QMouseEvent* event)
|
||||
{
|
||||
if (PDFWidgetTool* tool = getTopToolstackTool())
|
||||
@@ -194,11 +210,13 @@ PDFWidgetTool* PDFWidgetTool::getTopToolstackTool() const
|
||||
|
||||
void PDFWidgetTool::addTool(PDFWidgetTool* tool)
|
||||
{
|
||||
tool->setActive(isActive());
|
||||
m_toolStack.push_back(tool);
|
||||
}
|
||||
|
||||
void PDFWidgetTool::removeTool()
|
||||
{
|
||||
m_toolStack.back()->setActive(false);
|
||||
m_toolStack.pop_back();
|
||||
}
|
||||
|
||||
@@ -809,8 +827,12 @@ PDFMagnifierTool* PDFToolManager::getMagnifierTool() const
|
||||
|
||||
void PDFToolManager::shortcutOverrideEvent(QWidget* widget, QKeyEvent* event)
|
||||
{
|
||||
Q_UNUSED(widget);
|
||||
Q_UNUSED(event);
|
||||
event->ignore();
|
||||
|
||||
if (PDFWidgetTool* activeTool = getActiveTool())
|
||||
{
|
||||
activeTool->shortcutOverrideEvent(widget, event);
|
||||
}
|
||||
}
|
||||
|
||||
void PDFToolManager::keyPressEvent(QWidget* widget, QKeyEvent* event)
|
||||
@@ -854,8 +876,12 @@ void PDFToolManager::mousePressEvent(QWidget* widget, QMouseEvent* event)
|
||||
|
||||
void PDFToolManager::mouseDoubleClickEvent(QWidget* widget, QMouseEvent* event)
|
||||
{
|
||||
Q_UNUSED(widget);
|
||||
Q_UNUSED(event);
|
||||
event->ignore();
|
||||
|
||||
if (PDFWidgetTool* activeTool = getActiveTool())
|
||||
{
|
||||
activeTool->mouseDoubleClickEvent(widget, event);
|
||||
}
|
||||
}
|
||||
|
||||
void PDFToolManager::mouseReleaseEvent(QWidget* widget, QMouseEvent* event)
|
||||
@@ -1070,6 +1096,11 @@ void PDFPickTool::drawPage(QPainter* painter,
|
||||
Q_UNUSED(layoutGetter);
|
||||
Q_UNUSED(errors);
|
||||
|
||||
if (!isActive())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// If we are picking rectangles, then draw current selection rectangle
|
||||
if (m_mode == Mode::Rectangles && m_drawSelectionRectangle && m_pageIndex == pageIndex && !m_pickedPoints.empty())
|
||||
{
|
||||
@@ -1097,6 +1128,11 @@ void PDFPickTool::drawPage(QPainter* painter,
|
||||
|
||||
void PDFPickTool::drawPostRendering(QPainter* painter, QRect rect) const
|
||||
{
|
||||
if (!isActive())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_mode != Mode::Images)
|
||||
{
|
||||
m_snapper.drawSnapPoints(painter);
|
||||
@@ -1233,7 +1269,7 @@ void PDFPickTool::resetTool()
|
||||
m_snapper.clearReferencePoint();
|
||||
|
||||
buildSnapData();
|
||||
getProxy()->repaintNeeded();
|
||||
emit getProxy()->repaintNeeded();
|
||||
}
|
||||
|
||||
void PDFPickTool::buildSnapData()
|
||||
|
@@ -62,6 +62,11 @@ public:
|
||||
/// Returns action for activating/deactivating this tool
|
||||
QAction* getAction() const { return m_action; }
|
||||
|
||||
/// Handles shortcut override event from widget, over which tool operates
|
||||
/// \param widget Widget, over which tool operates
|
||||
/// \param event Event
|
||||
virtual void shortcutOverrideEvent(QWidget* widget, QKeyEvent* event);
|
||||
|
||||
/// Handles key press event from widget, over which tool operates
|
||||
/// \param widget Widget, over which tool operates
|
||||
/// \param event Event
|
||||
@@ -77,6 +82,11 @@ public:
|
||||
/// \param event Event
|
||||
virtual void mousePressEvent(QWidget* widget, QMouseEvent* event);
|
||||
|
||||
/// Handles mouse double click event from widget, over which tool operates
|
||||
/// \param widget Widget, over which tool operates
|
||||
/// \param event Event
|
||||
virtual void mouseDoubleClickEvent(QWidget* widget, QMouseEvent* event);
|
||||
|
||||
/// Handles mouse release event from widget, over which tool operates
|
||||
/// \param widget Widget, over which tool operates
|
||||
/// \param event Event
|
||||
@@ -344,8 +354,8 @@ public:
|
||||
void setSelectionRectangleColor(QColor selectionRectangleColor);
|
||||
|
||||
signals:
|
||||
void pointPicked(PDFInteger pageIndex, QPointF pagePoint);
|
||||
void rectanglePicked(PDFInteger pageIndex, QRectF pageRectangle);
|
||||
void pointPicked(pdf::PDFInteger pageIndex, QPointF pagePoint);
|
||||
void rectanglePicked(pdf::PDFInteger pageIndex, QRectF pageRectangle);
|
||||
void imagePicked(const QImage& image);
|
||||
|
||||
protected:
|
||||
|
Reference in New Issue
Block a user