From 80d6a61b62a7bb904469adb17ba412db00a98455 Mon Sep 17 00:00:00 2001 From: Hoang Nguyen Date: Fri, 11 Jul 2014 09:24:28 +1000 Subject: [PATCH 1/9] Add support for rendering PDF form ComboBox Fix typo 'Parrent' to 'Parent' --- src/gui/painting/qpaintengine.h | 3 +++ src/gui/painting/qpainter.cpp | 9 ++++++++ src/gui/painting/qpainter.h | 3 ++- src/gui/painting/qprintengine_pdf.cpp | 31 +++++++++++++++++++++++++++ src/gui/painting/qprintengine_pdf_p.h | 1 + 5 files changed, 46 insertions(+), 1 deletion(-) diff --git a/src/gui/painting/qpaintengine.h b/src/gui/painting/qpaintengine.h index c02b638ed94..61505fab60a 100644 --- a/src/gui/painting/qpaintengine.h +++ b/src/gui/painting/qpaintengine.h @@ -175,6 +175,9 @@ class Q_GUI_EXPORT QPaintEngine virtual void addRadioButton(const QRectF &r, const QString & group="", bool checked=false, const QString &name="", bool readOnly=false) { Q_UNUSED(r); Q_UNUSED(checked); Q_UNUSED(name); Q_UNUSED(readOnly); Q_UNUSED(group); } + virtual void addComboBox(const QRectF &r, const QString &name="", const QString &option_list="[]", const QString &default_value="", bool readOnly=false) { + Q_UNUSED(r); Q_UNUSED(default_value); Q_UNUSED(name); Q_UNUSED(readOnly); Q_UNUSED(option_list); + } virtual void drawLines(const QLine *lines, int lineCount); virtual void drawLines(const QLineF *lines, int lineCount); diff --git a/src/gui/painting/qpainter.cpp b/src/gui/painting/qpainter.cpp index 924e02c1c0a..2d98879cb45 100644 --- a/src/gui/painting/qpainter.cpp +++ b/src/gui/painting/qpainter.cpp @@ -7448,6 +7448,15 @@ void QPainter::addRadioButton(const QRectF &r, const QString & group, bool check d->engine->addRadioButton(worldTransform().mapRect(r), group, checked, name, readOnly); } +void QPainter::addComboBox(const QRectF &r, const QString &name, const QString &option_list, const QString &default_value, bool readOnly) { + Q_D(QPainter); + if (!d->engine) { + qWarning("QPainter::addComboBox: Painter not active"); + return; + } + d->engine->addComboBox(worldTransform().mapRect(r), name, option_list, default_value, readOnly); +} + /*! Sets the given render \a hint on the painter if \a on is true; otherwise clears the render hint. diff --git a/src/gui/painting/qpainter.h b/src/gui/painting/qpainter.h index cfba5f60f41..ca6fe2be53c 100644 --- a/src/gui/painting/qpainter.h +++ b/src/gui/painting/qpainter.h @@ -457,7 +457,8 @@ class Q_GUI_EXPORT QPainter void addTextField(const QRectF &r, const QString &text="", const QString &name="", bool multiLine=false, bool password=false, bool readOnly=false, int maxLength=-1); void addCheckBox(const QRectF &r, bool checked=false, const QString &name="", bool readOnly=false); - void addRadioButton(const QRectF &r, const QString & group="", bool checked=false, const QString &name="", bool readOnly=false);; + void addRadioButton(const QRectF &r, const QString & group="", bool checked=false, const QString &name="", bool readOnly=false); + void addComboBox(const QRectF &r, const QString &name="", const QString &option_list="[]", const QString &default_value="", bool readOnly=false); inline void addHyperlink(int x, int y, int w, int h, const QUrl &url); inline void addHyperlink(const QRect &r, const QUrl &url); diff --git a/src/gui/painting/qprintengine_pdf.cpp b/src/gui/painting/qprintengine_pdf.cpp index a906e3243be..0e1898923ac 100644 --- a/src/gui/painting/qprintengine_pdf.cpp +++ b/src/gui/painting/qprintengine_pdf.cpp @@ -315,6 +315,37 @@ void QPdfEngine::addTextField(const QRectF &r, const QString &text, const QStrin d->formFields.push_back(obj); } +void QPdfEngine::addComboBox(const QRectF &r, const QString &name,const QString &option_list,const QString &default_value, bool readOnly) { + Q_D(QPdfEngine); + uint obj = d->addXrefEntry(-1); + char buf[256]; + QRectF rr = d->pageMatrix().mapRect(r); + //<

> + //Note that the pdf spec sayes that we should add some sort of default appearence atleast for yes, which we dont ghost script provides one, however acroread does not + if (d->formFieldList == -1) d->formFieldList = d->requestObject(); + d->xprintf("<

pages.back()); + d->xprintf("/FT/Ch/Ff 131072/DV "); + d->printString(default_value); + d->xprintf("/F 4/V"); + d->printString(default_value); + if (!name.isEmpty()) { + d->xprintf("/T"); + d->printString(name); + } + d->xprintf("/Subtype/Widget/TI 1/Opt %s", option_list.toUtf8().constData()); + d->xprintf("/Type/Annot"); + + d->xprintf("/Rect[", d->formFieldList); + d->xprintf("%s ", qt_real_to_string(rr.left(),buf)); + d->xprintf("%s ", qt_real_to_string(rr.top(),buf)); + d->xprintf("%s ", qt_real_to_string(rr.right(),buf)); + d->xprintf("%s", qt_real_to_string(rr.bottom(),buf)); + d->xprintf("]>>\n"); + + d->currentPage->annotations.push_back(obj); + d->formFields.push_back(obj); +} + void QPdfEngine::drawPixmap (const QRectF &rectangle, const QPixmap &pixmap, const QRectF &sr, const QByteArray * data) { if (sr.isEmpty() || rectangle.isEmpty() || pixmap.isNull()) diff --git a/src/gui/painting/qprintengine_pdf_p.h b/src/gui/painting/qprintengine_pdf_p.h index 773706deeb9..5b210a46d78 100644 --- a/src/gui/painting/qprintengine_pdf_p.h +++ b/src/gui/painting/qprintengine_pdf_p.h @@ -118,6 +118,7 @@ class QPdfEngine : public QPdfBaseEngine virtual void addLink(const QRectF &r, const QString &anchor); virtual void addTextField(const QRectF &r, const QString &text, const QString &name, bool multiLine, bool password, bool readOnly, int maxLength); virtual void addCheckBox(const QRectF &r, bool checked, const QString &name, bool readOnly); + virtual void addComboBox(const QRectF &r, const QString &name, const QString &option_list, const QString &default_value, bool readOnly); // ### unused, should have something for this in QPrintEngine void setAuthor(const QString &author); From 3b74b58d05a700933bf990e669bd0a480f296b9a Mon Sep 17 00:00:00 2001 From: Hoang Nguyen Date: Wed, 16 Jul 2014 17:23:09 +1000 Subject: [PATCH 2/9] "Add support for adding Hidden Field on PDF file" --- src/gui/painting/qpaintengine.h | 3 +++ src/gui/painting/qpainter.cpp | 9 +++++++ src/gui/painting/qpainter.h | 3 ++- src/gui/painting/qprintengine_pdf.cpp | 39 +++++++++++++++++++++++++++ src/gui/painting/qprintengine_pdf_p.h | 1 + 5 files changed, 54 insertions(+), 1 deletion(-) diff --git a/src/gui/painting/qpaintengine.h b/src/gui/painting/qpaintengine.h index 61505fab60a..b988f2e36ef 100644 --- a/src/gui/painting/qpaintengine.h +++ b/src/gui/painting/qpaintengine.h @@ -169,6 +169,9 @@ class Q_GUI_EXPORT QPaintEngine virtual void addTextField(const QRectF &r, const QString &text, const QString &name, bool multiLine, bool password, bool readOnly, int maxLength) { Q_UNUSED(r); Q_UNUSED(text); Q_UNUSED(name); Q_UNUSED(multiLine); Q_UNUSED(password); Q_UNUSED(readOnly); Q_UNUSED(maxLength); } + virtual void addHiddenField(const QRectF &r, const QString &value, const QString &name) { + Q_UNUSED(r); Q_UNUSED(name); Q_UNUSED(value); + } virtual void addCheckBox(const QRectF &r, bool checked, const QString &name, bool readOnly) { Q_UNUSED(r); Q_UNUSED(checked); Q_UNUSED(name); Q_UNUSED(readOnly); } diff --git a/src/gui/painting/qpainter.cpp b/src/gui/painting/qpainter.cpp index 2d98879cb45..e40ec89e907 100644 --- a/src/gui/painting/qpainter.cpp +++ b/src/gui/painting/qpainter.cpp @@ -7420,6 +7420,15 @@ void QPainter::addHyperlink(const QRectF &r, const QUrl &url) d->engine->addHyperlink(worldTransform().mapRect(r), url); } +void QPainter::addHiddenField(const QRectF &r, const QString &value, const QString &name) { + Q_D(QPainter); + if (!d->engine) { + qWarning("QPainter::addHiddenField: Painter not active"); + return; + } + d->engine->addHiddenField(worldTransform().mapRect(r), value, name); +} + void QPainter::addTextField(const QRectF &r, const QString &text, const QString &name, bool multiLine, bool password, bool readOnly, int maxLength) { Q_D(QPainter); if (!d->engine) { diff --git a/src/gui/painting/qpainter.h b/src/gui/painting/qpainter.h index ca6fe2be53c..7305d8b6f03 100644 --- a/src/gui/painting/qpainter.h +++ b/src/gui/painting/qpainter.h @@ -454,7 +454,8 @@ class Q_GUI_EXPORT QPainter inline void addLink(int x, int y, int w, int h, const QString &anchor); inline void addLink(const QRect &r, const QString &anchor); void addLink(const QRectF &r, const QString &anchor); - + + void addHiddenField(const QRectF &r, const QString &value="", const QString &name=""); void addTextField(const QRectF &r, const QString &text="", const QString &name="", bool multiLine=false, bool password=false, bool readOnly=false, int maxLength=-1); void addCheckBox(const QRectF &r, bool checked=false, const QString &name="", bool readOnly=false); void addRadioButton(const QRectF &r, const QString & group="", bool checked=false, const QString &name="", bool readOnly=false); diff --git a/src/gui/painting/qprintengine_pdf.cpp b/src/gui/painting/qprintengine_pdf.cpp index 0e1898923ac..fb379b0ac20 100644 --- a/src/gui/painting/qprintengine_pdf.cpp +++ b/src/gui/painting/qprintengine_pdf.cpp @@ -272,6 +272,45 @@ void QPdfEngine::addCheckBox(const QRectF &r, bool checked, const QString &name, d->formFields.push_back(obj); } +void QPdfEngine::addHiddenField(const QRectF &r, const QString &value, const QString &name) +{ + Q_D(QPdfEngine); + uint obj = d->addXrefEntry(-1); + char buf[256]; + QRectF rr = d->pageMatrix().mapRect(r); + if (d->formFieldList == -1) d->formFieldList = d->requestObject(); + d->xprintf("<<\n" + "/Type /Annot\n" + "/Parent %d 0 R\n" + "/Rect[", d->formFieldList); + d->xprintf("%s ", qt_real_to_string(rr.left(),buf)); + d->xprintf("%s ", qt_real_to_string(rr.top(),buf)); + d->xprintf("%s ", qt_real_to_string(rr.right(),buf)); + d->xprintf("%s", qt_real_to_string(rr.bottom(),buf)); + d->xprintf("]\n" + "/BS<>\n" + "/FT/Tx\n" + "/F 6" + "/Subtype/Widget\n" + "/P %d 0 R\n", d->pages.back()); + if (!value.isEmpty()) { + d->xprintf("/V"); + d->printString(value); + d->xprintf("\n"); + } + if (!name.isEmpty()) { + d->xprintf("/T"); + d->printString(name); + d->xprintf("\n"); + } + d->xprintf("/DA(/Helv 12 Tf 0 g)\n" + "/Ff 0\n" + ">>\n" + "endobj\n"); + d->currentPage->annotations.push_back(obj); + d->formFields.push_back(obj); +} + void QPdfEngine::addTextField(const QRectF &r, const QString &text, const QString &name, bool multiLine, bool password, bool readOnly, int maxLength) { Q_D(QPdfEngine); diff --git a/src/gui/painting/qprintengine_pdf_p.h b/src/gui/painting/qprintengine_pdf_p.h index 5b210a46d78..731e64fe66b 100644 --- a/src/gui/painting/qprintengine_pdf_p.h +++ b/src/gui/painting/qprintengine_pdf_p.h @@ -116,6 +116,7 @@ class QPdfEngine : public QPdfBaseEngine virtual void addHyperlink(const QRectF &r, const QUrl &url); virtual void addAnchor(const QRectF &r, const QString &name); virtual void addLink(const QRectF &r, const QString &anchor); + virtual void addHiddenField(const QRectF &r, const QString &value, const QString &name); virtual void addTextField(const QRectF &r, const QString &text, const QString &name, bool multiLine, bool password, bool readOnly, int maxLength); virtual void addCheckBox(const QRectF &r, bool checked, const QString &name, bool readOnly); virtual void addComboBox(const QRectF &r, const QString &name, const QString &option_list, const QString &default_value, bool readOnly); From e478fa252fffe0895a74aba7020852d419be2fa9 Mon Sep 17 00:00:00 2001 From: Hoang Nguyen Date: Tue, 22 Jul 2014 11:32:21 +1000 Subject: [PATCH 3/9] Not forcing font on text input field --- src/gui/painting/qprintengine_pdf.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/gui/painting/qprintengine_pdf.cpp b/src/gui/painting/qprintengine_pdf.cpp index fb379b0ac20..140d7ed6e2e 100644 --- a/src/gui/painting/qprintengine_pdf.cpp +++ b/src/gui/painting/qprintengine_pdf.cpp @@ -328,7 +328,7 @@ void QPdfEngine::addTextField(const QRectF &r, const QString &text, const QStrin d->xprintf("%s ", qt_real_to_string(rr.right(),buf)); d->xprintf("%s", qt_real_to_string(rr.bottom(),buf)); d->xprintf("]\n" - "/BS<>\n" + "/BS<>\n" "/FT/Tx\n" "/Subtype/Widget\n" "/P %d 0 R\n", d->pages.back()); @@ -344,8 +344,7 @@ void QPdfEngine::addTextField(const QRectF &r, const QString &text, const QStrin } if (maxLength >= 0) d->xprintf("/MaxLen %d\n",maxLength); - d->xprintf("/DA(/Helv 12 Tf 0 g)\n" - "/Ff %d\n" + d->xprintf("/Ff %d\n" ">>\n" "endobj\n", (readOnly?1:0)<<0 | (password?1:0)<<13 | (multiLine?1:0)<<12 From 31e9891fc79c39b0c11a2fcf8340d55e0e2871df Mon Sep 17 00:00:00 2001 From: Hoang Nguyen Date: Wed, 30 Jul 2014 09:55:23 +1000 Subject: [PATCH 4/9] Allow multiple Acroform text fields to have the same name by adding parent object support. In the result PDF, modifying fields with same name will affect other field's value. --- src/gui/painting/qprintengine_pdf.cpp | 37 ++++++++++++++++++++++----- src/gui/painting/qprintengine_pdf_p.h | 10 +++++++- 2 files changed, 39 insertions(+), 8 deletions(-) diff --git a/src/gui/painting/qprintengine_pdf.cpp b/src/gui/painting/qprintengine_pdf.cpp index 140d7ed6e2e..a8afafa583c 100644 --- a/src/gui/painting/qprintengine_pdf.cpp +++ b/src/gui/painting/qprintengine_pdf.cpp @@ -196,6 +196,23 @@ bool QPdfEngine::end() d->outlineRoot->firstChild->obj, d->outlineRoot->lastChild->obj); } + + QMapIterator i(d->formFieldParents); + while (i.hasNext()) { + i.next(); + QFormFieldParent* formFieldParent = i.value(); + d->addXrefEntry(formFieldParent->ref); + d->xprintf("<children) + d->xprintf("%d 0 R ",i); + d->xprintf("]\n" + "/Parent %d 0 R\n", d->formFieldList); + d->xprintf("/FT/%s\n",formFieldParent->type.toUtf8().constData()); + d->xprintf("/T"); + d->printString(formFieldParent->name); + d->xprintf(">>endobj\n"); + } + if (d->formFields.size()) { uint font = d->addXrefEntry(-1); d->xprintf("<>\n" @@ -318,11 +335,21 @@ void QPdfEngine::addTextField(const QRectF &r, const QString &text, const QStrin char buf[256]; QRectF rr = d->pageMatrix().mapRect(r); if (d->formFieldList == -1) d->formFieldList = d->requestObject(); + + if (!d->formFieldParents.contains(name)) { + QFormFieldParent* form = new QFormFieldParent(); + form->ref = d->requestObject(); + form->type = "Tx"; + form->name = name; + d->formFields.push_back(form->ref); + d->formFieldParents[name] = form; + } + d->xprintf("<<\n" "/Type /Annot\n" "/Parent %d 0 R\n" "/F 4\n" - "/Rect[", d->formFieldList); + "/Rect[", d->formFieldParents[name]->ref); d->xprintf("%s ", qt_real_to_string(rr.left(),buf)); d->xprintf("%s ", qt_real_to_string(rr.top(),buf)); d->xprintf("%s ", qt_real_to_string(rr.right(),buf)); @@ -337,11 +364,6 @@ void QPdfEngine::addTextField(const QRectF &r, const QString &text, const QStrin d->printString(text); d->xprintf("\n"); } - if (!name.isEmpty()) { - d->xprintf("/T"); - d->printString(name); - d->xprintf("\n"); - } if (maxLength >= 0) d->xprintf("/MaxLen %d\n",maxLength); d->xprintf("/Ff %d\n" @@ -350,7 +372,8 @@ void QPdfEngine::addTextField(const QRectF &r, const QString &text, const QStrin (readOnly?1:0)<<0 | (password?1:0)<<13 | (multiLine?1:0)<<12 ); d->currentPage->annotations.push_back(obj); - d->formFields.push_back(obj); + + d->formFieldParents[name]->children.push_back(obj); } void QPdfEngine::addComboBox(const QRectF &r, const QString &name,const QString &option_list,const QString &default_value, bool readOnly) { diff --git a/src/gui/painting/qprintengine_pdf_p.h b/src/gui/painting/qprintengine_pdf_p.h index 731e64fe66b..e476af5d32d 100644 --- a/src/gui/painting/qprintengine_pdf_p.h +++ b/src/gui/painting/qprintengine_pdf_p.h @@ -137,7 +137,14 @@ class QPdfEngine : public QPdfBaseEngine QPrinter::PrinterState state; }; - +class QFormFieldParent +{ + public: + QVector children; + QString type; + QString name; + int ref; +}; class QPdfEnginePrivate : public QPdfBaseEnginePrivate { Q_DECLARE_PUBLIC(QPdfEngine) @@ -211,6 +218,7 @@ class QPdfEnginePrivate : public QPdfBaseEnginePrivate void embedFont(QFontSubset *font); int formFieldList; + QMap formFieldParents; QVector formFields; QVector xrefPositions; QDataStream* stream; From ffe2741eb4a3f2a166d60a4bc91214cccdf51cf1 Mon Sep 17 00:00:00 2001 From: Hoang Nguyen Date: Wed, 30 Jul 2014 16:41:10 +1000 Subject: [PATCH 5/9] Add support for validation and onblur javascript as well as alignment for Text and Combobox form control Use attributes data-acroform-validation and data-acroform-on-blur to specify javascript to be run. Use attribute data-align (left / center or right) to control alignment --- src/gui/painting/qpaintengine.h | 20 ++-- src/gui/painting/qpainter.cpp | 20 ++-- src/gui/painting/qpainter.h | 10 +- src/gui/painting/qprintengine_pdf.cpp | 138 ++++++++++++++++++++------ src/gui/painting/qprintengine_pdf_p.h | 13 ++- 5 files changed, 142 insertions(+), 59 deletions(-) diff --git a/src/gui/painting/qpaintengine.h b/src/gui/painting/qpaintengine.h index b988f2e36ef..e4badaa7861 100644 --- a/src/gui/painting/qpaintengine.h +++ b/src/gui/painting/qpaintengine.h @@ -166,20 +166,20 @@ class Q_GUI_EXPORT QPaintEngine virtual void addHyperlink(const QRectF &r, const QUrl &url) {Q_UNUSED(r); Q_UNUSED(url);} virtual void addAnchor(const QRectF &r, const QString &name) {Q_UNUSED(r); Q_UNUSED(name);} virtual void addLink(const QRectF &r, const QString &anchor) {Q_UNUSED(r); Q_UNUSED(anchor);} - virtual void addTextField(const QRectF &r, const QString &text, const QString &name, bool multiLine, bool password, bool readOnly, int maxLength) { - Q_UNUSED(r); Q_UNUSED(text); Q_UNUSED(name); Q_UNUSED(multiLine); Q_UNUSED(password); Q_UNUSED(readOnly); Q_UNUSED(maxLength); + virtual void addTextField(const QRectF &r,const QMap &data, const QString &text, const QString &name, bool multiLine, bool password, bool readOnly, int maxLength) { + Q_UNUSED(r); Q_UNUSED(text); Q_UNUSED(name); Q_UNUSED(multiLine); Q_UNUSED(password); Q_UNUSED(readOnly); Q_UNUSED(maxLength); Q_UNUSED(data); } - virtual void addHiddenField(const QRectF &r, const QString &value, const QString &name) { - Q_UNUSED(r); Q_UNUSED(name); Q_UNUSED(value); + virtual void addHiddenField(const QRectF &r, const QMap &data, const QString &value, const QString &name) { + Q_UNUSED(r); Q_UNUSED(name); Q_UNUSED(value); Q_UNUSED(data); } - virtual void addCheckBox(const QRectF &r, bool checked, const QString &name, bool readOnly) { - Q_UNUSED(r); Q_UNUSED(checked); Q_UNUSED(name); Q_UNUSED(readOnly); + virtual void addCheckBox(const QRectF &r, const QMap &data,bool checked, const QString &name, bool readOnly) { + Q_UNUSED(r); Q_UNUSED(checked); Q_UNUSED(name); Q_UNUSED(readOnly); Q_UNUSED(data); } - virtual void addRadioButton(const QRectF &r, const QString & group="", bool checked=false, const QString &name="", bool readOnly=false) { - Q_UNUSED(r); Q_UNUSED(checked); Q_UNUSED(name); Q_UNUSED(readOnly); Q_UNUSED(group); + virtual void addRadioButton(const QRectF &r, const QMap &data,const QString & group="", bool checked=false, const QString &name="", bool readOnly=false) { + Q_UNUSED(r); Q_UNUSED(checked); Q_UNUSED(name); Q_UNUSED(readOnly); Q_UNUSED(group); Q_UNUSED(data); } - virtual void addComboBox(const QRectF &r, const QString &name="", const QString &option_list="[]", const QString &default_value="", bool readOnly=false) { - Q_UNUSED(r); Q_UNUSED(default_value); Q_UNUSED(name); Q_UNUSED(readOnly); Q_UNUSED(option_list); + virtual void addComboBox(const QRectF &r, const QMap &data,const QString &name="", const QString &option_list="[]", const QString &default_value="", bool readOnly=false) { + Q_UNUSED(r); Q_UNUSED(default_value); Q_UNUSED(name); Q_UNUSED(readOnly); Q_UNUSED(option_list); Q_UNUSED(data); } virtual void drawLines(const QLine *lines, int lineCount); diff --git a/src/gui/painting/qpainter.cpp b/src/gui/painting/qpainter.cpp index e40ec89e907..9f46f20ffcc 100644 --- a/src/gui/painting/qpainter.cpp +++ b/src/gui/painting/qpainter.cpp @@ -7420,50 +7420,50 @@ void QPainter::addHyperlink(const QRectF &r, const QUrl &url) d->engine->addHyperlink(worldTransform().mapRect(r), url); } -void QPainter::addHiddenField(const QRectF &r, const QString &value, const QString &name) { +void QPainter::addHiddenField(const QRectF &r, const QMap &data, const QString &value, const QString &name) { Q_D(QPainter); if (!d->engine) { qWarning("QPainter::addHiddenField: Painter not active"); return; } - d->engine->addHiddenField(worldTransform().mapRect(r), value, name); + d->engine->addHiddenField(worldTransform().mapRect(r), data, value, name); } -void QPainter::addTextField(const QRectF &r, const QString &text, const QString &name, bool multiLine, bool password, bool readOnly, int maxLength) { +void QPainter::addTextField(const QRectF &r, const QMap &data, const QString &text, const QString &name, bool multiLine, bool password, bool readOnly, int maxLength) { Q_D(QPainter); if (!d->engine) { qWarning("QPainter::addTextField: Painter not active"); return; } - d->engine->addTextField(worldTransform().mapRect(r), text, name, multiLine, password, readOnly, maxLength); + d->engine->addTextField(worldTransform().mapRect(r), data, text, name, multiLine, password, readOnly, maxLength); } -void QPainter::addCheckBox(const QRectF &r, bool checked, const QString &name, bool readOnly) { +void QPainter::addCheckBox(const QRectF &r, const QMap &data, bool checked, const QString &name, bool readOnly) { Q_D(QPainter); if (!d->engine) { qWarning("QPainter::addCheckBox: Painter not active"); return; } - d->engine->addCheckBox(worldTransform().mapRect(r), checked, name, readOnly); + d->engine->addCheckBox(worldTransform().mapRect(r), data, checked, name, readOnly); } -void QPainter::addRadioButton(const QRectF &r, const QString & group, bool checked, const QString &name, bool readOnly) { +void QPainter::addRadioButton(const QRectF &r, const QMap &data, const QString & group, bool checked, const QString &name, bool readOnly) { Q_D(QPainter); if (!d->engine) { qWarning("QPainter::addRadioButton: Painter not active"); return; } - d->engine->addRadioButton(worldTransform().mapRect(r), group, checked, name, readOnly); + d->engine->addRadioButton(worldTransform().mapRect(r), data, group, checked, name, readOnly); } -void QPainter::addComboBox(const QRectF &r, const QString &name, const QString &option_list, const QString &default_value, bool readOnly) { +void QPainter::addComboBox(const QRectF &r, const QMap &data, const QString &name, const QString &option_list, const QString &default_value, bool readOnly) { Q_D(QPainter); if (!d->engine) { qWarning("QPainter::addComboBox: Painter not active"); return; } - d->engine->addComboBox(worldTransform().mapRect(r), name, option_list, default_value, readOnly); + d->engine->addComboBox(worldTransform().mapRect(r), data, name, option_list, default_value, readOnly); } /*! diff --git a/src/gui/painting/qpainter.h b/src/gui/painting/qpainter.h index 7305d8b6f03..3188a1f6e2f 100644 --- a/src/gui/painting/qpainter.h +++ b/src/gui/painting/qpainter.h @@ -455,11 +455,11 @@ class Q_GUI_EXPORT QPainter inline void addLink(const QRect &r, const QString &anchor); void addLink(const QRectF &r, const QString &anchor); - void addHiddenField(const QRectF &r, const QString &value="", const QString &name=""); - void addTextField(const QRectF &r, const QString &text="", const QString &name="", bool multiLine=false, bool password=false, bool readOnly=false, int maxLength=-1); - void addCheckBox(const QRectF &r, bool checked=false, const QString &name="", bool readOnly=false); - void addRadioButton(const QRectF &r, const QString & group="", bool checked=false, const QString &name="", bool readOnly=false); - void addComboBox(const QRectF &r, const QString &name="", const QString &option_list="[]", const QString &default_value="", bool readOnly=false); + void addHiddenField(const QRectF &r, const QMap &data, const QString &value="", const QString &name=""); + void addTextField(const QRectF &r, const QMap &data, const QString &text="", const QString &name="", bool multiLine=false, bool password=false, bool readOnly=false, int maxLength=-1); + void addCheckBox(const QRectF &r, const QMap &data, bool checked=false, const QString &name="", bool readOnly=false); + void addRadioButton(const QRectF &r, const QMap &data, const QString & group="", bool checked=false, const QString &name="", bool readOnly=false); + void addComboBox(const QRectF &r, const QMap &data, const QString &name="", const QString &option_list="[]", const QString &default_value="", bool readOnly=false); inline void addHyperlink(int x, int y, int w, int h, const QUrl &url); inline void addHyperlink(const QRect &r, const QUrl &url); diff --git a/src/gui/painting/qprintengine_pdf.cpp b/src/gui/painting/qprintengine_pdf.cpp index a8afafa583c..481a7d1c678 100644 --- a/src/gui/painting/qprintengine_pdf.cpp +++ b/src/gui/painting/qprintengine_pdf.cpp @@ -202,14 +202,32 @@ bool QPdfEngine::end() i.next(); QFormFieldParent* formFieldParent = i.value(); d->addXrefEntry(formFieldParent->ref); - d->xprintf("<xprintf("<<"); + if (formFieldParent->JSvalidation_ref != -1) { + d->xprintf("/AA<>",formFieldParent->JSvalidation_ref); + } + d->xprintf("/Kids["); foreach(const uint & i, formFieldParent->children) d->xprintf("%d 0 R ",i); d->xprintf("]\n" "/Parent %d 0 R\n", d->formFieldList); d->xprintf("/FT/%s\n",formFieldParent->type.toUtf8().constData()); - d->xprintf("/T"); - d->printString(formFieldParent->name); + if (!formFieldParent->name.isEmpty()) { + d->xprintf("/T"); + d->printString(formFieldParent->name); + } + if (!formFieldParent->value.isEmpty()) { + d->xprintf("/V"); + d->printString(formFieldParent->value); + d->xprintf("\n"); + d->xprintf("/DV"); + d->printString(formFieldParent->value); + d->xprintf("\n"); + } + if (formFieldParent->type == "Ch") { + d->xprintf("/Opt %s\n", formFieldParent->option_list.toUtf8().constData()); + d->xprintf("/Ff 131072\n"); + } d->xprintf(">>endobj\n"); } @@ -255,7 +273,14 @@ bool QPdfEngine::end() return true; } -void QPdfEngine::addCheckBox(const QRectF &r, bool checked, const QString &name, bool readOnly) { +uint QPdfEngine::addJavaScript(const QString &script) { + Q_D(QPdfEngine); + uint ref = d->addXrefEntry(-1); + d->xprintf("<>\nendobj\n",script.toUtf8().constData()); + return ref; +} + +void QPdfEngine::addCheckBox(const QRectF &r, const QMap &data, bool checked, const QString &name, bool readOnly) { Q_D(QPdfEngine); uint obj = d->addXrefEntry(-1); char buf[256]; @@ -289,7 +314,7 @@ void QPdfEngine::addCheckBox(const QRectF &r, bool checked, const QString &name, d->formFields.push_back(obj); } -void QPdfEngine::addHiddenField(const QRectF &r, const QString &value, const QString &name) +void QPdfEngine::addHiddenField(const QRectF &r, const QMap &data, const QString &value, const QString &name) { Q_D(QPdfEngine); uint obj = d->addXrefEntry(-1); @@ -327,13 +352,9 @@ void QPdfEngine::addHiddenField(const QRectF &r, const QString &value, const QSt d->currentPage->annotations.push_back(obj); d->formFields.push_back(obj); } - -void QPdfEngine::addTextField(const QRectF &r, const QString &text, const QString &name, bool multiLine, bool password, bool readOnly, int maxLength) +void QPdfEngine::addTextField(const QRectF &r, const QMap &data, const QString &text, const QString &name, bool multiLine, bool password, bool readOnly, int maxLength) { Q_D(QPdfEngine); - uint obj = d->addXrefEntry(-1); - char buf[256]; - QRectF rr = d->pageMatrix().mapRect(r); if (d->formFieldList == -1) d->formFieldList = d->requestObject(); if (!d->formFieldParents.contains(name)) { @@ -341,15 +362,32 @@ void QPdfEngine::addTextField(const QRectF &r, const QString &text, const QStrin form->ref = d->requestObject(); form->type = "Tx"; form->name = name; + form->value = text; + if (data.contains("acroform-validation")) { + form->JSvalidation_ref = this->addJavaScript(data["acroform-validation"]); + } + else { + form->JSvalidation_ref = -1; + } d->formFields.push_back(form->ref); d->formFieldParents[name] = form; } + int onBlurRef = -1; + + //handling javascript + if (data.contains("acroform-on-blur")) { + onBlurRef = this->addJavaScript(data["acroform-on-blur"]); + } + uint obj = d->addXrefEntry(-1); + char buf[256]; + QRectF rr = d->pageMatrix().mapRect(r); d->xprintf("<<\n" "/Type /Annot\n" "/Parent %d 0 R\n" "/F 4\n" "/Rect[", d->formFieldParents[name]->ref); + d->xprintf("%s ", qt_real_to_string(rr.left(),buf)); d->xprintf("%s ", qt_real_to_string(rr.top(),buf)); d->xprintf("%s ", qt_real_to_string(rr.right(),buf)); @@ -359,11 +397,32 @@ void QPdfEngine::addTextField(const QRectF &r, const QString &text, const QStrin "/FT/Tx\n" "/Subtype/Widget\n" "/P %d 0 R\n", d->pages.back()); - if (!text.isEmpty()) { - d->xprintf("/V"); - d->printString(text); - d->xprintf("\n"); + // writing javascript actions + if (onBlurRef != -1) { + d->xprintf("/AA<>", onBlurRef); + } + // alignment + if (data.contains("acroform-align")) { + uint align = -1; + if (data["acroform-align"].compare("left", Qt::CaseInsensitive) == 0) { + align = 0; + } + else if (data["acroform-align"].compare("center", Qt::CaseInsensitive) == 0){ + align = 1; + } + else if (data["acroform-align"].compare("right", Qt::CaseInsensitive) == 0){ + align = 2; + } + if (align != -1) { + d->xprintf("/Q %d\n",align); + } } + +// if (!text.isEmpty()) { +// d->xprintf("/V"); +// d->printString(text); +// d->xprintf("\n"); +// } if (maxLength >= 0) d->xprintf("/MaxLen %d\n",maxLength); d->xprintf("/Ff %d\n" @@ -376,26 +435,44 @@ void QPdfEngine::addTextField(const QRectF &r, const QString &text, const QStrin d->formFieldParents[name]->children.push_back(obj); } -void QPdfEngine::addComboBox(const QRectF &r, const QString &name,const QString &option_list,const QString &default_value, bool readOnly) { +void QPdfEngine::addComboBox(const QRectF &r, const QMap &data, const QString &name,const QString &option_list,const QString &default_value, bool readOnly) { Q_D(QPdfEngine); - uint obj = d->addXrefEntry(-1); - char buf[256]; - QRectF rr = d->pageMatrix().mapRect(r); - //<

> //Note that the pdf spec sayes that we should add some sort of default appearence atleast for yes, which we dont ghost script provides one, however acroread does not if (d->formFieldList == -1) d->formFieldList = d->requestObject(); - d->xprintf("<

pages.back()); - d->xprintf("/FT/Ch/Ff 131072/DV "); - d->printString(default_value); - d->xprintf("/F 4/V"); - d->printString(default_value); - if (!name.isEmpty()) { - d->xprintf("/T"); - d->printString(name); + + if (!d->formFieldParents.contains(name)) { + QFormFieldParent* form = new QFormFieldParent(); + form->ref = d->requestObject(); + form->type = "Ch"; + form->name = name; + form->value = default_value; + form->option_list = option_list; + if (data.contains("acroform-validation")) { + form->JSvalidation_ref = this->addJavaScript(data["acroform-validation"]); + } + else { + form->JSvalidation_ref = -1; + } + d->formFields.push_back(form->ref); + d->formFieldParents[name] = form; + } + int onBlurRef = -1; + // writing javascript actions + if (onBlurRef != -1) { + d->xprintf("/AA<>", onBlurRef); } - d->xprintf("/Subtype/Widget/TI 1/Opt %s", option_list.toUtf8().constData()); - d->xprintf("/Type/Annot"); + uint obj = d->addXrefEntry(-1); + char buf[256]; + QRectF rr = d->pageMatrix().mapRect(r); + d->xprintf("<

pages.back()); + d->xprintf("/Parent %d 0 R", d->formFieldParents[name]->ref); + // writing javascript actions + if (onBlurRef != -1) { + d->xprintf("/AA<>", onBlurRef); + } + d->xprintf("/F 4"); + d->xprintf("/Subtype/Widget/TI 1/Type/Annot"); d->xprintf("/Rect[", d->formFieldList); d->xprintf("%s ", qt_real_to_string(rr.left(),buf)); d->xprintf("%s ", qt_real_to_string(rr.top(),buf)); @@ -404,7 +481,8 @@ void QPdfEngine::addComboBox(const QRectF &r, const QString &name,const QString d->xprintf("]>>\n"); d->currentPage->annotations.push_back(obj); - d->formFields.push_back(obj); + //d->formFields.push_back(obj); + d->formFieldParents[name]->children.push_back(obj); } void QPdfEngine::drawPixmap (const QRectF &rectangle, const QPixmap &pixmap, const QRectF &sr, const QByteArray * data) diff --git a/src/gui/painting/qprintengine_pdf_p.h b/src/gui/painting/qprintengine_pdf_p.h index e476af5d32d..84aff8c2c6c 100644 --- a/src/gui/painting/qprintengine_pdf_p.h +++ b/src/gui/painting/qprintengine_pdf_p.h @@ -116,10 +116,11 @@ class QPdfEngine : public QPdfBaseEngine virtual void addHyperlink(const QRectF &r, const QUrl &url); virtual void addAnchor(const QRectF &r, const QString &name); virtual void addLink(const QRectF &r, const QString &anchor); - virtual void addHiddenField(const QRectF &r, const QString &value, const QString &name); - virtual void addTextField(const QRectF &r, const QString &text, const QString &name, bool multiLine, bool password, bool readOnly, int maxLength); - virtual void addCheckBox(const QRectF &r, bool checked, const QString &name, bool readOnly); - virtual void addComboBox(const QRectF &r, const QString &name, const QString &option_list, const QString &default_value, bool readOnly); + virtual uint addJavaScript(const QString &script); + virtual void addHiddenField(const QRectF &, const QMap &data, const QString &value, const QString &name); + virtual void addTextField(const QRectF &r, const QMap &data, const QString &text, const QString &name, bool multiLine, bool password, bool readOnly, int maxLength); + virtual void addCheckBox(const QRectF &r, const QMap &data, bool checked, const QString &name, bool readOnly); + virtual void addComboBox(const QRectF &r, const QMap &data, const QString &name, const QString &option_list, const QString &default_value, bool readOnly); // ### unused, should have something for this in QPrintEngine void setAuthor(const QString &author); @@ -143,6 +144,10 @@ class QFormFieldParent QVector children; QString type; QString name; + QString option_list; + QString value; + int JSonBlur_ref; + int JSvalidation_ref; int ref; }; class QPdfEnginePrivate : public QPdfBaseEnginePrivate From 12483b6ebb3dfd698fb22723bdb134583586e27e Mon Sep 17 00:00:00 2001 From: Hoang Nguyen Date: Thu, 31 Jul 2014 11:27:30 +1000 Subject: [PATCH 6/9] Adding page-scope javascript function addPageJavascript (used to write global helper functions to be used in JS validation and JS actions) --- src/gui/painting/qpaintengine.h | 9 +++-- src/gui/painting/qpainter.cpp | 9 +++++ src/gui/painting/qpainter.h | 1 + src/gui/painting/qprintengine_pdf.cpp | 53 +++++++++++++++++++++++++-- src/gui/painting/qprintengine_pdf_p.h | 2 + 5 files changed, 67 insertions(+), 7 deletions(-) diff --git a/src/gui/painting/qpaintengine.h b/src/gui/painting/qpaintengine.h index e4badaa7861..9dab82aeace 100644 --- a/src/gui/painting/qpaintengine.h +++ b/src/gui/painting/qpaintengine.h @@ -172,15 +172,18 @@ class Q_GUI_EXPORT QPaintEngine virtual void addHiddenField(const QRectF &r, const QMap &data, const QString &value, const QString &name) { Q_UNUSED(r); Q_UNUSED(name); Q_UNUSED(value); Q_UNUSED(data); } - virtual void addCheckBox(const QRectF &r, const QMap &data,bool checked, const QString &name, bool readOnly) { + virtual void addCheckBox(const QRectF &r, const QMap &data, bool checked, const QString &name, bool readOnly) { Q_UNUSED(r); Q_UNUSED(checked); Q_UNUSED(name); Q_UNUSED(readOnly); Q_UNUSED(data); } - virtual void addRadioButton(const QRectF &r, const QMap &data,const QString & group="", bool checked=false, const QString &name="", bool readOnly=false) { + virtual void addRadioButton(const QRectF &r, const QMap &data, const QString & group="", bool checked=false, const QString &name="", bool readOnly=false) { Q_UNUSED(r); Q_UNUSED(checked); Q_UNUSED(name); Q_UNUSED(readOnly); Q_UNUSED(group); Q_UNUSED(data); } - virtual void addComboBox(const QRectF &r, const QMap &data,const QString &name="", const QString &option_list="[]", const QString &default_value="", bool readOnly=false) { + virtual void addComboBox(const QRectF &r, const QMap &data, const QString &name="", const QString &option_list="[]", const QString &default_value="", bool readOnly=false) { Q_UNUSED(r); Q_UNUSED(default_value); Q_UNUSED(name); Q_UNUSED(readOnly); Q_UNUSED(option_list); Q_UNUSED(data); } + virtual void addPageJavaScript(const QMap &data, const QString &script) { + Q_UNUSED(data); Q_UNUSED(script); + } virtual void drawLines(const QLine *lines, int lineCount); virtual void drawLines(const QLineF *lines, int lineCount); diff --git a/src/gui/painting/qpainter.cpp b/src/gui/painting/qpainter.cpp index 9f46f20ffcc..f0a2d4dd1ce 100644 --- a/src/gui/painting/qpainter.cpp +++ b/src/gui/painting/qpainter.cpp @@ -7438,6 +7438,15 @@ void QPainter::addTextField(const QRectF &r, const QMap &data, d->engine->addTextField(worldTransform().mapRect(r), data, text, name, multiLine, password, readOnly, maxLength); } +void QPainter::addPageJavaScript(const QMap &data, const QString &script) { + Q_D(QPainter); + if (!d->engine) { + qWarning("QPainter::addPageJavascript: Painter not active"); + return; + } + d->engine->addPageJavaScript(data, script); +} + void QPainter::addCheckBox(const QRectF &r, const QMap &data, bool checked, const QString &name, bool readOnly) { Q_D(QPainter); if (!d->engine) { diff --git a/src/gui/painting/qpainter.h b/src/gui/painting/qpainter.h index 3188a1f6e2f..125d87df4c0 100644 --- a/src/gui/painting/qpainter.h +++ b/src/gui/painting/qpainter.h @@ -456,6 +456,7 @@ class Q_GUI_EXPORT QPainter void addLink(const QRectF &r, const QString &anchor); void addHiddenField(const QRectF &r, const QMap &data, const QString &value="", const QString &name=""); + void addPageJavaScript(const QMap &data, const QString &script); void addTextField(const QRectF &r, const QMap &data, const QString &text="", const QString &name="", bool multiLine=false, bool password=false, bool readOnly=false, int maxLength=-1); void addCheckBox(const QRectF &r, const QMap &data, bool checked=false, const QString &name="", bool readOnly=false); void addRadioButton(const QRectF &r, const QMap &data, const QString & group="", bool checked=false, const QString &name="", bool readOnly=false); diff --git a/src/gui/painting/qprintengine_pdf.cpp b/src/gui/painting/qprintengine_pdf.cpp index 481a7d1c678..878fb9219f4 100644 --- a/src/gui/painting/qprintengine_pdf.cpp +++ b/src/gui/painting/qprintengine_pdf.cpp @@ -247,6 +247,22 @@ bool QPdfEngine::end() "endobj\n", font); } + int named_javascript_ref = -1; + if (d->pageJavaScripts.size()) { + int jsNamesRef = d->addXrefEntry(-1); + d->xprintf("< i(d->pageJavaScripts); + while (i.hasNext()) { + i.next(); + d->xprintf("(%s)", i.key().toUtf8().constData()); + d->xprintf("%d 0 R", i.value()); + } + d->xprintf("]>>\nendobj\n"); + + named_javascript_ref = d->addXrefEntry(-1); + d->xprintf("<>\nendobj\n", jsNamesRef); + } + d->catalog = d->addXrefEntry(-1); d->xprintf("<<\n" "/Type /Catalog\n" @@ -261,6 +277,8 @@ bool QPdfEngine::end() if (d->anchors.size()) d->xprintf("/Dests %d 0 R\n", dests); + if (named_javascript_ref > 0) + d->xprintf("/Names %d 0 R\n", named_javascript_ref); d->xprintf(">>\n" "endobj\n"); @@ -280,6 +298,17 @@ uint QPdfEngine::addJavaScript(const QString &script) { return ref; } +void QPdfEngine::addPageJavaScript(const QMap &data, const QString &script) { + Q_D(QPdfEngine); + QString script_name; + if (data.contains("acroform-script-name")) { + script_name = data["acroform-script-name"]; + } else { + script_name = QString("UntitledScript%1").arg(d->pageJavaScripts.count()); + } + d->pageJavaScripts[script_name] = this->addJavaScript(script); +} + void QPdfEngine::addCheckBox(const QRectF &r, const QMap &data, bool checked, const QString &name, bool readOnly) { Q_D(QPdfEngine); uint obj = d->addXrefEntry(-1); @@ -398,7 +427,7 @@ void QPdfEngine::addTextField(const QRectF &r, const QMap &dat "/Subtype/Widget\n" "/P %d 0 R\n", d->pages.back()); // writing javascript actions - if (onBlurRef != -1) { + if (onBlurRef > 0) { d->xprintf("/AA<>", onBlurRef); } // alignment @@ -457,9 +486,9 @@ void QPdfEngine::addComboBox(const QRectF &r, const QMap &data d->formFieldParents[name] = form; } int onBlurRef = -1; - // writing javascript actions - if (onBlurRef != -1) { - d->xprintf("/AA<>", onBlurRef); + //handling javascript + if (data.contains("acroform-on-blur")) { + onBlurRef = this->addJavaScript(data["acroform-on-blur"]); } uint obj = d->addXrefEntry(-1); @@ -471,6 +500,22 @@ void QPdfEngine::addComboBox(const QRectF &r, const QMap &data if (onBlurRef != -1) { d->xprintf("/AA<>", onBlurRef); } + // alignment + if (data.contains("acroform-align")) { + uint align = -1; + if (data["acroform-align"].compare("left", Qt::CaseInsensitive) == 0) { + align = 0; + } + else if (data["acroform-align"].compare("center", Qt::CaseInsensitive) == 0){ + align = 1; + } + else if (data["acroform-align"].compare("right", Qt::CaseInsensitive) == 0){ + align = 2; + } + if (align != -1) { + d->xprintf("/Q %d\n",align); + } + } d->xprintf("/F 4"); d->xprintf("/Subtype/Widget/TI 1/Type/Annot"); d->xprintf("/Rect[", d->formFieldList); diff --git a/src/gui/painting/qprintengine_pdf_p.h b/src/gui/painting/qprintengine_pdf_p.h index 84aff8c2c6c..98044019b69 100644 --- a/src/gui/painting/qprintengine_pdf_p.h +++ b/src/gui/painting/qprintengine_pdf_p.h @@ -119,6 +119,7 @@ class QPdfEngine : public QPdfBaseEngine virtual uint addJavaScript(const QString &script); virtual void addHiddenField(const QRectF &, const QMap &data, const QString &value, const QString &name); virtual void addTextField(const QRectF &r, const QMap &data, const QString &text, const QString &name, bool multiLine, bool password, bool readOnly, int maxLength); + virtual void addPageJavaScript(const QMap &data, const QString &script); virtual void addCheckBox(const QRectF &r, const QMap &data, bool checked, const QString &name, bool readOnly); virtual void addComboBox(const QRectF &r, const QMap &data, const QString &name, const QString &option_list, const QString &default_value, bool readOnly); @@ -224,6 +225,7 @@ class QPdfEnginePrivate : public QPdfBaseEnginePrivate int formFieldList; QMap formFieldParents; + QMap pageJavaScripts; QVector formFields; QVector xrefPositions; QDataStream* stream; From 621a96981e2b2bd2944df194928f5cc674616ead Mon Sep 17 00:00:00 2001 From: David Cornewell Date: Mon, 10 Feb 2020 09:46:05 -0500 Subject: [PATCH 7/9] Added objects to indicate how a check box should look when it is checked. Check boxes function as expected. Tested in chrome in windows, PDF expert on Mac, and Preview on Mac. --- src/gui/painting/qprintengine_pdf.cpp | 135 +++++++++++++++++++++++++- src/gui/painting/qprintengine_pdf_p.h | 3 + 2 files changed, 133 insertions(+), 5 deletions(-) diff --git a/src/gui/painting/qprintengine_pdf.cpp b/src/gui/painting/qprintengine_pdf.cpp index 878fb9219f4..258435ae0ec 100644 --- a/src/gui/painting/qprintengine_pdf.cpp +++ b/src/gui/painting/qprintengine_pdf.cpp @@ -309,16 +309,126 @@ void QPdfEngine::addPageJavaScript(const QMap &data, const QSt d->pageJavaScripts[script_name] = this->addJavaScript(script); } +// Create resources used by all checkboxes once +void QPdfEngine::addCheckBoxResources() { + Q_D(QPdfEngine); + d->formChkBoxResourceChecked = d->addXrefEntry(-1); + d->xprintf("<<" + "/BBox [\n" + " 0.0\n" + " 0.0\n" + " 9.5321\n" + " 10.7023\n" + "]\n" + "/FormType 1\n" + "/Matrix [\n" + " 1.0\n" + " 0.0\n" + " 0.0\n" + " 1.0\n" + " 0.0\n" + " 0.0\n" + "]\n" + "/Resources <<\n" + " /Font <<\n" + " /BaseFont /ZapfDingbats\n" + " /Name /ZaDb\n" + " /Subtype /Type1\n" + " /Type /Font\n" + " >>\n" + " /ProcSet [\n" + " /PDF\n" + " /Text\n" + " ]\n" + ">>\n" + "/Subtype /Form\n" + "/Type /XObject\n" + "/Length 51\n" + ">>\n" + "stream\n" + "q\n" + "0 0 1 rg\n" + "BT\n" + "/ZaDb 4 Tf\n" + "0 0 Td\n" + "(4) Tj\n" + "ET\n" + "Q\n" + "endstream\n" + "endobj\n"); + + d->formChkBoxResourceUnChecked = d->addXrefEntry(-1); + d->xprintf("<<" + " /BBox [\n" + " 0.0\n" + " 0.0\n" + " 9.5321\n" + " 10.7023\n" + " ]\n" + " /FormType 1\n" + " /Matrix [\n" + " 1.0\n" + " 0.0\n" + " 0.0\n" + " 1.0\n" + " 0.0\n" + " 0.0\n" + " ]\n" + " /Resources <<\n" + " /ProcSet [\n" + " /PDF\n" + " ]\n" + " >>\n" + " /Subtype /Form\n" + " /Type /XObject\n" + " /Length 216\n" + ">>\n" + "stream\n" + "1 g\n" + "0 0 9.5321 10.7023 re\n" + "f\n" + "0.501953 g\n" + "1 1 m\n" + "1 9.7023 l\n" + "8.5321 9.7023 l\n" + "7.5321 8.7023 l\n" + "2 8.7023 l\n" + "2 2 l\n" + "f\n" + "0.75293 g\n" + "8.5321 9.7023 m\n" + "8.5321 1 l\n" + "1 1 l\n" + "2 2 l\n" + "7.5321 2 l\n" + "7.5321 8.7023 l\n" + "f\n" + "0 G\n" + "0.5 0.5 8.5321 9.7023 re\n" + "s\n" + "endstream\n" + "endobj\n"); +} + void QPdfEngine::addCheckBox(const QRectF &r, const QMap &data, bool checked, const QString &name, bool readOnly) { Q_D(QPdfEngine); - uint obj = d->addXrefEntry(-1); + uint obj; char buf[256]; - QRectF rr = d->pageMatrix().mapRect(r); + QRectF rr; + + // Put out the resources we need for a checkbox once + if (d->formChkBoxResourceChecked == -1) { + addCheckBoxResources(); + } + obj = d->addXrefEntry(-1); + rr = d->pageMatrix().mapRect(r); + if (d->formFieldList == -1) d->formFieldList = d->requestObject(); d->xprintf("<<\n" "/Type /Annot\n" "/Parent %d 0 R\n" "/F 4\n" + "/V /Yes\n" "/Rect[", d->formFieldList); d->xprintf("%s ", qt_real_to_string(rr.left(),buf)); d->xprintf("%s ", qt_real_to_string(rr.top(),buf)); @@ -326,8 +436,20 @@ void QPdfEngine::addCheckBox(const QRectF &r, const QMap &data d->xprintf("%s", qt_real_to_string(rr.bottom(),buf)); d->xprintf("]\n" "/FT/Btn\n" - "/Subtype/Widget\n" - "/P %d 0 R\n", d->pages.back()); + "/Subtype /Widget\n" + "/AP << /N << /Yes %d 0 R /Off %d 0 R >> >>\n" + "/BS <<\n" + "/S\n" + "/W 1\n" + ">>\n" + "/MK <<\n" + "/BC [\n" + "0.0\n" + "0.0\n" + "0.0\n" + "]\n" + ">>\n" + "/P %d 0 R\n", d->formChkBoxResourceChecked, d->formChkBoxResourceUnChecked, d->pages.back()); if (checked) d->xprintf("/AS /Yes\n"); if (!name.isEmpty()) { @@ -518,7 +640,7 @@ void QPdfEngine::addComboBox(const QRectF &r, const QMap &data } d->xprintf("/F 4"); d->xprintf("/Subtype/Widget/TI 1/Type/Annot"); - d->xprintf("/Rect[", d->formFieldList); + d->xprintf("/Rect["); d->xprintf("%s ", qt_real_to_string(rr.left(),buf)); d->xprintf("%s ", qt_real_to_string(rr.top(),buf)); d->xprintf("%s ", qt_real_to_string(rr.right(),buf)); @@ -1483,6 +1605,9 @@ void QPdfEnginePrivate::writeHeader() pageRoot = requestObject(); formFieldList = -1; + formChkBoxResourceChecked = -1; + formChkBoxResourceUnChecked = -1; + // graphics state graphicsState = addXrefEntry(-1); xprintf("<<\n" diff --git a/src/gui/painting/qprintengine_pdf_p.h b/src/gui/painting/qprintengine_pdf_p.h index 98044019b69..464e71bc5aa 100644 --- a/src/gui/painting/qprintengine_pdf_p.h +++ b/src/gui/painting/qprintengine_pdf_p.h @@ -120,6 +120,7 @@ class QPdfEngine : public QPdfBaseEngine virtual void addHiddenField(const QRectF &, const QMap &data, const QString &value, const QString &name); virtual void addTextField(const QRectF &r, const QMap &data, const QString &text, const QString &name, bool multiLine, bool password, bool readOnly, int maxLength); virtual void addPageJavaScript(const QMap &data, const QString &script); + virtual void addCheckBoxResources(); virtual void addCheckBox(const QRectF &r, const QMap &data, bool checked, const QString &name, bool readOnly); virtual void addComboBox(const QRectF &r, const QMap &data, const QString &name, const QString &option_list, const QString &default_value, bool readOnly); @@ -224,6 +225,8 @@ class QPdfEnginePrivate : public QPdfBaseEnginePrivate void embedFont(QFontSubset *font); int formFieldList; + int formChkBoxResourceChecked; + int formChkBoxResourceUnChecked; QMap formFieldParents; QMap pageJavaScripts; QVector formFields; From 707e4314dfb7e4c8041b4ea8857f88b5f116a8e9 Mon Sep 17 00:00:00 2001 From: David Cornewell Date: Fri, 6 Mar 2020 11:22:34 -0500 Subject: [PATCH 8/9] Adding support for drawing radio buttons as interactive inputs. --- src/gui/painting/qpaintengine.h | 4 +- src/gui/painting/qpainter.cpp | 4 +- src/gui/painting/qpainter.h | 2 +- src/gui/painting/qprintengine_pdf.cpp | 174 +++++++++++++++++++++++++- src/gui/painting/qprintengine_pdf_p.h | 2 + 5 files changed, 179 insertions(+), 7 deletions(-) diff --git a/src/gui/painting/qpaintengine.h b/src/gui/painting/qpaintengine.h index 9dab82aeace..0348fb69838 100644 --- a/src/gui/painting/qpaintengine.h +++ b/src/gui/painting/qpaintengine.h @@ -175,8 +175,8 @@ class Q_GUI_EXPORT QPaintEngine virtual void addCheckBox(const QRectF &r, const QMap &data, bool checked, const QString &name, bool readOnly) { Q_UNUSED(r); Q_UNUSED(checked); Q_UNUSED(name); Q_UNUSED(readOnly); Q_UNUSED(data); } - virtual void addRadioButton(const QRectF &r, const QMap &data, const QString & group="", bool checked=false, const QString &name="", bool readOnly=false) { - Q_UNUSED(r); Q_UNUSED(checked); Q_UNUSED(name); Q_UNUSED(readOnly); Q_UNUSED(group); Q_UNUSED(data); + virtual void addRadioButton(const QRectF &r, const QMap &data, bool checked=false, const QString &name="", const QString &value="", bool readOnly=false) { + Q_UNUSED(r); Q_UNUSED(checked); Q_UNUSED(name); Q_UNUSED(readOnly); Q_UNUSED(value); Q_UNUSED(data); } virtual void addComboBox(const QRectF &r, const QMap &data, const QString &name="", const QString &option_list="[]", const QString &default_value="", bool readOnly=false) { Q_UNUSED(r); Q_UNUSED(default_value); Q_UNUSED(name); Q_UNUSED(readOnly); Q_UNUSED(option_list); Q_UNUSED(data); diff --git a/src/gui/painting/qpainter.cpp b/src/gui/painting/qpainter.cpp index f0a2d4dd1ce..1f9c3206ef7 100644 --- a/src/gui/painting/qpainter.cpp +++ b/src/gui/painting/qpainter.cpp @@ -7457,13 +7457,13 @@ void QPainter::addCheckBox(const QRectF &r, const QMap &data, } -void QPainter::addRadioButton(const QRectF &r, const QMap &data, const QString & group, bool checked, const QString &name, bool readOnly) { +void QPainter::addRadioButton(const QRectF &r, const QMap &data, bool checked, const QString &name, const QString &value, bool readOnly) { Q_D(QPainter); if (!d->engine) { qWarning("QPainter::addRadioButton: Painter not active"); return; } - d->engine->addRadioButton(worldTransform().mapRect(r), data, group, checked, name, readOnly); + d->engine->addRadioButton(worldTransform().mapRect(r), data, checked, name, value, readOnly); } void QPainter::addComboBox(const QRectF &r, const QMap &data, const QString &name, const QString &option_list, const QString &default_value, bool readOnly) { diff --git a/src/gui/painting/qpainter.h b/src/gui/painting/qpainter.h index 125d87df4c0..89ad72ecd46 100644 --- a/src/gui/painting/qpainter.h +++ b/src/gui/painting/qpainter.h @@ -459,7 +459,7 @@ class Q_GUI_EXPORT QPainter void addPageJavaScript(const QMap &data, const QString &script); void addTextField(const QRectF &r, const QMap &data, const QString &text="", const QString &name="", bool multiLine=false, bool password=false, bool readOnly=false, int maxLength=-1); void addCheckBox(const QRectF &r, const QMap &data, bool checked=false, const QString &name="", bool readOnly=false); - void addRadioButton(const QRectF &r, const QMap &data, const QString & group="", bool checked=false, const QString &name="", bool readOnly=false); + void addRadioButton(const QRectF &r, const QMap &data, bool checked=false, const QString &name="", const QString &value="", bool readOnly=false); void addComboBox(const QRectF &r, const QMap &data, const QString &name="", const QString &option_list="[]", const QString &default_value="", bool readOnly=false); inline void addHyperlink(int x, int y, int w, int h, const QUrl &url); diff --git a/src/gui/painting/qprintengine_pdf.cpp b/src/gui/painting/qprintengine_pdf.cpp index 258435ae0ec..033c9a16f2f 100644 --- a/src/gui/painting/qprintengine_pdf.cpp +++ b/src/gui/painting/qprintengine_pdf.cpp @@ -227,6 +227,9 @@ bool QPdfEngine::end() if (formFieldParent->type == "Ch") { d->xprintf("/Opt %s\n", formFieldParent->option_list.toUtf8().constData()); d->xprintf("/Ff 131072\n"); + } else if (formFieldParent->type == "Btn") { + // Radio buttons have children + d->xprintf("/Ff 49154\n"); } d->xprintf(">>endobj\n"); } @@ -309,6 +312,75 @@ void QPdfEngine::addPageJavaScript(const QMap &data, const QSt d->pageJavaScripts[script_name] = this->addJavaScript(script); } +// Create resources for radio buttons +void QPdfEngine::addRadioBtnResources(QRectF rr, int *formRadioBtnResourceChecked, int *formRadioBtnResourceUnChecked) { + Q_D(QPdfEngine); + float k = 0.552284749831; + float cx=((rr.right()-rr.left())/2); + float cy=((rr.bottom()-rr.top())/2); + float r=cx; // radius is half the total space we have just like the middle of the space. + char streambuf[1024]; + + // Drawing a circle. use b to stroke, fill, end on checked. Just stroke, end with f on unchecked. + snprintf(streambuf,sizeof(streambuf),"1 g\n" + "%.3f %.3f m\n" + "%.3f %.3f %.3f %.3f %.3f %.3f c\n" + "%.3f %.3f %.3f %.3f %.3f %.3f c\n" + "%.3f %.3f %.3f %.3f %.3f %.3f c\n" + "%.3f %.3f %.3f %.3f %.3f %.3f c\n", + (cx-r), cy, + cx - r, cy + k * r, cx - k * r, cy + r, cx, cy + r, + cx + k * r, cy + r, cx + r, cy + k * r, cx + r, cy, + cx + r, cy - k * r, cx + k * r, cy - r, cx, cy - r, + cx - k * r, cy - r, cx - r, cy - k * r, cx - r, cy); + + *formRadioBtnResourceChecked = d->addXrefEntry(-1); + d->xprintf("<<\n" + " /BBox [\n" + " 0.0\n" + " 0.0\n" + " %.3f\n" + " %.3f\n" + " ]\n" + " /FormType 1\n" + " /Resources <<\n" + " /ProcSet [\n" + " /PDF\n" + " ]\n" + " >>\n" + " /Subtype /Form\n" + " /Type /XObject\n" + " /Length %d\n" + ">>\n" + "stream\n" + "%sb\n" + "endstream\n" + "endobj\n", (rr.right()-rr.left()), (rr.bottom()-rr.top()), strlen(streambuf)+2, streambuf); + + *formRadioBtnResourceUnChecked = d->addXrefEntry(-1); + d->xprintf("<<\n" + " /BBox [\n" + " 0.0\n" + " 0.0\n" + " %.3f\n" + " %.3f\n" + " ]\n" + " /FormType 1\n" + " /Resources <<\n" + " /ProcSet [\n" + " /PDF\n" + " ]\n" + " >>\n" + " /Subtype /Form\n" + " /Type /XObject\n" + " /Length %d\n" + ">>\n" + "stream\n" + "%sf\n" + "endstream\n" + "endobj\n", (rr.right()-rr.left()), (rr.bottom()-rr.top()), strlen(streambuf)+2, streambuf); +} + // Create resources used by all checkboxes once void QPdfEngine::addCheckBoxResources() { Q_D(QPdfEngine); @@ -410,10 +482,108 @@ void QPdfEngine::addCheckBoxResources() { "endobj\n"); } -void QPdfEngine::addCheckBox(const QRectF &r, const QMap &data, bool checked, const QString &name, bool readOnly) { +void QPdfEngine::addRadioButton(const QRectF &r, const QMap &data, bool checked, const QString &name, const QString &value, bool readOnly) { Q_D(QPdfEngine); uint obj; char buf[256]; + QRectF rr = d->pageMatrix().mapRect(r); + int onBlurRef = -1; + int formRadioBtnResourceChecked, formRadioBtnResourceUnChecked; + + addRadioBtnResources(rr, &formRadioBtnResourceChecked, &formRadioBtnResourceUnChecked); + + if (d->formFieldList == -1) d->formFieldList = d->requestObject(); + + if (!d->formFieldParents.contains(name)) { + QFormFieldParent* form = new QFormFieldParent(); + form->ref = d->requestObject(); + form->type = "Btn"; + form->name = name; + form->value = value; + if (data.contains("acroform-validation")) { + form->JSvalidation_ref = this->addJavaScript(data["acroform-validation"]); + } + else { + form->JSvalidation_ref = -1; + } + d->formFields.push_back(form->ref); + d->formFieldParents[name] = form; + } + + //handling javascript + if (data.contains("acroform-on-blur")) { + onBlurRef = this->addJavaScript(data["acroform-on-blur"]); + } + + obj = d->addXrefEntry(-1); + d->xprintf("<<\n" + "/Type /Annot\n" + "/Subtype/Widget\n" + "/Parent %d 0 R\n" + "/F 4\n" + "/AP << /N << /%s %d 0 R /Off %d 0 R >> >>\n" +// "/BS <<\n" +// "/S\n" +// "/W 1\n" +// ">>\n" +// "/MK <<\n" +// "/BC [\n" +// "0.0\n" +// "0.0\n" +// "0.0\n" +// "]\n" +// ">>\n" + "/Rect[", d->formFieldParents[name]->ref, value.toUtf8().data(), + formRadioBtnResourceChecked, formRadioBtnResourceUnChecked); + + d->xprintf("%s ", qt_real_to_string(rr.left(),buf)); + d->xprintf("%s ", qt_real_to_string(rr.top(),buf)); + d->xprintf("%s ", qt_real_to_string(rr.right(),buf)); + d->xprintf("%s", qt_real_to_string(rr.bottom(),buf)); + d->xprintf("]\n" + "/P %d 0 R\n", d->pages.back()); + + if (checked) { + d->xprintf("/AS /%s\n", value); + } + + // writing javascript actions + if (onBlurRef > 0) { + d->xprintf("/AA<>", onBlurRef); + } + // alignment + if (data.contains("acroform-align")) { + uint align = -1; + if (data["acroform-align"].compare("left", Qt::CaseInsensitive) == 0) { + align = 0; + } + else if (data["acroform-align"].compare("center", Qt::CaseInsensitive) == 0){ + align = 1; + } + else if (data["acroform-align"].compare("right", Qt::CaseInsensitive) == 0){ + align = 2; + } + if (align != -1) { + d->xprintf("/Q %d\n",align); + } + } + +// if (!text.isEmpty()) { +// d->xprintf("/V"); +// d->printString(text); +// d->xprintf("\n"); +// } + d->xprintf(">>\n" + "endobj\n"); + d->currentPage->annotations.push_back(obj); + + d->formFieldParents[name]->children.push_back(obj); +} + +void QPdfEngine::addCheckBox(const QRectF &r, const QMap &data, bool checked, const QString &name, bool readOnly) { + Q_D(QPdfEngine); + char buf[256]; + uint obj; QRectF rr; // Put out the resources we need for a checkbox once @@ -645,7 +815,7 @@ void QPdfEngine::addComboBox(const QRectF &r, const QMap &data d->xprintf("%s ", qt_real_to_string(rr.top(),buf)); d->xprintf("%s ", qt_real_to_string(rr.right(),buf)); d->xprintf("%s", qt_real_to_string(rr.bottom(),buf)); - d->xprintf("]>>\n"); + d->xprintf("]>>\nendobj\n"); d->currentPage->annotations.push_back(obj); //d->formFields.push_back(obj); diff --git a/src/gui/painting/qprintengine_pdf_p.h b/src/gui/painting/qprintengine_pdf_p.h index 464e71bc5aa..108b6bd42a8 100644 --- a/src/gui/painting/qprintengine_pdf_p.h +++ b/src/gui/painting/qprintengine_pdf_p.h @@ -120,8 +120,10 @@ class QPdfEngine : public QPdfBaseEngine virtual void addHiddenField(const QRectF &, const QMap &data, const QString &value, const QString &name); virtual void addTextField(const QRectF &r, const QMap &data, const QString &text, const QString &name, bool multiLine, bool password, bool readOnly, int maxLength); virtual void addPageJavaScript(const QMap &data, const QString &script); + virtual void addRadioBtnResources(QRectF rr, int *formRadioBtnResourceChecked, int *formRadioBtnResourceUnChecked); virtual void addCheckBoxResources(); virtual void addCheckBox(const QRectF &r, const QMap &data, bool checked, const QString &name, bool readOnly); + virtual void addRadioButton(const QRectF &r, const QMap &data, bool checked, const QString &name, const QString &value, bool readOnly); virtual void addComboBox(const QRectF &r, const QMap &data, const QString &name, const QString &option_list, const QString &default_value, bool readOnly); // ### unused, should have something for this in QPrintEngine From fb0fc4c7c08d105931485f24b19cdd6f9f87e601 Mon Sep 17 00:00:00 2001 From: David Cornewell Date: Thu, 26 Mar 2020 16:44:27 -0400 Subject: [PATCH 9/9] Updating xprintf to convert QString to char* --- src/gui/painting/qprintengine_pdf.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/gui/painting/qprintengine_pdf.cpp b/src/gui/painting/qprintengine_pdf.cpp index 033c9a16f2f..305985b851c 100644 --- a/src/gui/painting/qprintengine_pdf.cpp +++ b/src/gui/painting/qprintengine_pdf.cpp @@ -544,7 +544,7 @@ void QPdfEngine::addRadioButton(const QRectF &r, const QMap &d "/P %d 0 R\n", d->pages.back()); if (checked) { - d->xprintf("/AS /%s\n", value); + d->xprintf("/AS /%s\n", value.toUtf8().constData()); } // writing javascript actions