PngImagePlugin.py 27 KB

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