Closed kurokawaikki closed 4 years ago
Thanks for the submission. I will check this out. What I know so far: JavaScripts can be activated by form fields (e.g. buttons), but also by any annotation. Activating the script can be bound to certain events, which differ a little bit between different object types (pressing the mouse button, or releasing it, hovering over the object, etc.).
So...... Can I use the pyMUPDF to create a button (form fields) bound to specific javascript? What I know so far is that, pyMuPDF can create button by widget. But how can I bound the javascript to it? Thank you!!
I have investigated this and will publish this option in the next version.
The button definition as PDF object will need to have an action entry /A
which points to an action object roughly looking like this (at least for shorter scripts):
nnn 0 obj
<<
/S /JavaScript
/JS(... text of the script ...)
>>
endobj
I have developed and tested adding JavaScript to form fields. For your info, here is a first draft of the respective documentation:
(New in version 1.16.12) JavaScript code for an action associated with the widget, or None
.
(New in version 1.16.12) JavaScript code to be performed when the user types a key-stroke into a text field or combo box or modifies the selection in a scrollable list box. This action can check the keystroke for validity and reject or modify it. None
if not present.
(New in version 1.16.12) JavaScript code to be performed before the field is formatted to display its current value. This action can modify the field’s value before formatting. None
if not present.
(New in version 1.16.12) JavaScript code to be performed when the field’s value is changed. This action can check the new value for validity. (The name V stands for“validate.”). None
if not present.
(New in version 1.16.12) JavaScript code to be performed to recalculate the value of this field when that of another field changes. (The name C stands for “calculate.”). None
if not present.
Note For adding or changing one of the above scripts, just put the appropriate JavaScript code in the widget attribute. It will automatically replace the script previously stored in the PDF. To remove a script, set the respective attribute to
None
.
Please let me know if you would like an unofficial pre-version for testing. In that case I can send you a wheel via mail or this channel.
Thank you very much! I would very like to test the unofficial pre-version. I will try out the function to add Javascript to form filed and buttons when clicked. Can you provide the wheel in here? Thank you again for your great help.
Ok, right then. This is the list of wheels I have, please tell me which one you need:
PyMuPDF-1.16.12-cp27-cp27m-macosx_10_6_intel.whl
PyMuPDF-1.16.12-cp27-cp27m-manylinux2010_x86_64.whl
PyMuPDF-1.16.12-cp27-cp27m-win32.whl
PyMuPDF-1.16.12-cp27-cp27m-win_amd64.whl
PyMuPDF-1.16.12-cp27-cp27mu-manylinux2010_x86_64.whl
PyMuPDF-1.16.12-cp35-cp35m-macosx_10_6_intel.whl
PyMuPDF-1.16.12-cp35-cp35m-manylinux2010_x86_64.whl
PyMuPDF-1.16.12-cp35-cp35m-win32.whl
PyMuPDF-1.16.12-cp35-cp35m-win_amd64.whl
PyMuPDF-1.16.12-cp36-cp36m-macosx_10_6_intel.whl
PyMuPDF-1.16.12-cp36-cp36m-manylinux2010_x86_64.whl
PyMuPDF-1.16.12-cp36-cp36m-win32.whl
PyMuPDF-1.16.12-cp36-cp36m-win_amd64.whl
PyMuPDF-1.16.12-cp37-cp37m-macosx_10_6_intel.whl
PyMuPDF-1.16.12-cp37-cp37m-manylinux2010_x86_64.whl
PyMuPDF-1.16.12-cp37-cp37m-win32.whl
PyMuPDF-1.16.12-cp37-cp37m-win_amd64.whl
PyMuPDF-1.16.12-cp38-cp38-macosx_10_9_x86_64.whl
PyMuPDF-1.16.12-cp38-cp38-manylinux2010_x86_64.whl
PyMuPDF-1.16.12-cp38-cp38-win32.whl
PyMuPDF-1.16.12-cp38-cp38-win_amd64.whl
May I have "PyMuPDF-1.16.12-cp38-cp38-win_amd64.whl"? Thank you very much!
PyMuPDF-1.16.12-cp38-cp38-win_amd64.zip
Github won't let me upload *.whl files. So you have to change the extension from .zip to .whl before installing. Good luck!
Thank you! I have installed it. I tried to create the widget with your instructions.
doc = fitz.open("TEST.pdf")
p = doc[0]
widget = fitz.Widget()
widget.rect = fitz.Rect(10,10,50,50)
widget.field_type = fitz.PDF_WIDGET_TYPE_BUTTON # also tried 1
widget.field_type_string = "Button"
widget.script = 'app.alert("clicked")'
widget.field_name = "btn"
widget.field_value = "TEST"
annot = p.addWidget(widget)
doc.save("t2.pdf")
It works and executes the javascript when I clicked it. However, I cannot create a button with " fitz.PDF_WIDGET_TYPE_BUTTON ". It gave we a checkbox instead. I also tried to read the existed button to check the filed_type and field_type_string. I set the same value to that. But it still gave me the checkbox. How can I fix this? With the "widget.script" attribute I was able to write javascripts to the widget when clicked. Thank you for your help!
Godd that the script works! Unfortunately, PDF specification is a little tricky and non-intuitive in this are. First of all you do not need to set widget.field_type_string - this is a comment only for better interpretation.
I was successful in coding:
widget.field_type = fitz.PDF_WIDGET_TYPE_BUTTON
widget.field_flags = fitz.PDF_BTN_FIELD_IS_PUSHBUTTON
The widget.field_flags
can also be further modified, but don't forget that it is a field of bit flags, so single values must be modified using boolean operators (&, |, etc.).
It is probably best to read section 8.6.3 Field Types of the PDF manual on page 685. A good working idea could also be gotten from the documentation here.
Since we cannot make the text in free text annotation become bold, we tried to use javascript instead. Thank for JorjMcKie's great help. Now, it is possible to have a workaround. Here is my final code to make the text become bold in free text annotation.
doc = fitz.open("tetetetet.pdf")
p = doc[0]
widget = fitz.Widget()
widget.rect = fitz.Rect(10,10,50,50)
widget.field_type = fitz.PDF_WIDGET_TYPE_TEXT
widget.script = "var annt = this.getAnnots(); \n annt.forEach(function (item, index) { \n try{ \n var span = item.richContents; \n span.forEach(function (it, dx) {it.fontWeight = 800;}) \n item.richContents = span; \n}\n catch(err){} \n }); \n app.alert('Done');"
widget.field_name = "btn"
widget.field_value = "Make Bold"
widget.rotate = p.rotation
annot = p.addWidget(widget)
doc.save("t2.pdf")
I have one last question. Can I rotate the widget? I cannot rotate it with widget.rotate = p.rotation. Thank you very much!!
You can rotate text in a freetext annotation. For all annotations you can also control how it should react if the page is rotated (annotation flags). But you cannot rotate a widget currently.
@kurokawaikki - I find your solution very interesting! Can I persuade you to write an article in the Wiki of this repo? Or a simple description of what you did in a text file? I can offer to add it to PyMuPDF's documentation mentioning your contribution ... 😎
Yes, I would like to do it. I can write an article in the Wiki. What kind of contents should I proved to you? What should I write?
I think I would add it to the recipes section of the documentation and the Wiki pages. Title "How to modify a Freetext annotation font".
[Workaround] - How to make the text bold in a Freetext Annotation.
Problem: Since v1.16.0 a 'Freetext' annotation font is restricted to the "normal" versions of Times Roman, Helvetica, Courier - no more bold, italic. It is impossible to use pyMuPDF to modify the properties.
Solution: According to the documentation provided by the Adobe, it is possible to use Javascripts to manipulate the properties of Freetext annotation. Here is the API reference provided by Adobe (https://www.adobe.com/content/dam/acom/en/devnet/acrobat/pdfs/js_api_reference.pdf) or (https://www.adobe.com/devnet/acrobat/documentation.html) for newer documentation. The Freetext annotation can be obtained by this.getAnnots()
(this will return all annotation in the PDF as an array). Next, we can loop over this array to set the properties of the text through richContents
attribute. There is no explicit property to set a bold text though, it is possible to set it through fontWeight = 800
(400 is the normal size) attribute of the richContents
. Additionally, it is possible to set the color, italic and other font properties through richContents
. Therefore, we can use pyMuPDF to create Freetext annotations in the document on the desired place first. Subsequently, use the new script
attribute provided in the new pyMuPDF (version ID) to create an push button
that links to the Javascript containing the code to make the text bold onClicked
. Finally, we can export the PDF and open in the adobe reader (if you clicked it with PDF X-Change editor, all the annotation will disappear... Probably, it is a bug in PDF X-Change editor.). After clicking on the button, we can find that all the text in the Freetext annotation is Bold right now! You can remove the button by pyMuPDF or adobe acrobat (non-free) or PDF X-Change editor (free).
Here is a simple example to make a push button that can make the text in Freetext annotation bold.
import fitz
doc = fitz.open("test.pdf") #Open document
p = doc[0] #get the page object on page 1
widget = fitz.Widget() #create a widget
widget.rect = fitz.Rect(10,10,50,50) #set the size and position (x1, y1, x2, y2)
widget.field_type = fitz.PDF_WIDGET_TYPE_BUTTON
widget.field_flags = fitz.PDF_BTN_FIELD_IS_PUSHBUTTON #set the widget become push button
widget.script = "var annt = this.getAnnots(); \n annt.forEach(function (item, index) { \n try{ \n var span = item.richContents; \n span.forEach(function (it, dx) {it.fontWeight = 800;}) \n item.richContents = span; \n}\n catch(err){} \n }); \n app.alert('Done');" #set the specific Javascript that can make all annotation become bold.
widget.field_name = "btn"
widget.field_value = "Make Bold"
annot = p.addWidget(widget) # add the widget to the page
doc.save("t2.pdf") #output the file
With this workaround, you can set text properties that are not supported in the pyMuPDF. With the help from Javascript in PDF, it is possible to do much more stuffs.
If you need any other information, please let me know. Thank you very much for your great help.
@kurokawaikki
Hey thank you very much for this contribution indeed. I will publish it as promised in the documentation of the next PyMuPDF version, and also on Wiki. How can I reference you: just the Github name or do you wish to name your e-mail address or something else? Thanks again!
Thank you! It is okay just putting the Github name. I am very appreciate your work on pyMuPDF!
Official version 1.16.12 published.
Can I add a button that can run Javascript? I mean... Can I use PyMuPDF to add a button widget that contain a specific Javascript? After exporting as PDF file, I can click on that button and run the Javascript that I predefined in PyMuPDF. Thank you very much for your great help!