comparison tests/test_drawings.py @ 1:1d09e1dec1d9 upstream

ADD: PyMuPDF v1.26.4: the original sdist. It does not yet contain MuPDF. This normally will be downloaded when building PyMuPDF.
author Franz Glasner <fzglas.hg@dom66.de>
date Mon, 15 Sep 2025 11:37:51 +0200
parents
children
comparison
equal deleted inserted replaced
-1:000000000000 1:1d09e1dec1d9
1 """
2 Extract drawings of a PDF page and compare with stored expected result.
3 """
4
5 import io
6 import os
7 import sys
8 import pprint
9
10 import pymupdf
11
12 scriptdir = os.path.abspath(os.path.dirname(__file__))
13 filename = os.path.join(scriptdir, "resources", "symbol-list.pdf")
14 symbols = os.path.join(scriptdir, "resources", "symbols.txt")
15
16
17 def test_drawings1():
18 symbols_text = open(symbols).read() # expected result
19 doc = pymupdf.open(filename)
20 page = doc[0]
21 paths = page.get_cdrawings()
22 out = io.StringIO() # pprint output goes here
23 pprint.pprint(paths, stream=out)
24 assert symbols_text == out.getvalue()
25
26
27 def test_drawings2():
28 delta = (0, 20, 0, 20)
29 doc = pymupdf.open()
30 page = doc.new_page()
31
32 r = pymupdf.Rect(100, 100, 200, 200)
33 page.draw_circle(r.br, 2, color=0)
34 r += delta
35
36 page.draw_line(r.tl, r.br, color=0)
37 r += delta
38
39 page.draw_oval(r, color=0)
40 r += delta
41
42 page.draw_rect(r, color=0)
43 r += delta
44
45 page.draw_quad(r.quad, color=0)
46 r += delta
47
48 page.draw_polyline((r.tl, r.tr, r.br), color=0)
49 r += delta
50
51 page.draw_bezier(r.tl, r.tr, r.br, r.bl, color=0)
52 r += delta
53
54 page.draw_curve(r.tl, r.tr, r.br, color=0)
55 r += delta
56
57 page.draw_squiggle(r.tl, r.br, color=0)
58 r += delta
59
60 rects = [p["rect"] for p in page.get_cdrawings()]
61 bboxes = [b[1] for b in page.get_bboxlog()]
62 for i, r in enumerate(rects):
63 assert pymupdf.Rect(r) in pymupdf.Rect(bboxes[i])
64
65
66 def _dict_difference(a, b):
67 """
68 Verifies that dictionaries "a", "b"
69 * have the same keys and values, except for key "items":
70 * the items list of "a" must be one shorter but otherwise equal the "b" items
71
72 Returns last item of b["items"].
73 """
74 assert a.keys() == b.keys()
75 for k in a.keys():
76 v1 = a[k]
77 v2 = b[k]
78 if k != "items":
79 assert v1 == v2
80 else:
81 assert v1 == v2[:-1]
82 rc = v2[-1]
83 return rc
84
85
86 def test_drawings3():
87 doc = pymupdf.open()
88 page1 = doc.new_page()
89 shape1 = page1.new_shape()
90 shape1.draw_line((10, 10), (10, 50))
91 shape1.draw_line((10, 50), (100, 100))
92 shape1.finish(closePath=False)
93 shape1.commit()
94 drawings1 = page1.get_drawings()[0]
95
96 page2 = doc.new_page()
97 shape2 = page2.new_shape()
98 shape2.draw_line((10, 10), (10, 50))
99 shape2.draw_line((10, 50), (100, 100))
100 shape2.finish(closePath=True)
101 shape2.commit()
102 drawings2 = page2.get_drawings()[0]
103
104 assert _dict_difference(drawings1, drawings2) == ("l", (100, 100), (10, 10))
105
106 page3 = doc.new_page()
107 shape3 = page3.new_shape()
108 shape3.draw_line((10, 10), (10, 50))
109 shape3.draw_line((10, 50), (100, 100))
110 shape3.draw_line((100, 100), (50, 70))
111 shape3.finish(closePath=False)
112 shape3.commit()
113 drawings3 = page3.get_drawings()[0]
114
115 page4 = doc.new_page()
116 shape4 = page4.new_shape()
117 shape4.draw_line((10, 10), (10, 50))
118 shape4.draw_line((10, 50), (100, 100))
119 shape4.draw_line((100, 100), (50, 70))
120 shape4.finish(closePath=True)
121 shape4.commit()
122 drawings4 = page4.get_drawings()[0]
123
124 assert _dict_difference(drawings3, drawings4) == ("l", (50, 70), (10, 10))
125
126
127 def test_2365():
128 """Draw a filled rectangle on a new page.
129
130 Then extract the page's vector graphics and confirm that only one path
131 was generated which has all the right properties."""
132 doc = pymupdf.open()
133 page = doc.new_page()
134 rect = pymupdf.Rect(100, 100, 200, 200)
135 page.draw_rect(
136 rect, color=pymupdf.pdfcolor["black"], fill=pymupdf.pdfcolor["yellow"], width=3
137 )
138 paths = page.get_drawings()
139 assert len(paths) == 1
140 path = paths[0]
141 assert path["type"] == "fs"
142 assert path["fill"] == pymupdf.pdfcolor["yellow"]
143 assert path["fill_opacity"] == 1
144 assert path["color"] == pymupdf.pdfcolor["black"]
145 assert path["stroke_opacity"] == 1
146 assert path["width"] == 3
147 assert path["rect"] == rect
148
149
150 def test_2462():
151 """
152 Assertion happens, if this code does NOT bring down the interpreter.
153
154 Background:
155 We previously ignored clips for non-vector-graphics. However, ending
156 a clip does not refer back the object(s) that have been clipped.
157 In order to correctly compute the "scissor" rectangle, we now keep track
158 of the clipped object type.
159 """
160 doc = pymupdf.open(f"{scriptdir}/resources/test-2462.pdf")
161 page = doc[0]
162 vg = page.get_drawings(extended=True)
163
164
165 def test_2556():
166 """Ensure that incomplete clip paths will be properly ignored."""
167 doc = pymupdf.open() # new empty PDF
168 page = doc.new_page() # new page
169 # following contains an incomplete clip
170 c = b"q 50 697.6 400 100.0 re W n q 0 0 m W n Q "
171 xref = doc.get_new_xref() # prepare /Contents object for page
172 doc.update_object(xref, "<<>>") # new xref now is a dictionary
173 doc.update_stream(xref, c) # store drawing commands
174 page.set_contents(xref) # give the page this xref as /Contents
175 # following will bring down interpreter if fix not installed
176 assert page.get_drawings(extended=True)
177
178
179 def test_3207():
180 """Example graphics with multiple "close path" commands within same path.
181
182 The fix translates a close-path commands into an additional line
183 which connects the current point with a preceding "move" target.
184 The example page has 2 paths which each contain 2 close-path
185 commands after 2 normal "line" commands, i.e. 2 command sequences
186 "move-to, line-to, line-to, close-path".
187 This is converted into 3 connected lines, where the last end point
188 is connect to the start point of the first line.
189 So, in the sequence of lines / points
190
191 (p0, p1), (p2, p3), (p4, p5), (p6, p7), (p8, p9), (p10, p11)
192
193 point p5 must equal p0, and p11 must equal p6 (for each of the
194 two paths in the example).
195 """
196 filename = os.path.join(scriptdir, "resources", "test-3207.pdf")
197 doc = pymupdf.open(filename)
198 page = doc[0]
199 paths = page.get_drawings()
200 assert len(paths) == 2
201
202 path0 = paths[0]
203 items = path0["items"]
204 assert len(items) == 6
205 p0 = items[0][1]
206 p5 = items[2][2]
207 p6 = items[3][1]
208 p11 = items[5][2]
209 assert p0 == p5
210 assert p6 == p11
211
212 path1 = paths[1]
213 items = path1["items"]
214 assert len(items) == 6
215 p0 = items[0][1]
216 p5 = items[2][2]
217 p6 = items[3][1]
218 p11 = items[5][2]
219 assert p0 == p5
220 assert p6 == p11
221
222
223 def test_3591():
224 """Confirm correct scaling factor for rotation matrices."""
225 filename = os.path.join(scriptdir, "resources", "test-3591.pdf")
226 doc = pymupdf.open(filename)
227 page = doc[0]
228 paths = page.get_drawings()
229 for p in paths:
230 assert p["width"] == 15