AVYASH commited on
Commit
44a8689
·
1 Parent(s): 0b9bef7

Upload poc.py

Browse files
Files changed (1) hide show
  1. poc.py +108 -0
poc.py ADDED
@@ -0,0 +1,108 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # FROM https://github.com/osnr/horrifying-pdf-experiments
2
+ import sys
3
+
4
+ from pdfrw import PdfWriter
5
+ from pdfrw.objects.pdfname import PdfName
6
+ from pdfrw.objects.pdfstring import PdfString
7
+ from pdfrw.objects.pdfdict import PdfDict
8
+ from pdfrw.objects.pdfarray import PdfArray
9
+
10
+ def make_js_action(js):
11
+ action = PdfDict()
12
+ action.S = PdfName.JavaScript
13
+ action.JS = js
14
+ return action
15
+
16
+ def make_field(name, x, y, width, height, r, g, b, value=""):
17
+ annot = PdfDict()
18
+ annot.Type = PdfName.Annot
19
+ annot.Subtype = PdfName.Widget
20
+ annot.FT = PdfName.Tx
21
+ annot.Ff = 2
22
+ annot.Rect = PdfArray([x, y, x + width, y + height])
23
+ annot.MaxLen = 160
24
+ annot.T = PdfString.encode(name)
25
+ annot.V = PdfString.encode(value)
26
+
27
+ # Default appearance stream: can be arbitrary PDF XObject or
28
+ # something. Very general.
29
+ annot.AP = PdfDict()
30
+
31
+ ap = annot.AP.N = PdfDict()
32
+ ap.Type = PdfName.XObject
33
+ ap.Subtype = PdfName.Form
34
+ ap.FormType = 1
35
+ ap.BBox = PdfArray([0, 0, width, height])
36
+ ap.Matrix = PdfArray([1.0, 0.0, 0.0, 1.0, 0.0, 0.0])
37
+ ap.stream = """
38
+ %f %f %f rg
39
+ 0.0 0.0 %f %f re f
40
+ """ % (r, g, b, width, height)
41
+
42
+ # It took me a while to figure this out. See PDF spec:
43
+ # https://www.adobe.com/content/dam/Adobe/en/devnet/acrobat/pdfs/pdf_reference_1-7.pdf#page=641
44
+
45
+ # Basically, the appearance stream we just specified doesn't
46
+ # follow the field rect if it gets changed in JS (at least not in
47
+ # Chrome).
48
+
49
+ # But this simple MK field here, with border/color
50
+ # characteristics, _does_ follow those movements and resizes, so
51
+ # we can get moving colored rectangles this way.
52
+ annot.MK = PdfDict()
53
+ annot.MK.BG = PdfArray([r, g, b])
54
+
55
+ return annot
56
+
57
+ def make_page(fields, script):
58
+ page = PdfDict()
59
+ page.Type = PdfName.Page
60
+
61
+ page.Resources = PdfDict()
62
+ page.Resources.Font = PdfDict()
63
+ page.Resources.Font.F1 = PdfDict()
64
+ page.Resources.Font.F1.Type = PdfName.Font
65
+ page.Resources.Font.F1.Subtype = PdfName.Type1
66
+ page.Resources.Font.F1.BaseFont = PdfName.Helvetica
67
+
68
+ page.MediaBox = PdfArray([0, 0, 612, 792])
69
+
70
+ page.Contents = PdfDict()
71
+ page.Contents.stream = """
72
+ BT
73
+ /F1 24 Tf
74
+ ET
75
+ """
76
+
77
+ annots = fields
78
+
79
+ page.AA = PdfDict()
80
+ # You probably should just wrap each JS action with a try/catch,
81
+ # because Chrome does no error reporting or even logging otherwise;
82
+ # you just get a silent failure.
83
+ page.AA.O = make_js_action("""
84
+ try {
85
+ %s
86
+ } catch (e) {
87
+ app.alert(e.message);
88
+ }
89
+ """ % (script))
90
+
91
+ page.Annots = PdfArray(annots)
92
+ return page
93
+
94
+ if len(sys.argv) > 1:
95
+ js_file = open(sys.argv[1], 'r')
96
+
97
+ fields = []
98
+ for line in js_file:
99
+ if not line.startswith('/// '): break
100
+ pieces = line.split()
101
+ params = [pieces[1]] + [float(token) for token in pieces[2:]]
102
+ fields.append(make_field(*params))
103
+
104
+ js_file.seek(0)
105
+
106
+ out = PdfWriter()
107
+ out.addpage(make_page(fields, js_file.read()))
108
+ out.write('result.pdf')