Hide keyboard shortcuts

Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

1""":mod:`wand.api` --- Low-level interfaces 

2~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 

3 

4.. versionchanged:: 0.1.10 

5 Changed to throw :exc:`~exceptions.ImportError` instead of 

6 :exc:`~exceptions.AttributeError` when the shared library fails to load. 

7 

8""" 

9import ctypes 

10import ctypes.util 

11import itertools 

12import os 

13import os.path 

14import platform 

15import sys 

16import traceback 

17# Forward import for backwords compatibility. 

18from .cdefs.structures import (AffineMatrix, MagickPixelPacket, PixelInfo, 

19 PointInfo) 

20if platform.system() == "Windows": 

21 try: 

22 import winreg 

23 except ImportError: 

24 import _winreg as winreg 

25 

26__all__ = ('AffineMatrix', 'MagickPixelPacket', 'library', 'libc', 'libmagick', 

27 'load_library', 'PixelInfo', 'PointInfo') 

28 

29 

30def library_paths(): 

31 """Iterates for library paths to try loading. The result paths are not 

32 guaranteed that they exist. 

33 

34 :returns: a pair of libwand and libmagick paths. they can be the same. 

35 path can be ``None`` as well 

36 :rtype: :class:`tuple` 

37 

38 """ 

39 libwand = None 

40 libmagick = None 

41 versions = '', '-7', '-7.Q8', '-7.Q16', '-6', '-Q16', '-Q8', '-6.Q16' 

42 options = '', 'HDRI', 'HDRI-2' 

43 system = platform.system() 

44 magick_home = os.environ.get('MAGICK_HOME') 

45 magick_suffix = os.environ.get('WAND_MAGICK_LIBRARY_SUFFIX') 

46 

47 if system == 'Windows': 

48 # ImageMagick installers normally install coder and filter DLLs in 

49 # subfolders, we need to add those folders to PATH, otherwise loading 

50 # the DLL later will fail. 

51 try: 

52 with winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, 

53 r"SOFTWARE\ImageMagick\Current") as reg_key: 

54 libPath = winreg.QueryValueEx(reg_key, "LibPath") 

55 coderPath = winreg.QueryValueEx(reg_key, "CoderModulesPath") 

56 filterPath = winreg.QueryValueEx(reg_key, "FilterModulesPath") 

57 magick_home = libPath[0] 

58 os.environ['PATH'] += str((';' + libPath[0] + ";" + 

59 coderPath[0] + ";" + filterPath[0])) 

60 except OSError: 

61 # otherwise use MAGICK_HOME, and we assume the coder and 

62 # filter DLLs are in the same directory 

63 pass 

64 

65 def magick_path(path): 

66 return os.path.join(magick_home, *path) 

67 combinations = itertools.product(versions, options) 

68 suffixes = list() 

69 if magick_suffix: 

70 suffixes = str(magick_suffix).split(';') 

71 # We need to convert the ``combinations`` generator to a list so we can 

72 # iterate over it twice. 

73 suffixes.extend(list(version + option for version, option in combinations)) 

74 if magick_home: 

75 # exhaustively search for libraries in magick_home before calling 

76 # find_library. 

77 for suffix in suffixes: 

78 # On Windows, the API is split between two libs. On other 

79 # platforms, it's all contained in one. 

80 if system == 'Windows': 

81 libwand = 'CORE_RL_wand_{0}.dll'.format(suffix), 

82 libmagick = 'CORE_RL_magick_{0}.dll'.format(suffix), 

83 yield magick_path(libwand), magick_path(libmagick) 

84 libwand = 'CORE_RL_MagickWand_{0}.dll'.format(suffix), 

85 libmagick = 'CORE_RL_MagickCore_{0}.dll'.format(suffix), 

86 yield magick_path(libwand), magick_path(libmagick) 

87 libwand = 'libMagickWand{0}.dll'.format(suffix), 

88 libmagick = 'libMagickCore{0}.dll'.format(suffix), 

89 yield magick_path(libwand), magick_path(libmagick) 

90 elif system == 'Darwin': 

91 libwand = 'lib', 'libMagickWand{0}.dylib'.format(suffix), 

92 yield magick_path(libwand), magick_path(libwand) 

93 else: 

94 libwand = 'lib', 'libMagickWand{0}.so'.format(suffix), 

95 libmagick = 'lib', 'libMagickCore{0}.so'.format(suffix), 

96 yield magick_path(libwand), magick_path(libmagick) 

97 libwand = 'lib', 'libMagickWand{0}.so.6'.format(suffix), 

98 libmagick = 'lib', 'libMagickCore{0}.so.6'.format(suffix), 

99 yield magick_path(libwand), magick_path(libmagick) 

100 for suffix in suffixes: 

101 if system == 'Windows': 

102 libwand = ctypes.util.find_library('CORE_RL_wand_' + suffix) 

103 libmagick = ctypes.util.find_library('CORE_RL_magick_' + suffix) 

104 yield libwand, libmagick 

105 libwand = ctypes.util.find_library('CORE_RL_MagickWand_' + suffix) 

106 libmagick = ctypes.util.find_library( 

107 'CORE_RL_MagickCore_' + suffix 

108 ) 

109 yield libwand, libmagick 

110 libwand = ctypes.util.find_library('libMagickWand' + suffix) 

111 libmagick = ctypes.util.find_library('libMagickCore' + suffix) 

112 yield libwand, libmagick 

113 else: 

114 libwand = ctypes.util.find_library('MagickWand' + suffix) 

115 yield libwand, libwand 

116 

117 

118def load_library(): 

119 """Loads the MagickWand library. 

120 

121 :returns: the MagickWand library and the ImageMagick library 

122 :rtype: :class:`ctypes.CDLL` 

123 

124 """ 

125 tried_paths = [] 

126 for libwand_path, libmagick_path in library_paths(): 

127 if libwand_path is None or libmagick_path is None: 

128 continue 

129 try: 

130 tried_paths.append(libwand_path) 

131 libwand = ctypes.CDLL(str(libwand_path)) 

132 if libwand_path == libmagick_path: 

133 libmagick = libwand 

134 else: 

135 tried_paths.append(libmagick_path) 

136 libmagick = ctypes.CDLL(str(libmagick_path)) 

137 except (IOError, OSError): 

138 continue 

139 return libwand, libmagick 

140 raise IOError('cannot find library; tried paths: ' + repr(tried_paths)) 

141 

142 

143try: 

144 # Preserve the module itself even if it fails to import 

145 sys.modules['wand._api'] = sys.modules['wand.api'] 

146except KeyError: 

147 # Loading the module locally or a non-standard setting 

148 pass 

149 

150try: 

151 libraries = load_library() 

152except (OSError, IOError): 

153 msg = 'http://docs.wand-py.org/en/latest/guide/install.html' 

154 if sys.platform.startswith(('dragonfly', 'freebsd')): 

155 msg = 'pkg install' 

156 elif sys.platform == 'win32': 

157 msg += '#install-imagemagick-on-windows' 

158 elif sys.platform == 'darwin': 

159 mac_pkgmgrs = {'brew': 'brew install freetype imagemagick', 

160 'port': 'port install imagemagick'} 

161 for pkgmgr in mac_pkgmgrs: 

162 with os.popen('which ' + pkgmgr) as f: 

163 if f.read().strip(): 

164 msg = mac_pkgmgrs[pkgmgr] 

165 break 

166 else: 

167 msg += '#install-imagemagick-on-mac' 

168 elif hasattr(platform, 'linux_distribution'): 

169 distname, _, __ = platform.linux_distribution() 

170 distname = (distname or '').lower() 

171 if distname in ('debian', 'ubuntu'): 

172 msg = 'apt-get install libmagickwand-dev' 

173 elif distname in ('fedora', 'centos', 'redhat'): 

174 msg = 'yum install ImageMagick-devel' 

175 raise ImportError('MagickWand shared library not found.\n' 

176 'You probably had not installed ImageMagick library.\n' 

177 'Try to install:\n ' + msg) 

178 

179#: (:class:`ctypes.CDLL`) The MagickWand library. 

180library = libraries[0] 

181 

182#: (:class:`ctypes.CDLL`) The ImageMagick library. It is the same with 

183#: :data:`library` on platforms other than Windows. 

184#: 

185#: .. versionadded:: 0.1.10 

186libmagick = libraries[1] 

187 

188try: 

189 from wand.cdefs import (core, magick_wand, magick_image, magick_property, 

190 pixel_iterator, pixel_wand, drawing_wand) 

191 

192 core.load(libmagick) 

193 # Let's get the magick-version number to pass to load methods. 

194 IM_VERSION = ctypes.c_size_t() 

195 libmagick.GetMagickVersion(ctypes.byref(IM_VERSION)) 

196 # Query Quantum Depth (i.e. Q8, Q16, ... etc). 

197 IM_QUANTUM_DEPTH = ctypes.c_size_t() 

198 libmagick.GetMagickQuantumDepth(ctypes.byref(IM_QUANTUM_DEPTH)) 

199 # Does the library support HDRI? 

200 IM_HDRI = 'HDRI' in str(libmagick.GetMagickFeatures()) 

201 core.load_with_version(libmagick, IM_VERSION.value) 

202 magick_wand.load(library, IM_VERSION.value) 

203 magick_property.load(library, IM_VERSION.value) 

204 magick_image.load(library, IM_VERSION.value) 

205 pixel_iterator.load(library, IM_VERSION.value) 

206 pixel_wand.load(library, IM_VERSION.value, IM_QUANTUM_DEPTH.value, IM_HDRI) 

207 drawing_wand.load(library, IM_VERSION.value) 

208 del IM_HDRI, IM_QUANTUM_DEPTH, IM_VERSION 

209 

210except AttributeError: 

211 raise ImportError('MagickWand shared library not found or incompatible\n' 

212 'Original exception was raised in:\n' + 

213 traceback.format_exc()) 

214 

215#: (:class:`ctypes.CDLL`) The C standard library. 

216libc = None 

217 

218if platform.system() == 'Windows': 

219 msvcrt = ctypes.util.find_msvcrt() 

220 # workaround -- the newest visual studio DLL is named differently: 

221 if not msvcrt and '1900' in platform.python_compiler(): 

222 msvcrt = 'vcruntime140.dll' 

223 if msvcrt: 

224 libc = ctypes.CDLL(msvcrt) 

225else: 

226 libc_path = ctypes.util.find_library('c') 

227 if libc_path: 

228 libc = ctypes.cdll.LoadLibrary(libc_path) 

229 else: 

230 # Attempt to guess popular versions of libc 

231 libc_paths = ('libc.so.6', 'libc.so', 'libc.a', 'libc.dylib', 

232 '/usr/lib/libc.dylib') 

233 for libc_path in libc_paths: 

234 try: 

235 libc = ctypes.cdll.LoadLibrary(libc_path) 

236 break 

237 except (IOError, OSError): 

238 continue 

239 if libc: 

240 libc.fdopen.argtypes = [ctypes.c_int, ctypes.c_char_p] 

241 libc.fdopen.restype = ctypes.c_void_p 

242 libc.fflush.argtypes = [ctypes.c_void_p]