File tree Expand file tree Collapse file tree 3 files changed +38
-1
lines changed
Expand file tree Collapse file tree 3 files changed +38
-1
lines changed Original file line number Diff line number Diff line change @@ -546,6 +546,9 @@ Have a look at the :ref:`FAQ` section to see some pixmap usage "at work".
546546 367 ns ± 1.75 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
547547 In [4]: %timeit len(pix.samples)
548548 3.52 ms ± 57.5 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
549+
550+ After the Pixmap has been destroyed, any attempt to use the memoryview
551+ will fail with ValueError.
549552
550553 :type: memoryview
551554
@@ -559,6 +562,9 @@ Have a look at the :ref:`FAQ` section to see some pixmap usage "at work".
559562 img = QtGui.QImage(pix.samples_ptr, pix.width, pix.height, format) # (2)
560563
561564 Both of the above lead to the same Qt image, but (2) can be **many hundred times faster **, because it avoids an additional copy of the pixel area.
565+
566+ Warning: after the Pixmap has been destroyed, the Python pointer will be
567+ invalid and attempting to use it may crash the Python interpreter.
562568
563569 :type: int
564570
Original file line number Diff line number Diff line change @@ -10059,6 +10059,9 @@ def __init__(self, *args):
1005910059 # data. Doesn't seem to make much difference to Pixmap.set_pixel() so
1006010060 # not currently used.
1006110061 self._memory_view = None
10062+
10063+ # Cache for property `self.samples_mv`.
10064+ self._samples_mv = None
1006210065
1006310066 def __len__(self):
1006410067 return self.size
@@ -10339,7 +10342,13 @@ def samples_mv(self):
1033910342 '''
1034010343 Pixmap samples memoryview.
1034110344 '''
10342- return mupdf.fz_pixmap_samples_memoryview(self.this)
10345+ # We remember the returned memoryview so that our `__del__()` can
10346+ # release it; otherwise accessing it after we have been destructed will
10347+ # fail, possibly crashing Python; this is #4155.
10348+ #
10349+ if self._samples_mv is None:
10350+ self._samples_mv = mupdf.fz_pixmap_samples_memoryview(self.this)
10351+ return self._samples_mv
1034310352
1034410353 @property
1034510354 def samples_ptr(self):
@@ -10625,6 +10634,10 @@ def yres(self):
1062510634
1062610635 width = w
1062710636 height = h
10637+
10638+ def __del__(self):
10639+ if self._samples_mv:
10640+ self._samples_mv.release()
1062810641
1062910642
1063010643del Point
Original file line number Diff line number Diff line change @@ -428,3 +428,21 @@ def test_3854():
428428 assert rms < 1
429429 else :
430430 assert rms == 0
431+
432+
433+ def test_4155 ():
434+ path = os .path .normpath (f'{ __file__ } /../../tests/resources/test_3854.pdf' )
435+ with pymupdf .open (path ) as document :
436+ page = document [0 ]
437+ pixmap = page .get_pixmap ()
438+ mv = pixmap .samples_mv
439+ mvb1 = mv .tobytes ()
440+ del page
441+ del pixmap
442+ try :
443+ mvb2 = mv .tobytes ()
444+ except ValueError as e :
445+ print (f'Received exception: { e } ' )
446+ assert 'operation forbidden on released memoryview object' in str (e )
447+ else :
448+ assert 0 , f'Did not receive expected exception when using defunct memoryview.'
You can’t perform that action at this time.
0 commit comments