PpmImagePlugin.py 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164
  1. #
  2. # The Python Imaging Library.
  3. # $Id$
  4. #
  5. # PPM support for PIL
  6. #
  7. # History:
  8. # 96-03-24 fl Created
  9. # 98-03-06 fl Write RGBA images (as RGB, that is)
  10. #
  11. # Copyright (c) Secret Labs AB 1997-98.
  12. # Copyright (c) Fredrik Lundh 1996.
  13. #
  14. # See the README file for information on usage and redistribution.
  15. #
  16. from . import Image, ImageFile
  17. #
  18. # --------------------------------------------------------------------
  19. b_whitespace = b"\x20\x09\x0a\x0b\x0c\x0d"
  20. MODES = {
  21. # standard
  22. b"P4": "1",
  23. b"P5": "L",
  24. b"P6": "RGB",
  25. # extensions
  26. b"P0CMYK": "CMYK",
  27. # PIL extensions (for test purposes only)
  28. b"PyP": "P",
  29. b"PyRGBA": "RGBA",
  30. b"PyCMYK": "CMYK",
  31. }
  32. def _accept(prefix):
  33. return prefix[0:1] == b"P" and prefix[1] in b"0456y"
  34. ##
  35. # Image plugin for PBM, PGM, and PPM images.
  36. class PpmImageFile(ImageFile.ImageFile):
  37. format = "PPM"
  38. format_description = "Pbmplus image"
  39. def _token(self, s=b""):
  40. while True: # read until next whitespace
  41. c = self.fp.read(1)
  42. if not c or c in b_whitespace:
  43. break
  44. if c > b"\x79":
  45. raise ValueError("Expected ASCII value, found binary")
  46. s = s + c
  47. if len(s) > 9:
  48. raise ValueError("Expected int, got > 9 digits")
  49. return s
  50. def _open(self):
  51. # check magic
  52. s = self.fp.read(1)
  53. if s != b"P":
  54. raise SyntaxError("not a PPM file")
  55. magic_number = self._token(s)
  56. mode = MODES[magic_number]
  57. self.custom_mimetype = {
  58. b"P4": "image/x-portable-bitmap",
  59. b"P5": "image/x-portable-graymap",
  60. b"P6": "image/x-portable-pixmap",
  61. }.get(magic_number)
  62. if mode == "1":
  63. self.mode = "1"
  64. rawmode = "1;I"
  65. else:
  66. self.mode = rawmode = mode
  67. for ix in range(3):
  68. while True:
  69. while True:
  70. s = self.fp.read(1)
  71. if s not in b_whitespace:
  72. break
  73. if s == b"":
  74. raise ValueError("File does not extend beyond magic number")
  75. if s != b"#":
  76. break
  77. s = self.fp.readline()
  78. s = int(self._token(s))
  79. if ix == 0:
  80. xsize = s
  81. elif ix == 1:
  82. ysize = s
  83. if mode == "1":
  84. break
  85. elif ix == 2:
  86. # maxgrey
  87. if s > 255:
  88. if not mode == "L":
  89. raise ValueError(f"Too many colors for band: {s}")
  90. if s < 2 ** 16:
  91. self.mode = "I"
  92. rawmode = "I;16B"
  93. else:
  94. self.mode = "I"
  95. rawmode = "I;32B"
  96. self._size = xsize, ysize
  97. self.tile = [("raw", (0, 0, xsize, ysize), self.fp.tell(), (rawmode, 0, 1))]
  98. #
  99. # --------------------------------------------------------------------
  100. def _save(im, fp, filename):
  101. if im.mode == "1":
  102. rawmode, head = "1;I", b"P4"
  103. elif im.mode == "L":
  104. rawmode, head = "L", b"P5"
  105. elif im.mode == "I":
  106. if im.getextrema()[1] < 2 ** 16:
  107. rawmode, head = "I;16B", b"P5"
  108. else:
  109. rawmode, head = "I;32B", b"P5"
  110. elif im.mode == "RGB":
  111. rawmode, head = "RGB", b"P6"
  112. elif im.mode == "RGBA":
  113. rawmode, head = "RGB", b"P6"
  114. else:
  115. raise OSError(f"cannot write mode {im.mode} as PPM")
  116. fp.write(head + ("\n%d %d\n" % im.size).encode("ascii"))
  117. if head == b"P6":
  118. fp.write(b"255\n")
  119. if head == b"P5":
  120. if rawmode == "L":
  121. fp.write(b"255\n")
  122. elif rawmode == "I;16B":
  123. fp.write(b"65535\n")
  124. elif rawmode == "I;32B":
  125. fp.write(b"2147483648\n")
  126. ImageFile._save(im, fp, [("raw", (0, 0) + im.size, 0, (rawmode, 0, 1))])
  127. # ALTERNATIVE: save via builtin debug function
  128. # im._dump(filename)
  129. #
  130. # --------------------------------------------------------------------
  131. Image.register_open(PpmImageFile.format, PpmImageFile, _accept)
  132. Image.register_save(PpmImageFile.format, _save)
  133. Image.register_extensions(PpmImageFile.format, [".pbm", ".pgm", ".ppm", ".pnm"])
  134. Image.register_mime(PpmImageFile.format, "image/x-portable-anymap")