WmfImagePlugin.py 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178
  1. #
  2. # The Python Imaging Library
  3. # $Id$
  4. #
  5. # WMF stub codec
  6. #
  7. # history:
  8. # 1996-12-14 fl Created
  9. # 2004-02-22 fl Turned into a stub driver
  10. # 2004-02-23 fl Added EMF support
  11. #
  12. # Copyright (c) Secret Labs AB 1997-2004. All rights reserved.
  13. # Copyright (c) Fredrik Lundh 1996.
  14. #
  15. # See the README file for information on usage and redistribution.
  16. #
  17. # WMF/EMF reference documentation:
  18. # https://winprotocoldoc.blob.core.windows.net/productionwindowsarchives/MS-WMF/[MS-WMF].pdf
  19. # http://wvware.sourceforge.net/caolan/index.html
  20. # http://wvware.sourceforge.net/caolan/ora-wmf.html
  21. from . import Image, ImageFile
  22. from ._binary import i16le as word
  23. from ._binary import i32le as dword
  24. from ._binary import si16le as short
  25. from ._binary import si32le as _long
  26. _handler = None
  27. def register_handler(handler):
  28. """
  29. Install application-specific WMF image handler.
  30. :param handler: Handler object.
  31. """
  32. global _handler
  33. _handler = handler
  34. if hasattr(Image.core, "drawwmf"):
  35. # install default handler (windows only)
  36. class WmfHandler:
  37. def open(self, im):
  38. im.mode = "RGB"
  39. self.bbox = im.info["wmf_bbox"]
  40. def load(self, im):
  41. im.fp.seek(0) # rewind
  42. return Image.frombytes(
  43. "RGB",
  44. im.size,
  45. Image.core.drawwmf(im.fp.read(), im.size, self.bbox),
  46. "raw",
  47. "BGR",
  48. (im.size[0] * 3 + 3) & -4,
  49. -1,
  50. )
  51. register_handler(WmfHandler())
  52. #
  53. # --------------------------------------------------------------------
  54. # Read WMF file
  55. def _accept(prefix):
  56. return (
  57. prefix[:6] == b"\xd7\xcd\xc6\x9a\x00\x00" or prefix[:4] == b"\x01\x00\x00\x00"
  58. )
  59. ##
  60. # Image plugin for Windows metafiles.
  61. class WmfStubImageFile(ImageFile.StubImageFile):
  62. format = "WMF"
  63. format_description = "Windows Metafile"
  64. def _open(self):
  65. self._inch = None
  66. # check placable header
  67. s = self.fp.read(80)
  68. if s[:6] == b"\xd7\xcd\xc6\x9a\x00\x00":
  69. # placeable windows metafile
  70. # get units per inch
  71. self._inch = word(s, 14)
  72. # get bounding box
  73. x0 = short(s, 6)
  74. y0 = short(s, 8)
  75. x1 = short(s, 10)
  76. y1 = short(s, 12)
  77. # normalize size to 72 dots per inch
  78. self.info["dpi"] = 72
  79. size = (
  80. (x1 - x0) * self.info["dpi"] // self._inch,
  81. (y1 - y0) * self.info["dpi"] // self._inch,
  82. )
  83. self.info["wmf_bbox"] = x0, y0, x1, y1
  84. # sanity check (standard metafile header)
  85. if s[22:26] != b"\x01\x00\t\x00":
  86. raise SyntaxError("Unsupported WMF file format")
  87. elif dword(s) == 1 and s[40:44] == b" EMF":
  88. # enhanced metafile
  89. # get bounding box
  90. x0 = _long(s, 8)
  91. y0 = _long(s, 12)
  92. x1 = _long(s, 16)
  93. y1 = _long(s, 20)
  94. # get frame (in 0.01 millimeter units)
  95. frame = _long(s, 24), _long(s, 28), _long(s, 32), _long(s, 36)
  96. size = x1 - x0, y1 - y0
  97. # calculate dots per inch from bbox and frame
  98. xdpi = 2540.0 * (x1 - y0) / (frame[2] - frame[0])
  99. ydpi = 2540.0 * (y1 - y0) / (frame[3] - frame[1])
  100. self.info["wmf_bbox"] = x0, y0, x1, y1
  101. if xdpi == ydpi:
  102. self.info["dpi"] = xdpi
  103. else:
  104. self.info["dpi"] = xdpi, ydpi
  105. else:
  106. raise SyntaxError("Unsupported file format")
  107. self.mode = "RGB"
  108. self._size = size
  109. loader = self._load()
  110. if loader:
  111. loader.open(self)
  112. def _load(self):
  113. return _handler
  114. def load(self, dpi=None):
  115. if dpi is not None and self._inch is not None:
  116. self.info["dpi"] = dpi
  117. x0, y0, x1, y1 = self.info["wmf_bbox"]
  118. self._size = (
  119. (x1 - x0) * self.info["dpi"] // self._inch,
  120. (y1 - y0) * self.info["dpi"] // self._inch,
  121. )
  122. super().load()
  123. def _save(im, fp, filename):
  124. if _handler is None or not hasattr(_handler, "save"):
  125. raise OSError("WMF save handler not installed")
  126. _handler.save(im, fp, filename)
  127. #
  128. # --------------------------------------------------------------------
  129. # Registry stuff
  130. Image.register_open(WmfStubImageFile.format, WmfStubImageFile, _accept)
  131. Image.register_save(WmfStubImageFile.format, _save)
  132. Image.register_extensions(WmfStubImageFile.format, [".wmf", ".emf"])