You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

871 lines
25 KiB

4 years ago
  1. #
  2. # The Python Imaging Library.
  3. # $Id$
  4. #
  5. # PNG support code
  6. #
  7. # See "PNG (Portable Network Graphics) Specification, version 1.0;
  8. # W3C Recommendation", 1996-10-01, Thomas Boutell (ed.).
  9. #
  10. # history:
  11. # 1996-05-06 fl Created (couldn't resist it)
  12. # 1996-12-14 fl Upgraded, added read and verify support (0.2)
  13. # 1996-12-15 fl Separate PNG stream parser
  14. # 1996-12-29 fl Added write support, added getchunks
  15. # 1996-12-30 fl Eliminated circular references in decoder (0.3)
  16. # 1998-07-12 fl Read/write 16-bit images as mode I (0.4)
  17. # 2001-02-08 fl Added transparency support (from Zircon) (0.5)
  18. # 2001-04-16 fl Don't close data source in "open" method (0.6)
  19. # 2004-02-24 fl Don't even pretend to support interlaced files (0.7)
  20. # 2004-08-31 fl Do basic sanity check on chunk identifiers (0.8)
  21. # 2004-09-20 fl Added PngInfo chunk container
  22. # 2004-12-18 fl Added DPI read support (based on code by Niki Spahiev)
  23. # 2008-08-13 fl Added tRNS support for RGB images
  24. # 2009-03-06 fl Support for preserving ICC profiles (by Florian Hoech)
  25. # 2009-03-08 fl Added zTXT support (from Lowell Alleman)
  26. # 2009-03-29 fl Read interlaced PNG files (from Conrado Porto Lopes Gouvua)
  27. #
  28. # Copyright (c) 1997-2009 by Secret Labs AB
  29. # Copyright (c) 1996 by Fredrik Lundh
  30. #
  31. # See the README file for information on usage and redistribution.
  32. #
  33. import logging
  34. import re
  35. import zlib
  36. import struct
  37. from . import Image, ImageFile, ImagePalette
  38. from ._binary import i8, i16be as i16, i32be as i32, o16be as o16, o32be as o32
  39. from ._util import py3
  40. __version__ = "0.9"
  41. logger = logging.getLogger(__name__)
  42. is_cid = re.compile(br"\w\w\w\w").match
  43. _MAGIC = b"\211PNG\r\n\032\n"
  44. _MODES = {
  45. # supported bits/color combinations, and corresponding modes/rawmodes
  46. (1, 0): ("1", "1"),
  47. (2, 0): ("L", "L;2"),
  48. (4, 0): ("L", "L;4"),
  49. (8, 0): ("L", "L"),
  50. (16, 0): ("I", "I;16B"),
  51. (8, 2): ("RGB", "RGB"),
  52. (16, 2): ("RGB", "RGB;16B"),
  53. (1, 3): ("P", "P;1"),
  54. (2, 3): ("P", "P;2"),
  55. (4, 3): ("P", "P;4"),
  56. (8, 3): ("P", "P"),
  57. (8, 4): ("LA", "LA"),
  58. (16, 4): ("RGBA", "LA;16B"), # LA;16B->LA not yet available
  59. (8, 6): ("RGBA", "RGBA"),
  60. (16, 6): ("RGBA", "RGBA;16B"),
  61. }
  62. _simple_palette = re.compile(b'^\xff*\x00\xff*$')
  63. # Maximum decompressed size for a iTXt or zTXt chunk.
  64. # Eliminates decompression bombs where compressed chunks can expand 1000x
  65. MAX_TEXT_CHUNK = ImageFile.SAFEBLOCK
  66. # Set the maximum total text chunk size.
  67. MAX_TEXT_MEMORY = 64 * MAX_TEXT_CHUNK
  68. def _safe_zlib_decompress(s):
  69. dobj = zlib.decompressobj()
  70. plaintext = dobj.decompress(s, MAX_TEXT_CHUNK)
  71. if dobj.unconsumed_tail:
  72. raise ValueError("Decompressed Data Too Large")
  73. return plaintext
  74. def _crc32(data, seed=0):
  75. return zlib.crc32(data, seed) & 0xffffffff
  76. # --------------------------------------------------------------------
  77. # Support classes. Suitable for PNG and related formats like MNG etc.
  78. class ChunkStream(object):
  79. def __init__(self, fp):
  80. self.fp = fp
  81. self.queue = []
  82. def read(self):
  83. "Fetch a new chunk. Returns header information."
  84. cid = None
  85. if self.queue:
  86. cid, pos, length = self.queue.pop()
  87. self.fp.seek(pos)
  88. else:
  89. s = self.fp.read(8)
  90. cid = s[4:]
  91. pos = self.fp.tell()
  92. length = i32(s)
  93. if not is_cid(cid):
  94. if not ImageFile.LOAD_TRUNCATED_IMAGES:
  95. raise SyntaxError("broken PNG file (chunk %s)" % repr(cid))
  96. return cid, pos, length
  97. def __enter__(self):
  98. return self
  99. def __exit__(self, *args):
  100. self.close()
  101. def close(self):
  102. self.queue = self.crc = self.fp = None
  103. def push(self, cid, pos, length):
  104. self.queue.append((cid, pos, length))
  105. def call(self, cid, pos, length):
  106. "Call the appropriate chunk handler"
  107. logger.debug("STREAM %r %s %s", cid, pos, length)
  108. return getattr(self, "chunk_" + cid.decode('ascii'))(pos, length)
  109. def crc(self, cid, data):
  110. "Read and verify checksum"
  111. # Skip CRC checks for ancillary chunks if allowed to load truncated
  112. # images
  113. # 5th byte of first char is 1 [specs, section 5.4]
  114. if ImageFile.LOAD_TRUNCATED_IMAGES and (i8(cid[0]) >> 5 & 1):
  115. self.crc_skip(cid, data)
  116. return
  117. try:
  118. crc1 = _crc32(data, _crc32(cid))
  119. crc2 = i32(self.fp.read(4))
  120. if crc1 != crc2:
  121. raise SyntaxError("broken PNG file (bad header checksum in %r)"
  122. % cid)
  123. except struct.error:
  124. raise SyntaxError("broken PNG file (incomplete checksum in %r)"
  125. % cid)
  126. def crc_skip(self, cid, data):
  127. "Read checksum. Used if the C module is not present"
  128. self.fp.read(4)
  129. def verify(self, endchunk=b"IEND"):
  130. # Simple approach; just calculate checksum for all remaining
  131. # blocks. Must be called directly after open.
  132. cids = []
  133. while True:
  134. try:
  135. cid, pos, length = self.read()
  136. except struct.error:
  137. raise IOError("truncated PNG file")
  138. if cid == endchunk:
  139. break
  140. self.crc(cid, ImageFile._safe_read(self.fp, length))
  141. cids.append(cid)
  142. return cids
  143. class iTXt(str):
  144. """
  145. Subclass of string to allow iTXt chunks to look like strings while
  146. keeping their extra information
  147. """
  148. @staticmethod
  149. def __new__(cls, text, lang, tkey):
  150. """
  151. :param cls: the class to use when creating the instance
  152. :param text: value for this key
  153. :param lang: language code
  154. :param tkey: UTF-8 version of the key name
  155. """
  156. self = str.__new__(cls, text)
  157. self.lang = lang
  158. self.tkey = tkey
  159. return self
  160. class PngInfo(object):
  161. """
  162. PNG chunk container (for use with save(pnginfo=))
  163. """
  164. def __init__(self):
  165. self.chunks = []
  166. def add(self, cid, data):
  167. """Appends an arbitrary chunk. Use with caution.
  168. :param cid: a byte string, 4 bytes long.
  169. :param data: a byte string of the encoded data
  170. """
  171. self.chunks.append((cid, data))
  172. def add_itxt(self, key, value, lang="", tkey="", zip=False):
  173. """Appends an iTXt chunk.
  174. :param key: latin-1 encodable text key name
  175. :param value: value for this key
  176. :param lang: language code
  177. :param tkey: UTF-8 version of the key name
  178. :param zip: compression flag
  179. """
  180. if not isinstance(key, bytes):
  181. key = key.encode("latin-1", "strict")
  182. if not isinstance(value, bytes):
  183. value = value.encode("utf-8", "strict")
  184. if not isinstance(lang, bytes):
  185. lang = lang.encode("utf-8", "strict")
  186. if not isinstance(tkey, bytes):
  187. tkey = tkey.encode("utf-8", "strict")
  188. if zip:
  189. self.add(b"iTXt", key + b"\0\x01\0" + lang + b"\0" + tkey + b"\0" +
  190. zlib.compress(value))
  191. else:
  192. self.add(b"iTXt", key + b"\0\0\0" + lang + b"\0" + tkey + b"\0" +
  193. value)
  194. def add_text(self, key, value, zip=False):
  195. """Appends a text chunk.
  196. :param key: latin-1 encodable text key name
  197. :param value: value for this key, text or an
  198. :py:class:`PIL.PngImagePlugin.iTXt` instance
  199. :param zip: compression flag
  200. """
  201. if isinstance(value, iTXt):
  202. return self.add_itxt(key, value, value.lang, value.tkey, zip=zip)
  203. # The tEXt chunk stores latin-1 text
  204. if not isinstance(value, bytes):
  205. try:
  206. value = value.encode('latin-1', 'strict')
  207. except UnicodeError:
  208. return self.add_itxt(key, value, zip=zip)
  209. if not isinstance(key, bytes):
  210. key = key.encode('latin-1', 'strict')
  211. if zip:
  212. self.add(b"zTXt", key + b"\0\0" + zlib.compress(value))
  213. else:
  214. self.add(b"tEXt", key + b"\0" + value)
  215. # --------------------------------------------------------------------
  216. # PNG image stream (IHDR/IEND)
  217. class PngStream(ChunkStream):
  218. def __init__(self, fp):
  219. ChunkStream.__init__(self, fp)
  220. # local copies of Image attributes
  221. self.im_info = {}
  222. self.im_text = {}
  223. self.im_size = (0, 0)
  224. self.im_mode = None
  225. self.im_tile = None
  226. self.im_palette = None
  227. self.text_memory = 0
  228. def check_text_memory(self, chunklen):
  229. self.text_memory += chunklen
  230. if self.text_memory > MAX_TEXT_MEMORY:
  231. raise ValueError("Too much memory used in text chunks: "
  232. "%s>MAX_TEXT_MEMORY" % self.text_memory)
  233. def chunk_iCCP(self, pos, length):
  234. # ICC profile
  235. s = ImageFile._safe_read(self.fp, length)
  236. # according to PNG spec, the iCCP chunk contains:
  237. # Profile name 1-79 bytes (character string)
  238. # Null separator 1 byte (null character)
  239. # Compression method 1 byte (0)
  240. # Compressed profile n bytes (zlib with deflate compression)
  241. i = s.find(b"\0")
  242. logger.debug("iCCP profile name %r", s[:i])
  243. logger.debug("Compression method %s", i8(s[i]))
  244. comp_method = i8(s[i])
  245. if comp_method != 0:
  246. raise SyntaxError("Unknown compression method %s in iCCP chunk" %
  247. comp_method)
  248. try:
  249. icc_profile = _safe_zlib_decompress(s[i+2:])
  250. except ValueError:
  251. if ImageFile.LOAD_TRUNCATED_IMAGES:
  252. icc_profile = None
  253. else:
  254. raise
  255. except zlib.error:
  256. icc_profile = None # FIXME
  257. self.im_info["icc_profile"] = icc_profile
  258. return s
  259. def chunk_IHDR(self, pos, length):
  260. # image header
  261. s = ImageFile._safe_read(self.fp, length)
  262. self.im_size = i32(s), i32(s[4:])
  263. try:
  264. self.im_mode, self.im_rawmode = _MODES[(i8(s[8]), i8(s[9]))]
  265. except:
  266. pass
  267. if i8(s[12]):
  268. self.im_info["interlace"] = 1
  269. if i8(s[11]):
  270. raise SyntaxError("unknown filter category")
  271. return s
  272. def chunk_IDAT(self, pos, length):
  273. # image data
  274. self.im_tile = [("zip", (0, 0)+self.im_size, pos, self.im_rawmode)]
  275. self.im_idat = length
  276. raise EOFError
  277. def chunk_IEND(self, pos, length):
  278. # end of PNG image
  279. raise EOFError
  280. def chunk_PLTE(self, pos, length):
  281. # palette
  282. s = ImageFile._safe_read(self.fp, length)
  283. if self.im_mode == "P":
  284. self.im_palette = "RGB", s
  285. return s
  286. def chunk_tRNS(self, pos, length):
  287. # transparency
  288. s = ImageFile._safe_read(self.fp, length)
  289. if self.im_mode == "P":
  290. if _simple_palette.match(s):
  291. # tRNS contains only one full-transparent entry,
  292. # other entries are full opaque
  293. i = s.find(b"\0")
  294. if i >= 0:
  295. self.im_info["transparency"] = i
  296. else:
  297. # otherwise, we have a byte string with one alpha value
  298. # for each palette entry
  299. self.im_info["transparency"] = s
  300. elif self.im_mode == "L":
  301. self.im_info["transparency"] = i16(s)
  302. elif self.im_mode == "RGB":
  303. self.im_info["transparency"] = i16(s), i16(s[2:]), i16(s[4:])
  304. return s
  305. def chunk_gAMA(self, pos, length):
  306. # gamma setting
  307. s = ImageFile._safe_read(self.fp, length)
  308. self.im_info["gamma"] = i32(s) / 100000.0
  309. return s
  310. def chunk_cHRM(self, pos, length):
  311. # chromaticity, 8 unsigned ints, actual value is scaled by 100,000
  312. # WP x,y, Red x,y, Green x,y Blue x,y
  313. s = ImageFile._safe_read(self.fp, length)
  314. raw_vals = struct.unpack('>%dI' % (len(s) // 4), s)
  315. self.im_info['chromaticity'] = tuple(elt/100000.0 for elt in raw_vals)
  316. return s
  317. def chunk_sRGB(self, pos, length):
  318. # srgb rendering intent, 1 byte
  319. # 0 perceptual
  320. # 1 relative colorimetric
  321. # 2 saturation
  322. # 3 absolute colorimetric
  323. s = ImageFile._safe_read(self.fp, length)
  324. self.im_info['srgb'] = i8(s)
  325. return s
  326. def chunk_pHYs(self, pos, length):
  327. # pixels per unit
  328. s = ImageFile._safe_read(self.fp, length)
  329. px, py = i32(s), i32(s[4:])
  330. unit = i8(s[8])
  331. if unit == 1: # meter
  332. dpi = int(px * 0.0254 + 0.5), int(py * 0.0254 + 0.5)
  333. self.im_info["dpi"] = dpi
  334. elif unit == 0:
  335. self.im_info["aspect"] = px, py
  336. return s
  337. def chunk_tEXt(self, pos, length):
  338. # text
  339. s = ImageFile._safe_read(self.fp, length)
  340. try:
  341. k, v = s.split(b"\0", 1)
  342. except ValueError:
  343. # fallback for broken tEXt tags
  344. k = s
  345. v = b""
  346. if k:
  347. if py3:
  348. k = k.decode('latin-1', 'strict')
  349. v = v.decode('latin-1', 'replace')
  350. self.im_info[k] = self.im_text[k] = v
  351. self.check_text_memory(len(v))
  352. return s
  353. def chunk_zTXt(self, pos, length):
  354. # compressed text
  355. s = ImageFile._safe_read(self.fp, length)
  356. try:
  357. k, v = s.split(b"\0", 1)
  358. except ValueError:
  359. k = s
  360. v = b""
  361. if v:
  362. comp_method = i8(v[0])
  363. else:
  364. comp_method = 0
  365. if comp_method != 0:
  366. raise SyntaxError("Unknown compression method %s in zTXt chunk" %
  367. comp_method)
  368. try:
  369. v = _safe_zlib_decompress(v[1:])
  370. except ValueError:
  371. if ImageFile.LOAD_TRUNCATED_IMAGES:
  372. v = b""
  373. else:
  374. raise
  375. except zlib.error:
  376. v = b""
  377. if k:
  378. if py3:
  379. k = k.decode('latin-1', 'strict')
  380. v = v.decode('latin-1', 'replace')
  381. self.im_info[k] = self.im_text[k] = v
  382. self.check_text_memory(len(v))
  383. return s
  384. def chunk_iTXt(self, pos, length):
  385. # international text
  386. r = s = ImageFile._safe_read(self.fp, length)
  387. try:
  388. k, r = r.split(b"\0", 1)
  389. except ValueError:
  390. return s
  391. if len(r) < 2:
  392. return s
  393. cf, cm, r = i8(r[0]), i8(r[1]), r[2:]
  394. try:
  395. lang, tk, v = r.split(b"\0", 2)
  396. except ValueError:
  397. return s
  398. if cf != 0:
  399. if cm == 0:
  400. try:
  401. v = _safe_zlib_decompress(v)
  402. except ValueError:
  403. if ImageFile.LOAD_TRUNCATED_IMAGES:
  404. return s
  405. else:
  406. raise
  407. except zlib.error:
  408. return s
  409. else:
  410. return s
  411. if py3:
  412. try:
  413. k = k.decode("latin-1", "strict")
  414. lang = lang.decode("utf-8", "strict")
  415. tk = tk.decode("utf-8", "strict")
  416. v = v.decode("utf-8", "strict")
  417. except UnicodeError:
  418. return s
  419. self.im_info[k] = self.im_text[k] = iTXt(v, lang, tk)
  420. self.check_text_memory(len(v))
  421. return s
  422. # --------------------------------------------------------------------
  423. # PNG reader
  424. def _accept(prefix):
  425. return prefix[:8] == _MAGIC
  426. ##
  427. # Image plugin for PNG images.
  428. class PngImageFile(ImageFile.ImageFile):
  429. format = "PNG"
  430. format_description = "Portable network graphics"
  431. def _open(self):
  432. if self.fp.read(8) != _MAGIC:
  433. raise SyntaxError("not a PNG file")
  434. #
  435. # Parse headers up to the first IDAT chunk
  436. self.png = PngStream(self.fp)
  437. while True:
  438. #
  439. # get next chunk
  440. cid, pos, length = self.png.read()
  441. try:
  442. s = self.png.call(cid, pos, length)
  443. except EOFError:
  444. break
  445. except AttributeError:
  446. logger.debug("%r %s %s (unknown)", cid, pos, length)
  447. s = ImageFile._safe_read(self.fp, length)
  448. self.png.crc(cid, s)
  449. #
  450. # Copy relevant attributes from the PngStream. An alternative
  451. # would be to let the PngStream class modify these attributes
  452. # directly, but that introduces circular references which are
  453. # difficult to break if things go wrong in the decoder...
  454. # (believe me, I've tried ;-)
  455. self.mode = self.png.im_mode
  456. self._size = self.png.im_size
  457. self.info = self.png.im_info
  458. self.text = self.png.im_text # experimental
  459. self.tile = self.png.im_tile
  460. if self.png.im_palette:
  461. rawmode, data = self.png.im_palette
  462. self.palette = ImagePalette.raw(rawmode, data)
  463. self.__idat = length # used by load_read()
  464. def verify(self):
  465. "Verify PNG file"
  466. if self.fp is None:
  467. raise RuntimeError("verify must be called directly after open")
  468. # back up to beginning of IDAT block
  469. self.fp.seek(self.tile[0][2] - 8)
  470. self.png.verify()
  471. self.png.close()
  472. self.fp = None
  473. def load_prepare(self):
  474. "internal: prepare to read PNG file"
  475. if self.info.get("interlace"):
  476. self.decoderconfig = self.decoderconfig + (1,)
  477. ImageFile.ImageFile.load_prepare(self)
  478. def load_read(self, read_bytes):
  479. "internal: read more image data"
  480. while self.__idat == 0:
  481. # end of chunk, skip forward to next one
  482. self.fp.read(4) # CRC
  483. cid, pos, length = self.png.read()
  484. if cid not in [b"IDAT", b"DDAT"]:
  485. self.png.push(cid, pos, length)
  486. return b""
  487. self.__idat = length # empty chunks are allowed
  488. # read more data from this chunk
  489. if read_bytes <= 0:
  490. read_bytes = self.__idat
  491. else:
  492. read_bytes = min(read_bytes, self.__idat)
  493. self.__idat = self.__idat - read_bytes
  494. return self.fp.read(read_bytes)
  495. def load_end(self):
  496. "internal: finished reading image data"
  497. self.png.close()
  498. self.png = None
  499. # --------------------------------------------------------------------
  500. # PNG writer
  501. _OUTMODES = {
  502. # supported PIL modes, and corresponding rawmodes/bits/color combinations
  503. "1": ("1", b'\x01\x00'),
  504. "L;1": ("L;1", b'\x01\x00'),
  505. "L;2": ("L;2", b'\x02\x00'),
  506. "L;4": ("L;4", b'\x04\x00'),
  507. "L": ("L", b'\x08\x00'),
  508. "LA": ("LA", b'\x08\x04'),
  509. "I": ("I;16B", b'\x10\x00'),
  510. "P;1": ("P;1", b'\x01\x03'),
  511. "P;2": ("P;2", b'\x02\x03'),
  512. "P;4": ("P;4", b'\x04\x03'),
  513. "P": ("P", b'\x08\x03'),
  514. "RGB": ("RGB", b'\x08\x02'),
  515. "RGBA": ("RGBA", b'\x08\x06'),
  516. }
  517. def putchunk(fp, cid, *data):
  518. """Write a PNG chunk (including CRC field)"""
  519. data = b"".join(data)
  520. fp.write(o32(len(data)) + cid)
  521. fp.write(data)
  522. crc = _crc32(data, _crc32(cid))
  523. fp.write(o32(crc))
  524. class _idat(object):
  525. # wrap output from the encoder in IDAT chunks
  526. def __init__(self, fp, chunk):
  527. self.fp = fp
  528. self.chunk = chunk
  529. def write(self, data):
  530. self.chunk(self.fp, b"IDAT", data)
  531. def _save(im, fp, filename, chunk=putchunk):
  532. # save an image to disk (called by the save method)
  533. mode = im.mode
  534. if mode == "P":
  535. #
  536. # attempt to minimize storage requirements for palette images
  537. if "bits" in im.encoderinfo:
  538. # number of bits specified by user
  539. colors = 1 << im.encoderinfo["bits"]
  540. else:
  541. # check palette contents
  542. if im.palette:
  543. colors = max(min(len(im.palette.getdata()[1])//3, 256), 2)
  544. else:
  545. colors = 256
  546. if colors <= 2:
  547. bits = 1
  548. elif colors <= 4:
  549. bits = 2
  550. elif colors <= 16:
  551. bits = 4
  552. else:
  553. bits = 8
  554. if bits != 8:
  555. mode = "%s;%d" % (mode, bits)
  556. # encoder options
  557. im.encoderconfig = (im.encoderinfo.get("optimize", False),
  558. im.encoderinfo.get("compress_level", -1),
  559. im.encoderinfo.get("compress_type", -1),
  560. im.encoderinfo.get("dictionary", b""))
  561. # get the corresponding PNG mode
  562. try:
  563. rawmode, mode = _OUTMODES[mode]
  564. except KeyError:
  565. raise IOError("cannot write mode %s as PNG" % mode)
  566. #
  567. # write minimal PNG file
  568. fp.write(_MAGIC)
  569. chunk(fp, b"IHDR",
  570. o32(im.size[0]), o32(im.size[1]), # 0: size
  571. mode, # 8: depth/type
  572. b'\0', # 10: compression
  573. b'\0', # 11: filter category
  574. b'\0') # 12: interlace flag
  575. chunks = [b"cHRM", b"gAMA", b"sBIT", b"sRGB", b"tIME"]
  576. icc = im.encoderinfo.get("icc_profile", im.info.get("icc_profile"))
  577. if icc:
  578. # ICC profile
  579. # according to PNG spec, the iCCP chunk contains:
  580. # Profile name 1-79 bytes (character string)
  581. # Null separator 1 byte (null character)
  582. # Compression method 1 byte (0)
  583. # Compressed profile n bytes (zlib with deflate compression)
  584. name = b"ICC Profile"
  585. data = name + b"\0\0" + zlib.compress(icc)
  586. chunk(fp, b"iCCP", data)
  587. # You must either have sRGB or iCCP.
  588. # Disallow sRGB chunks when an iCCP-chunk has been emitted.
  589. chunks.remove(b"sRGB")
  590. info = im.encoderinfo.get("pnginfo")
  591. if info:
  592. chunks_multiple_allowed = [b"sPLT", b"iTXt", b"tEXt", b"zTXt"]
  593. for cid, data in info.chunks:
  594. if cid in chunks:
  595. chunks.remove(cid)
  596. chunk(fp, cid, data)
  597. elif cid in chunks_multiple_allowed:
  598. chunk(fp, cid, data)
  599. if im.mode == "P":
  600. palette_byte_number = (2 ** bits) * 3
  601. palette_bytes = im.im.getpalette("RGB")[:palette_byte_number]
  602. while len(palette_bytes) < palette_byte_number:
  603. palette_bytes += b'\0'
  604. chunk(fp, b"PLTE", palette_bytes)
  605. transparency = im.encoderinfo.get('transparency',
  606. im.info.get('transparency', None))
  607. if transparency or transparency == 0:
  608. if im.mode == "P":
  609. # limit to actual palette size
  610. alpha_bytes = 2**bits
  611. if isinstance(transparency, bytes):
  612. chunk(fp, b"tRNS", transparency[:alpha_bytes])
  613. else:
  614. transparency = max(0, min(255, transparency))
  615. alpha = b'\xFF' * transparency + b'\0'
  616. chunk(fp, b"tRNS", alpha[:alpha_bytes])
  617. elif im.mode == "L":
  618. transparency = max(0, min(65535, transparency))
  619. chunk(fp, b"tRNS", o16(transparency))
  620. elif im.mode == "RGB":
  621. red, green, blue = transparency
  622. chunk(fp, b"tRNS", o16(red) + o16(green) + o16(blue))
  623. else:
  624. if "transparency" in im.encoderinfo:
  625. # don't bother with transparency if it's an RGBA
  626. # and it's in the info dict. It's probably just stale.
  627. raise IOError("cannot use transparency for this mode")
  628. else:
  629. if im.mode == "P" and im.im.getpalettemode() == "RGBA":
  630. alpha = im.im.getpalette("RGBA", "A")
  631. alpha_bytes = 2**bits
  632. chunk(fp, b"tRNS", alpha[:alpha_bytes])
  633. dpi = im.encoderinfo.get("dpi")
  634. if dpi:
  635. chunk(fp, b"pHYs",
  636. o32(int(dpi[0] / 0.0254 + 0.5)),
  637. o32(int(dpi[1] / 0.0254 + 0.5)),
  638. b'\x01')
  639. info = im.encoderinfo.get("pnginfo")
  640. if info:
  641. chunks = [b"bKGD", b"hIST"]
  642. for cid, data in info.chunks:
  643. if cid in chunks:
  644. chunks.remove(cid)
  645. chunk(fp, cid, data)
  646. ImageFile._save(im, _idat(fp, chunk),
  647. [("zip", (0, 0)+im.size, 0, rawmode)])
  648. chunk(fp, b"IEND", b"")
  649. if hasattr(fp, "flush"):
  650. fp.flush()
  651. # --------------------------------------------------------------------
  652. # PNG chunk converter
  653. def getchunks(im, **params):
  654. """Return a list of PNG chunks representing this image."""
  655. class collector(object):
  656. data = []
  657. def write(self, data):
  658. pass
  659. def append(self, chunk):
  660. self.data.append(chunk)
  661. def append(fp, cid, *data):
  662. data = b"".join(data)
  663. crc = o32(_crc32(data, _crc32(cid)))
  664. fp.append((cid, data, crc))
  665. fp = collector()
  666. try:
  667. im.encoderinfo = params
  668. _save(im, fp, None, append)
  669. finally:
  670. del im.encoderinfo
  671. return fp.data
  672. # --------------------------------------------------------------------
  673. # Registry
  674. Image.register_open(PngImageFile.format, PngImageFile, _accept)
  675. Image.register_save(PngImageFile.format, _save)
  676. Image.register_extension(PngImageFile.format, ".png")
  677. Image.register_mime(PngImageFile.format, "image/png")