Skip to content

Commit 349849b

Browse files
authored
Merge pull request matplotlib#26215 from meeseeksmachine/auto-backport-of-pr-25824-on-v3.7.x
Backport PR matplotlib#25824 on branch v3.7.x (pdf: Use explicit palette when saving indexed images)
2 parents 8d42212 + 399ebe1 commit 349849b

File tree

21 files changed

+46
-22
lines changed

21 files changed

+46
-22
lines changed

lib/matplotlib/backends/backend_pdf.py

Lines changed: 21 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1757,39 +1757,43 @@ def _writeImg(self, data, id, smask=None):
17571757
'Subtype': Name('Image'),
17581758
'Width': width,
17591759
'Height': height,
1760-
'ColorSpace': Name({1: 'DeviceGray',
1761-
3: 'DeviceRGB'}[color_channels]),
1760+
'ColorSpace': Name({1: 'DeviceGray', 3: 'DeviceRGB'}[color_channels]),
17621761
'BitsPerComponent': 8}
17631762
if smask:
17641763
obj['SMask'] = smask
17651764
if mpl.rcParams['pdf.compression']:
17661765
if data.shape[-1] == 1:
17671766
data = data.squeeze(axis=-1)
1767+
png = {'Predictor': 10, 'Colors': color_channels, 'Columns': width}
17681768
img = Image.fromarray(data)
17691769
img_colors = img.getcolors(maxcolors=256)
17701770
if color_channels == 3 and img_colors is not None:
1771-
# Convert to indexed color if there are 256 colors or fewer
1772-
# This can significantly reduce the file size
1771+
# Convert to indexed color if there are 256 colors or fewer. This can
1772+
# significantly reduce the file size.
17731773
num_colors = len(img_colors)
1774-
# These constants were converted to IntEnums and deprecated in
1775-
# Pillow 9.2
1776-
dither = getattr(Image, 'Dither', Image).NONE
1777-
pmode = getattr(Image, 'Palette', Image).ADAPTIVE
1778-
img = img.convert(
1779-
mode='P', dither=dither, palette=pmode, colors=num_colors
1780-
)
1774+
palette = np.array([comp for _, color in img_colors for comp in color],
1775+
dtype=np.uint8)
1776+
palette24 = ((palette[0::3].astype(np.uint32) << 16) |
1777+
(palette[1::3].astype(np.uint32) << 8) |
1778+
palette[2::3])
1779+
rgb24 = ((data[:, :, 0].astype(np.uint32) << 16) |
1780+
(data[:, :, 1].astype(np.uint32) << 8) |
1781+
data[:, :, 2])
1782+
indices = np.argsort(palette24).astype(np.uint8)
1783+
rgb8 = indices[np.searchsorted(palette24, rgb24, sorter=indices)]
1784+
img = Image.fromarray(rgb8, mode='P')
1785+
img.putpalette(palette)
17811786
png_data, bit_depth, palette = self._writePng(img)
17821787
if bit_depth is None or palette is None:
17831788
raise RuntimeError("invalid PNG header")
1784-
palette = palette[:num_colors * 3] # Trim padding
1785-
obj['ColorSpace'] = Verbatim(
1786-
b'[/Indexed /DeviceRGB %d %s]'
1787-
% (num_colors - 1, pdfRepr(palette)))
1789+
palette = palette[:num_colors * 3] # Trim padding; remove for Pillow>=9
1790+
obj['ColorSpace'] = [Name('Indexed'), Name('DeviceRGB'),
1791+
num_colors - 1, palette]
17881792
obj['BitsPerComponent'] = bit_depth
1789-
color_channels = 1
1793+
png['Colors'] = 1
1794+
png['BitsPerComponent'] = bit_depth
17901795
else:
17911796
png_data, _, _ = self._writePng(img)
1792-
png = {'Predictor': 10, 'Colors': color_channels, 'Columns': width}
17931797
else:
17941798
png = None
17951799
self.beginStream(
Binary file not shown.
-2.48 KB
Binary file not shown.
41 Bytes
Binary file not shown.
Binary file not shown.
Binary file not shown.
-15.6 KB
Binary file not shown.
66 Bytes
Binary file not shown.
-237 Bytes
Binary file not shown.
Binary file not shown.

0 commit comments

Comments
 (0)