Merge branch 'dev' of github.com:tp4a/teleport into dev

pull/130/head
Apex Liu 2018-10-15 11:24:17 +08:00
commit cd6255178f
421 changed files with 101349 additions and 5246 deletions

View File

@ -1,8 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="PYTHON_MODULE" version="4">
<component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$" />
<orderEntry type="inheritedJdk" />
<content url="file://$MODULE_DIR$/builder" />
<orderEntry type="jdk" jdkName="py37" jdkType="Python SDK" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
<component name="TestRunnerService">

View File

@ -48,8 +48,9 @@ class BuilderBase:
self._build_openssl(file_name)
def _build_openssl(self, file_name):
_alt_ver = '_'.join(env.ver_openssl.split('.'))
_alt_ver = '_'.join(env.ver_ossl.split('.'))
if not utils.download_file('openssl source tarball', 'https://github.com/openssl/openssl/archive/OpenSSL_{}.zip'.format(_alt_ver), PATH_DOWNLOAD, file_name):
cc.e("can not download openssl source tarball.")
return False
else:
return True
@ -145,8 +146,9 @@ class BuilderWin(BuilderBase):
def _build_openssl(self, file_name):
cc.n('build openssl static library from source code... ')
_alt_ver = '_'.join(env.ver_ossl.split('.'))
if not utils.download_file('openssl source tarball', 'https://github.com/openssl/openssl/archive/OpenSSL_{}.zip'.format(_alt_ver), PATH_DOWNLOAD, file_name):
if not super()._build_openssl(file_name):
# _alt_ver = '_'.join(env.ver_ossl.split('.'))
# if not utils.download_file('openssl source tarball', 'https://github.com/openssl/openssl/archive/OpenSSL_{}.zip'.format(_alt_ver), PATH_DOWNLOAD, file_name):
return
_chk_output = [
@ -201,11 +203,7 @@ class BuilderWin(BuilderBase):
out_file_lib = os.path.join(self.LIBSSH_PATH_SRC, 'lib', ctx.target_path, 'ssh.lib')
out_file_dll = os.path.join(self.LIBSSH_PATH_SRC, 'lib', ctx.target_path, 'ssh.dll')
need_build = False
if not (os.path.exists(out_file_lib) and (os.path.exists(out_file_dll))):
need_build = True
if not need_build:
if os.path.exists(out_file_lib) and os.path.exists(out_file_dll):
cc.w('already exists, skip.')
return
cc.v('')
@ -495,10 +493,6 @@ class BuilderLinux(BuilderBase):
pass
os.chdir(old_p)
# utils.ensure_file_exists(os.path.join(self.LIBSSH_PATH_SRC, 'build', 'src', 'libssh.a'))
# utils.copy_file(os.path.join(self.LIBSSH_PATH_SRC, 'build', 'src'), os.path.join(self.PATH_RELEASE, 'lib'), 'libssh.a')
# utils.copy_ex(os.path.join(self.LIBSSH_PATH_SRC, 'include'), os.path.join(self.PATH_RELEASE, 'include'), 'libssh')
utils.ensure_file_exists(os.path.join(self.PATH_RELEASE, 'lib', 'libssh.a'))
files = os.listdir(os.path.join(self.PATH_RELEASE, 'lib'))
for i in files:
@ -506,23 +500,6 @@ class BuilderLinux(BuilderBase):
# use os.unlink() because some file should be a link.
os.unlink(os.path.join(self.PATH_RELEASE, 'lib', i))
# def _build_sqlite(self, file_name):
# if not os.path.exists(self.SQLITE_PATH_SRC):
# os.system('tar -zxvf "{}/{}" -C "{}"'.format(PATH_DOWNLOAD, file_name, self.PATH_TMP))
#
# cc.n('build sqlite static...', end='')
# if os.path.exists(os.path.join(self.PATH_RELEASE, 'lib', 'libsqlite3.a')):
# cc.w('already exists, skip.')
# return
# cc.v('')
#
# old_p = os.getcwd()
# os.chdir(self.SQLITE_PATH_SRC)
# os.system('./configure --prefix={}'.format(self.PATH_RELEASE))
# os.system('make')
# os.system('make install')
# os.chdir(old_p)
def fix_output(self):
pass
# remove .so files, otherwise will link to .so but not .a in default.
@ -541,7 +518,7 @@ class BuilderMacOS(BuilderBase):
def _init_path(self):
self.PATH_TMP = os.path.join(PATH_EXTERNAL, 'macos', 'tmp')
self.PATH_RELEASE = os.path.join(PATH_EXTERNAL, 'macos', 'release')
# self.OPENSSL_PATH_SRC = os.path.join(self.PATH_TMP, 'openssl-OpenSSL_{}'.format(env.ver_openssl.replace('.', '_')))
self.OPENSSL_PATH_SRC = os.path.join(self.PATH_TMP, 'openssl-OpenSSL_{}'.format(env.ver_ossl.replace('.', '_')))
self.LIBUV_PATH_SRC = os.path.join(self.PATH_TMP, 'libuv-{}'.format(env.ver_libuv))
self.MBEDTLS_PATH_SRC = os.path.join(self.PATH_TMP, 'mbedtls-mbedtls-{}'.format(env.ver_mbedtls))
self.LIBSSH_PATH_SRC = os.path.join(self.PATH_TMP, 'libssh-{}'.format(env.ver_libssh))
@ -572,24 +549,35 @@ class BuilderMacOS(BuilderBase):
cc.w('already exists, skip.')
def _build_openssl(self, file_name):
pass # we do not need build openssl anymore, because first time run build.sh we built Python, it include openssl.
#
# if not os.path.exists(self.OPENSSL_PATH_SRC):
# os.system('tar -zxvf "{}/{}" -C "{}"'.format(PATH_DOWNLOAD, file_name, self.PATH_TMP))
#
# cc.n('build openssl static...', end='')
# if os.path.exists(os.path.join(self.PATH_RELEASE, 'lib', 'libssl.a')):
# cc.w('already exists, skip.')
# return
#
# old_p = os.getcwd()
# os.chdir(self.OPENSSL_PATH_SRC)
# # os.system('./config --prefix={} --openssldir={}/openssl no-zlib no-shared'.format(self.PATH_RELEASE, self.PATH_RELEASE))
# # os.system('./Configure darwin64-x86_64-cc')
# os.system('./Configure darwin64-x86_64-cc --prefix={} --openssldir={}/openssl -fPIC no-zlib no-shared'.format(self.PATH_RELEASE, self.PATH_RELEASE))
# os.system('make')
# os.system('make install')
# os.chdir(old_p)
if not super()._build_openssl(file_name):
return
cc.n('prepare openssl source code...')
_alt_ver = '_'.join(env.ver_ossl.split('.'))
if not os.path.exists(self.OPENSSL_PATH_SRC):
# utils.unzip(os.path.join(PATH_DOWNLOAD, file_name), PATH_EXTERNAL)
os.system('unzip "{}/{}" -d "{}"'.format(PATH_DOWNLOAD, file_name, self.PATH_TMP))
# os.rename(os.path.join(PATH_EXTERNAL, 'openssl-OpenSSL_{}'.format(_alt_ver)), self.OPENSSL_PATH_SRC)
if not os.path.exists(self.OPENSSL_PATH_SRC):
raise RuntimeError('can not prepare openssl source code.')
else:
cc.w('already exists, skip.')
cc.n('build openssl static...', end='')
out_file_lib = os.path.join(self.PATH_RELEASE, 'lib', 'libssl.a')
if os.path.exists(out_file_lib):
cc.w('already exists, skip.')
return
cc.v('')
old_p = os.getcwd()
os.chdir(self.OPENSSL_PATH_SRC)
# os.system('./config --prefix={} --openssldir={}/openssl no-zlib no-shared'.format(self.PATH_RELEASE, self.PATH_RELEASE))
# os.system('./Configure darwin64-x86_64-cc')
os.system('./Configure darwin64-x86_64-cc --prefix={} --openssldir={}/openssl -fPIC no-zlib no-shared'.format(self.PATH_RELEASE, self.PATH_RELEASE))
os.system('make')
os.system('make install')
os.chdir(old_p)
def _build_libuv(self, file_name):
cc.n('prepare libuv source code...', end='')
@ -673,51 +661,21 @@ class BuilderMacOS(BuilderBase):
cc.v('')
build_path = os.path.join(self.LIBSSH_PATH_SRC, 'build')
# utils.makedirs(build_path)
# here is a bug in cmake v2.8.11 (default on ubuntu14), in FindOpenSSL.cmake,
# it parse opensslv.h, use regex like this:
# REGEX "^#define[\t ]+OPENSSL_VERSION_NUMBER[\t ]+0x([0-9a-fA-F])+.*")
# but in openssl-1.0.2h, the version define line is:
# # define OPENSSL_VERSION_NUMBER 0x1000208fL
# notice there is a space char between # and define, so find openssl always fail.
# old_p = os.getcwd()
# os.chdir(build_path)
# cmd = 'cmake' \
# ' -DCMAKE_INSTALL_PREFIX={}' \
# ' -D_OPENSSL_VERSION={}' \
# ' -DOPENSSL_INCLUDE_DIR={}/include' \
# ' -DOPENSSL_LIBRARIES={}/lib' \
# ' -DCMAKE_BUILD_TYPE=Release' \
# ' -DWITH_GSSAPI=OFF' \
# ' -DWITH_ZLIB=OFF' \
# ' -DWITH_STATIC_LIB=ON' \
# ' -DWITH_PCAP=OFF' \
# ' -DWITH_EXAMPLES=OFF' \
# ' -DWITH_NACL=OFF' \
# ' ..'.format(self.PATH_RELEASE, OPENSSL_VER, self.PATH_RELEASE, self.PATH_RELEASE)
# cc.n(cmd)
# os.system(cmd)
# # os.system('make ssh_static ssh_threads_static')
# os.system('make ssh_static')
# # os.system('make install')
# os.chdir(old_p)
cmake_define = ' -DCMAKE_INSTALL_PREFIX={prefix}' \
' -D_OPENSSL_VERSION={oss_ver}' \
' -DOPENSSL_INCLUDE_DIR={ossl_inc}' \
' -DOPENSSL_LIBRARIES={ossl_lib}' \
cmake_define = ' -DCMAKE_INSTALL_PREFIX={path_release}' \
' -DOPENSSL_INCLUDE_DIR={path_release}/include' \
' -DOPENSSL_LIBRARIES={path_release}/lib' \
' -DWITH_SFTP=ON' \
' -DWITH_SERVER=ON' \
' -DWITH_STATIC_LIB=ON' \
' -DWITH_GSSAPI=OFF' \
' -DWITH_ZLIB=OFF' \
' -DWITH_STATIC_LIB=ON' \
' -DWITH_PCAP=OFF' \
' -DWITH_TESTING=OFF' \
' -DWITH_CLIENT_TESTING=OFF' \
' -DUNIT_TESTING=OFF' \
' -DWITH_EXAMPLES=OFF' \
' -DWITH_BENCHMARKS=OFF' \
' -DWITH_NACL=OFF' \
''.format(prefix=self.PATH_RELEASE, oss_ver=env.ver_ossl_number, ossl_inc=env.path_ossl_inc, ossl_lib=env.path_ossl_lib)
''.format(path_release=self.PATH_RELEASE)
try:
utils.cmake(build_path, 'Release', False, cmake_define)
@ -727,32 +685,18 @@ class BuilderMacOS(BuilderBase):
# because make install will fail because we can not disable ssh_shared target,
# so we copy necessary files ourselves.
utils.ensure_file_exists(os.path.join(self.LIBSSH_PATH_SRC, 'build', 'src', 'libssh.a'))
utils.ensure_file_exists(os.path.join(self.LIBSSH_PATH_SRC, 'build', 'src', 'threads', 'libssh_threads.a'))
# utils.ensure_file_exists(os.path.join(self.LIBSSH_PATH_SRC, 'build', 'src', 'threads', 'libssh_threads.a'))
utils.copy_file(os.path.join(self.LIBSSH_PATH_SRC, 'build', 'src'), os.path.join(self.PATH_RELEASE, 'lib'), 'libssh.a')
utils.copy_file(os.path.join(self.LIBSSH_PATH_SRC, 'build', 'src', 'threads'), os.path.join(self.PATH_RELEASE, 'lib'), 'libssh_threads.a')
# utils.copy_file(os.path.join(self.LIBSSH_PATH_SRC, 'build', 'src', 'threads'), os.path.join(self.PATH_RELEASE, 'lib'), 'libssh_threads.a')
utils.copy_ex(os.path.join(self.LIBSSH_PATH_SRC, 'include'), os.path.join(self.PATH_RELEASE, 'include'), 'libssh')
# def _build_sqlite(self, file_name):
# if not os.path.exists(self.SQLITE_PATH_SRC):
# os.system('tar -zxvf "{}/{}" -C "{}"'.format(PATH_DOWNLOAD, file_name, self.PATH_TMP))
#
# cc.n('build sqlite static...', end='')
# if os.path.exists(os.path.join(self.PATH_RELEASE, 'lib', 'libsqlite3.a')):
# cc.w('already exists, skip.')
# return
# cc.v('')
#
# old_p = os.getcwd()
# os.chdir(self.SQLITE_PATH_SRC)
# os.system('./configure --prefix={}'.format(self.PATH_RELEASE))
# os.system('make')
# os.system('make install')
# os.chdir(old_p)
def _prepare_python(self):
pass
def fix_output(self):
# remove .so files, otherwise will link to .so but not .a in default.
# rm = ['libsqlite3.la', 'libsqlite3.so.0', 'libsqlite3.so', 'libsqlite3.so.0.8.6', 'libuv.la', 'libuv.so.1', 'libuv.so', 'libuv.so.1.0.0']
rm = ['libuv.la', 'libuv.so.1', 'libuv.so', 'libuv.so.1.0.0']
rm = ['libuv.la', 'libuv.dylib', 'libuv.so.1', 'libuv.so', 'libuv.so.1.0.0']
for i in rm:
_path = os.path.join(self.PATH_RELEASE, 'lib', i)
if os.path.exists(_path):

0
client/tp_assist_macos/apple-scripts/compile.sh Normal file → Executable file
View File

View File

@ -10,7 +10,7 @@ on CommandRun(theCmd, theProfile, theTitle)
if it is not running then
tell application "iTerm"
activate
delay 0.2
delay 0.5
try
close first window
end try
@ -24,9 +24,12 @@ on CommandRun(theCmd, theProfile, theTitle)
end try
tell the current window
tell the current session
delay 0.5
set name to theTitle
set profile to theProfile
write text theCmd
delay 0.5
write text "useless"
end tell
end tell
end tell
@ -43,14 +46,18 @@ on CommandRun(theCmd, theProfile, theTitle)
end try
tell the current tab
tell the current session
delay 0.5
set name to theTitle
write text theCmd
delay 0.5
write text "useless"
end tell
end tell
end tell
end tell
on error msg
--if all iTerm windows are closed the app stays open. In this scenario iTerm has no "current window" and will give an error when trying to create the new tab.
-- if all iTerm windows are closed the app stays open. In this scenario iTerm has
-- no "current window" and will give an error when trying to create the new tab.
tell application "iTerm"
try
create window with profile theProfile
@ -59,8 +66,11 @@ on CommandRun(theCmd, theProfile, theTitle)
end try
tell the current window
tell the current session
delay 0.5
set name to theTitle
write text theCmd
delay 0.5
write text "useless"
end tell
end tell
end tell

View File

@ -69,6 +69,7 @@
<div class="col-sm-8">
<input id="rdp-app" type="text" class="form-control input-args"/>
<span class="desc"><i class="fa fa-info-circle"></i> 建议使用homebrew安装freerdp安装后freerdp默认路径在/usr/local/Cellar/freerdp/x.y.z/bin/xfreerdp</span>
<span class="desc"><i class="fa fa-info-circle"></i> 首次安装freerdp后需要重新启动计算机</span>
</div>
</div>
</div>

View File

@ -3,7 +3,6 @@
// tp_assist
//
// Created by ApexLiu on 2017/9/29.
// Copyright © 2017年 eomsoft. All rights reserved.
//
#include "AppDelegate-C-Interface.h"

View File

@ -3,7 +3,6 @@
// tp_assist
//
// Created by ApexLiu on 2017/9/27.
// Copyright © 2017年 eomsoft. All rights reserved.
//
#ifndef wrap_c_objc_h

View File

@ -167,7 +167,7 @@ int AppDelegate_start_ssh_client (void *_self, const char* cmd_line, const char*
- (IBAction)visitWebsite:(id)sender {
NSURL *url = [NSURL URLWithString:@"http://www.tp4a.com/"];
NSURL *url = [NSURL URLWithString:@"https://www.tp4a.com/"];
[[NSWorkspace sharedWorkspace] openURL:url];
}

Binary file not shown.

Binary file not shown.

View File

@ -1,8 +1,7 @@
#ifndef __TS_CONST_H__
#define __TS_CONST_H__
//#define TS_WEB_URL L"http://teleport.eomsoft.net/"
//#define TS_BBS_URL L"http://bbs.eomsoft.net/"
//#define TS_WEB_URL L"https://www.tp4a.com/"
//#define TS_TRAY_MSG L"Teleport助手正常工作中"
#define TS_HTTP_RPC_PORT 50022

View File

@ -25,7 +25,7 @@ bool TsEnv::init(const char* cfg_file, const char* res_path)
ex_astr2wstr(res_path, m_res_path);
//#ifdef EX_DEBUG
// m_site_path = L"/Users/apex/work/eomsoft/teleport-dev/client/tp_assist_macos/site";
// m_site_path = L"/Users/apex/work/tp4a/teleport/client/tp_assist_macos/site";
//#else
m_site_path = m_res_path;
ex_path_join(m_site_path, false, L"site", NULL);

View File

@ -29,12 +29,12 @@
<key>LSUIElement</key>
<true/>
<key>NSHumanReadableCopyright</key>
<string>Copyright © 2017~2018 EOMSOFT. All rights reserved.</string>
<string>Copyright © 2017~2018 TP4A. All rights reserved.</string>
<key>NSMainNibFile</key>
<string>MainMenu</string>
<key>NSPrincipalClass</key>
<string>NSApplication</string>
<key>Product Homepage</key>
<string>http://teleport.eomsoft.net/</string>
<string>https://www.tp4a.com/</string>
</dict>
</plist>

View File

@ -9,12 +9,13 @@
/* Begin PBXBuildFile section */
0ADB3B0C178EF8DB004E9BB9 /* StatusIconAlt.png in Resources */ = {isa = PBXBuildFile; fileRef = 0ADB3B08178EF8DB004E9BB9 /* StatusIconAlt.png */; };
0ADB3B0D178EF8DB004E9BB9 /* StatusIcon.png in Resources */ = {isa = PBXBuildFile; fileRef = 0ADB3B09178EF8DB004E9BB9 /* StatusIcon.png */; };
0ADB3B0E178EF8DB004E9BB9 /* StatusIcon@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 0ADB3B0A178EF8DB004E9BB9 /* StatusIcon@2x.png */; };
0ADB3B0F178EF8DB004E9BB9 /* StatusIconAlt@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 0ADB3B0B178EF8DB004E9BB9 /* StatusIconAlt@2x.png */; };
7A0C94AA1F68BD2900E04C3E /* AboutWindowController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 6FC541A51D45CF7F00A896E3 /* AboutWindowController.xib */; };
7A18188F1F7D5D7F00F3C882 /* AppDelegate-C-Interface.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7A18188E1F7D5D7F00F3C882 /* AppDelegate-C-Interface.cpp */; };
7A1818911F7FBBC200F3C882 /* tp-assist.default.json in Resources */ = {isa = PBXBuildFile; fileRef = 7A1818901F7FBBC200F3C882 /* tp-assist.default.json */; };
7A1818931F815B8A00F3C882 /* Terminal.scpt in Resources */ = {isa = PBXBuildFile; fileRef = 7A1818921F815B8A00F3C882 /* Terminal.scpt */; };
7A1F87AD215D59BD00B69F88 /* Terminal.scpt in Resources */ = {isa = PBXBuildFile; fileRef = 7A1F87AB215D59BD00B69F88 /* Terminal.scpt */; };
7A1F87AE215D59BD00B69F88 /* iTerm2.scpt in Resources */ = {isa = PBXBuildFile; fileRef = 7A1F87AC215D59BD00B69F88 /* iTerm2.scpt */; };
7A1F87B1215D5A1600B69F88 /* StatusIconAlt@2X.png in Resources */ = {isa = PBXBuildFile; fileRef = 7A1F87AF215D5A1600B69F88 /* StatusIconAlt@2X.png */; };
7A1F87B2215D5A1600B69F88 /* StatusIcon@2X.png in Resources */ = {isa = PBXBuildFile; fileRef = 7A1F87B0215D5A1600B69F88 /* StatusIcon@2X.png */; };
7A27E4A91F6A8EEC004FDE5D /* ts_env.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7A27E4A71F6A8EEC004FDE5D /* ts_env.cpp */; };
7AA2CD381F6A92620074C92B /* ts_http_rpc.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7AA2CD371F6A92620074C92B /* ts_http_rpc.cpp */; };
7AA2CD3B1F6A955A0074C92B /* ts_cfg.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7AA2CD391F6A955A0074C92B /* ts_cfg.cpp */; };
@ -30,7 +31,6 @@
7AA2CD541F6AB9F10074C92B /* json_writer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7AA2CD501F6AB9F10074C92B /* json_writer.cpp */; };
7AA2CD571F6ABA2E0074C92B /* mongoose.c in Sources */ = {isa = PBXBuildFile; fileRef = 7AA2CD561F6ABA2E0074C92B /* mongoose.c */; };
7AA2CD591F6AC0DA0074C92B /* site in Resources */ = {isa = PBXBuildFile; fileRef = 7AA2CD581F6AC0DA0074C92B /* site */; };
7AD1F1D31F7A55EA0048A496 /* iTerm2.scpt in Resources */ = {isa = PBXBuildFile; fileRef = 7AD1F1D11F7A55EA0048A496 /* iTerm2.scpt */; };
A1B7B9DD1DB53ED200809327 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = A1B7B9DF1DB53ED200809327 /* Localizable.strings */; };
A1D700071A5DCE8D003563E4 /* AboutWindowController.m in Sources */ = {isa = PBXBuildFile; fileRef = A1D700061A5DCE8D003563E4 /* AboutWindowController.m */; };
C149EBFE15D5214600B1F558 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C149EBFD15D5214600B1F558 /* Cocoa.framework */; };
@ -44,17 +44,37 @@
/* Begin PBXFileReference section */
0ADB3B08178EF8DB004E9BB9 /* StatusIconAlt.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = StatusIconAlt.png; sourceTree = "<group>"; };
0ADB3B09178EF8DB004E9BB9 /* StatusIcon.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = StatusIcon.png; sourceTree = "<group>"; };
0ADB3B0A178EF8DB004E9BB9 /* StatusIcon@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "StatusIcon@2x.png"; sourceTree = "<group>"; };
0ADB3B0B178EF8DB004E9BB9 /* StatusIconAlt@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "StatusIconAlt@2x.png"; sourceTree = "<group>"; };
7A18188E1F7D5D7F00F3C882 /* AppDelegate-C-Interface.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = "AppDelegate-C-Interface.cpp"; sourceTree = "<group>"; };
7A1818901F7FBBC200F3C882 /* tp-assist.default.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = "tp-assist.default.json"; sourceTree = "<group>"; };
7A1818921F815B8A00F3C882 /* Terminal.scpt */ = {isa = PBXFileReference; lastKnownFileType = file; path = Terminal.scpt; sourceTree = "<group>"; };
7A1818951F8242E900F3C882 /* apple-scripts */ = {isa = PBXFileReference; lastKnownFileType = folder; path = "apple-scripts"; sourceTree = "<group>"; };
7A1F8797215D565600B69F88 /* ex_util.h */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.h; fileEncoding = 4; name = ex_util.h; path = ../../../../common/libex/include/ex/ex_util.h; sourceTree = "<group>"; };
7A1F8798215D565600B69F88 /* ex_ini.h */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.h; fileEncoding = 4; name = ex_ini.h; path = ../../../../common/libex/include/ex/ex_ini.h; sourceTree = "<group>"; };
7A1F8799215D565600B69F88 /* ex_const.h */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.h; fileEncoding = 4; name = ex_const.h; path = ../../../../common/libex/include/ex/ex_const.h; sourceTree = "<group>"; };
7A1F879B215D565600B69F88 /* ex_log.h */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.h; fileEncoding = 4; name = ex_log.h; path = ../../../../common/libex/include/ex/ex_log.h; sourceTree = "<group>"; };
7A1F879C215D565600B69F88 /* ex_platform.h */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.h; fileEncoding = 4; name = ex_platform.h; path = ../../../../common/libex/include/ex/ex_platform.h; sourceTree = "<group>"; };
7A1F879D215D565600B69F88 /* ex_types.h */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.h; fileEncoding = 4; name = ex_types.h; path = ../../../../common/libex/include/ex/ex_types.h; sourceTree = "<group>"; };
7A1F879E215D565700B69F88 /* ex_str.h */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.h; fileEncoding = 4; name = ex_str.h; path = ../../../../common/libex/include/ex/ex_str.h; sourceTree = "<group>"; };
7A1F879F215D565700B69F88 /* ex_path.h */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.h; fileEncoding = 4; name = ex_path.h; path = ../../../../common/libex/include/ex/ex_path.h; sourceTree = "<group>"; };
7A1F87A0215D565700B69F88 /* ex_thread.h */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.h; fileEncoding = 4; name = ex_thread.h; path = ../../../../common/libex/include/ex/ex_thread.h; sourceTree = "<group>"; };
7A1F87A1215D56B500B69F88 /* writer.h */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.h; fileEncoding = 4; name = writer.h; path = ../../../../external/jsoncpp/include/json/writer.h; sourceTree = "<group>"; };
7A1F87A2215D570000B69F88 /* value.h */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.h; fileEncoding = 4; name = value.h; path = ../../../../external/jsoncpp/include/json/value.h; sourceTree = "<group>"; };
7A1F87A3215D570000B69F88 /* reader.h */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.h; fileEncoding = 4; name = reader.h; path = ../../../../external/jsoncpp/include/json/reader.h; sourceTree = "<group>"; };
7A1F87A4215D570000B69F88 /* json.h */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.h; fileEncoding = 4; name = json.h; path = ../../../../external/jsoncpp/include/json/json.h; sourceTree = "<group>"; };
7A1F87A5215D574400B69F88 /* config.h */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.h; fileEncoding = 4; name = config.h; path = ../../../../external/jsoncpp/include/json/config.h; sourceTree = "<group>"; };
7A1F87A6215D574500B69F88 /* features.h */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.h; fileEncoding = 4; name = features.h; path = ../../../../external/jsoncpp/include/json/features.h; sourceTree = "<group>"; };
7A1F87A7215D574500B69F88 /* forwards.h */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.h; fileEncoding = 4; name = forwards.h; path = ../../../../external/jsoncpp/include/json/forwards.h; sourceTree = "<group>"; };
7A1F87A8215D574500B69F88 /* assertions.h */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.h; fileEncoding = 4; name = assertions.h; path = ../../../../external/jsoncpp/include/json/assertions.h; sourceTree = "<group>"; };
7A1F87A9215D574500B69F88 /* autolink.h */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.h; fileEncoding = 4; name = autolink.h; path = ../../../../external/jsoncpp/include/json/autolink.h; sourceTree = "<group>"; };
7A1F87AA215D574500B69F88 /* version.h */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.h; fileEncoding = 4; name = version.h; path = ../../../../external/jsoncpp/include/json/version.h; sourceTree = "<group>"; };
7A1F87AB215D59BD00B69F88 /* Terminal.scpt */ = {isa = PBXFileReference; lastKnownFileType = file; path = Terminal.scpt; sourceTree = "<group>"; };
7A1F87AC215D59BD00B69F88 /* iTerm2.scpt */ = {isa = PBXFileReference; lastKnownFileType = file; path = iTerm2.scpt; sourceTree = "<group>"; };
7A1F87AF215D5A1600B69F88 /* StatusIconAlt@2X.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "StatusIconAlt@2X.png"; sourceTree = "<group>"; };
7A1F87B0215D5A1600B69F88 /* StatusIcon@2X.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "StatusIcon@2X.png"; sourceTree = "<group>"; };
7A27E4A61F6A899B004FDE5D /* ts_const.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ts_const.h; sourceTree = "<group>"; };
7A27E4A71F6A8EEC004FDE5D /* ts_env.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ts_env.cpp; sourceTree = "<group>"; };
7A27E4A81F6A8EEC004FDE5D /* ts_env.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ts_env.h; sourceTree = "<group>"; };
7A40FFE21F7B2A4500F11697 /* AppDelegate-C-Interface.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "AppDelegate-C-Interface.h"; sourceTree = "<group>"; };
7AA2CD361F6A92380074C92B /* ts_http_rpc.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ts_http_rpc.h; sourceTree = "<group>"; };
7AA2CD361F6A92380074C92B /* ts_http_rpc.h */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.h; fileEncoding = 2147486000; path = ts_http_rpc.h; sourceTree = "<group>"; };
7AA2CD371F6A92620074C92B /* ts_http_rpc.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ts_http_rpc.cpp; sourceTree = "<group>"; };
7AA2CD391F6A955A0074C92B /* ts_cfg.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ts_cfg.cpp; sourceTree = "<group>"; };
7AA2CD3A1F6A955A0074C92B /* ts_cfg.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ts_cfg.h; sourceTree = "<group>"; };
@ -66,13 +86,12 @@
7AA2CD421F6AB9750074C92B /* ex_util.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ex_util.cpp; path = ../../../../common/libex/src/ex_util.cpp; sourceTree = "<group>"; };
7AA2CD431F6AB9750074C92B /* ex_winsrv.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ex_winsrv.cpp; path = ../../../../common/libex/src/ex_winsrv.cpp; sourceTree = "<group>"; };
7AA2CD4C1F6AB9F10074C92B /* json_reader.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = json_reader.cpp; path = ../../../../external/jsoncpp/src/lib_json/json_reader.cpp; sourceTree = "<group>"; };
7AA2CD4D1F6AB9F10074C92B /* json_tool.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = json_tool.h; path = ../../../../external/jsoncpp/src/lib_json/json_tool.h; sourceTree = "<group>"; };
7AA2CD4D1F6AB9F10074C92B /* json_tool.h */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.h; fileEncoding = 4; name = json_tool.h; path = ../../../../external/jsoncpp/src/lib_json/json_tool.h; sourceTree = "<group>"; };
7AA2CD4E1F6AB9F10074C92B /* json_value.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = json_value.cpp; path = ../../../../external/jsoncpp/src/lib_json/json_value.cpp; sourceTree = "<group>"; };
7AA2CD4F1F6AB9F10074C92B /* json_valueiterator.inl */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = json_valueiterator.inl; path = ../../../../external/jsoncpp/src/lib_json/json_valueiterator.inl; sourceTree = "<group>"; };
7AA2CD501F6AB9F10074C92B /* json_writer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = json_writer.cpp; path = ../../../../external/jsoncpp/src/lib_json/json_writer.cpp; sourceTree = "<group>"; };
7AA2CD561F6ABA2E0074C92B /* mongoose.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = mongoose.c; path = ../../../../external/mongoose/mongoose.c; sourceTree = "<group>"; };
7AA2CD581F6AC0DA0074C92B /* site */ = {isa = PBXFileReference; lastKnownFileType = folder; path = site; sourceTree = "<group>"; };
7AD1F1D11F7A55EA0048A496 /* iTerm2.scpt */ = {isa = PBXFileReference; lastKnownFileType = file; path = iTerm2.scpt; sourceTree = "<group>"; };
A1B7B9D31DB5361700809327 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = "<group>"; };
A1B7B9DE1DB53ED200809327 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Localizable.strings; sourceTree = "<group>"; };
A1B7B9E01DB53ED700809327 /* Base */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = Base; path = Base.lproj/Localizable.strings; sourceTree = "<group>"; };
@ -113,10 +132,10 @@
0ADB3B10178EF8E2004E9BB9 /* Images */ = {
isa = PBXGroup;
children = (
7A1F87B0215D5A1600B69F88 /* StatusIcon@2X.png */,
7A1F87AF215D5A1600B69F88 /* StatusIconAlt@2X.png */,
0ADB3B09178EF8DB004E9BB9 /* StatusIcon.png */,
0ADB3B0A178EF8DB004E9BB9 /* StatusIcon@2x.png */,
0ADB3B08178EF8DB004E9BB9 /* StatusIconAlt.png */,
0ADB3B0B178EF8DB004E9BB9 /* StatusIconAlt@2x.png */,
);
name = Images;
sourceTree = "<group>";
@ -124,6 +143,15 @@
7AA2CD3C1F6AB9560074C92B /* libex */ = {
isa = PBXGroup;
children = (
7A1F8799215D565600B69F88 /* ex_const.h */,
7A1F8798215D565600B69F88 /* ex_ini.h */,
7A1F879B215D565600B69F88 /* ex_log.h */,
7A1F879F215D565700B69F88 /* ex_path.h */,
7A1F879C215D565600B69F88 /* ex_platform.h */,
7A1F879E215D565700B69F88 /* ex_str.h */,
7A1F87A0215D565700B69F88 /* ex_thread.h */,
7A1F879D215D565600B69F88 /* ex_types.h */,
7A1F8797215D565600B69F88 /* ex_util.h */,
7AA2CD3D1F6AB9750074C92B /* ex_ini.cpp */,
7AA2CD3E1F6AB9750074C92B /* ex_log.cpp */,
7AA2CD3F1F6AB9750074C92B /* ex_path.cpp */,
@ -138,6 +166,16 @@
7AA2CD4B1F6AB9880074C92B /* jsoncpp */ = {
isa = PBXGroup;
children = (
7A1F87A8215D574500B69F88 /* assertions.h */,
7A1F87A9215D574500B69F88 /* autolink.h */,
7A1F87A5215D574400B69F88 /* config.h */,
7A1F87A6215D574500B69F88 /* features.h */,
7A1F87A7215D574500B69F88 /* forwards.h */,
7A1F87AA215D574500B69F88 /* version.h */,
7A1F87A4215D570000B69F88 /* json.h */,
7A1F87A3215D570000B69F88 /* reader.h */,
7A1F87A2215D570000B69F88 /* value.h */,
7A1F87A1215D56B500B69F88 /* writer.h */,
7AA2CD4C1F6AB9F10074C92B /* json_reader.cpp */,
7AA2CD4D1F6AB9F10074C92B /* json_tool.h */,
7AA2CD4E1F6AB9F10074C92B /* json_value.cpp */,
@ -175,8 +213,8 @@
A12D9BE61BCF2C72004F52A6 /* apple-scpt */ = {
isa = PBXGroup;
children = (
7A1818921F815B8A00F3C882 /* Terminal.scpt */,
7AD1F1D11F7A55EA0048A496 /* iTerm2.scpt */,
7A1F87AC215D59BD00B69F88 /* iTerm2.scpt */,
7A1F87AB215D59BD00B69F88 /* Terminal.scpt */,
);
path = "apple-scpt";
sourceTree = "<group>";
@ -308,16 +346,16 @@
7AA2CD591F6AC0DA0074C92B /* site in Resources */,
A1B7B9DD1DB53ED200809327 /* Localizable.strings in Resources */,
0ADB3B0D178EF8DB004E9BB9 /* StatusIcon.png in Resources */,
7A1F87B1215D5A1600B69F88 /* StatusIconAlt@2X.png in Resources */,
7A1F87AE215D59BD00B69F88 /* iTerm2.scpt in Resources */,
0ADB3B0C178EF8DB004E9BB9 /* StatusIconAlt.png in Resources */,
C149EC0815D5214600B1F558 /* InfoPlist.strings in Resources */,
0ADB3B0F178EF8DB004E9BB9 /* StatusIconAlt@2x.png in Resources */,
7AD1F1D31F7A55EA0048A496 /* iTerm2.scpt in Resources */,
0ADB3B0E178EF8DB004E9BB9 /* StatusIcon@2x.png in Resources */,
7A1F87AD215D59BD00B69F88 /* Terminal.scpt in Resources */,
C149EC1415D5214600B1F558 /* MainMenu.xib in Resources */,
C159DC2815D5DE8000F5DE24 /* teleport.icns in Resources */,
7A0C94AA1F68BD2900E04C3E /* AboutWindowController.xib in Resources */,
7A1818911F7FBBC200F3C882 /* tp-assist.default.json in Resources */,
7A1818931F815B8A00F3C882 /* Terminal.scpt in Resources */,
7A1F87B2215D5A1600B69F88 /* StatusIcon@2X.png in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@ -399,6 +437,7 @@
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
@ -454,6 +493,7 @@
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;

View File

@ -35,7 +35,7 @@ wchar_t* ex_abspath_to(const wchar_t* base_abs_path, const wchar_t* relate_path)
bool ex_exec_file(ex_wstr& out_filename);
bool ex_abspath(ex_wstr& inout_path);
bool ex_dirname(ex_wstr& inout_filename);
bool ex_path_join(ex_wstr& inout_path, bool auto_abspath, ...);
bool ex_path_join(ex_wstr& inout_path, EX_BOOL auto_abspath, ...);
bool ex_abspath_to(const ex_wstr& base_abs_path, const ex_wstr& relate_path, ex_wstr& out_path);
bool ex_mkdirs(const ex_wstr& in_path);

View File

@ -409,7 +409,7 @@ bool ex_abspath(ex_wstr& inout_path)
return true;
}
bool ex_path_join(ex_wstr& inout_path, bool auto_abspath, ...)
bool ex_path_join(ex_wstr& inout_path, EX_BOOL auto_abspath, ...)
{
wchar_t* tmp;

1
server/.idea/.name Normal file
View File

@ -0,0 +1 @@
teleport

View File

@ -1,32 +1,37 @@
MESSAGE(STATUS "operation system is ${CMAKE_SYSTEM}")
cmake_minimum_required(VERSION 3.5)
#project(teleport)
project(teleport)
#set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
MESSAGE(STATUS "operation system is ${CMAKE_SYSTEM}")
MESSAGE(STATUS "current source directory is ${CMAKE_CURRENT_SOURCE_DIR}")
#set(SOURCE_FILES main.cpp)
#add_executable(teleport ${SOURCE_FILES})
#set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${teleport_SOURCE_DIR}/../out/server/x64/bin")
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${teleport_SOURCE_DIR}/../out/server/x64/bin")
set(CMAKE_CONFIGURATION_TYPES Debug Release)
IF (CMAKE_SYSTEM_NAME MATCHES "Linux")
# Determine the platform.
if("${CMAKE_SYSTEM_NAME}" STREQUAL "Darwin")
MESSAGE(STATUS "build on macOS...")
set(OS_MACOS 1)
set(OS_POSIX 1)
set(TP_EXTERNAL_RELEASE_DIR "${teleport_SOURCE_DIR}/../external/macos/release")
elseif("${CMAKE_SYSTEM_NAME}" STREQUAL "Linux")
set(OS_LINUX 1)
set(OS_POSIX 1)
MESSAGE(STATUS "build on Linux...")
add_subdirectory(tp_web/src)
ELSEIF (CMAKE_SYSTEM_NAME MATCHES "Windows")
set(TP_EXTERNAL_RELEASE_DIR "${teleport_SOURCE_DIR}/../external/linux/release")
elseif("${CMAKE_SYSTEM_NAME}" STREQUAL "Windows")
MESSAGE(FATAL_ERROR "unsupported platform: Windows")
ELSEIF (CMAKE_SYSTEM_NAME MATCHES "Darwin")
MESSAGE(STATUS "build on MacOS...")
ELSE ()
else()
MESSAGE(FATAL_ERROR "unsupported platform: ${CMAKE_SYSTEM_NAME}")
ENDIF (CMAKE_SYSTEM_NAME MATCHES "Linux")
endif()
add_subdirectory(tp_core/core)
add_subdirectory(tp_core/protocol/ssh)
add_subdirectory(tp_core/protocol/telnet)
#add_subdirectory(testssh/testssh)
IF (EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/tp_core/protocol/rdp")
if (EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/tp_core/protocol/rdp")
add_subdirectory(tp_core/protocol/rdp)
ENDIF (EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/tp_core/protocol/rdp")
endif()

View File

@ -1,10 +1,10 @@
cmake_minimum_required(VERSION 3.5)
project(tpcore)
#set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${Project_SOURCE_DIR}/../out/server/x64/bin")
MESSAGE(STATUS "=======================================================")
MESSAGE(STATUS " tp_core")
MESSAGE(STATUS "=======================================================")
MESSAGE(STATUS "operation system is ${CMAKE_SYSTEM}")
MESSAGE(STATUS "current source directory is ${CMAKE_CURRENT_SOURCE_DIR}")
ADD_DEFINITIONS(
-DMG_ENABLE_THREADS
@ -22,9 +22,6 @@ aux_source_directory(../../../common/libex/src DIR_SRCS)
aux_source_directory(../../../external/mongoose DIR_SRCS)
aux_source_directory(../../../external/jsoncpp/src/lib_json DIR_SRCS)
#list(REMOVE_ITEM DIR_SRCS "./src/ts_win_service_helper.cpp")
include_directories(
../../../common/libex/include
../../../common/teleport
@ -32,24 +29,17 @@ include_directories(
../../../external/jsoncpp/include
)
IF (CMAKE_SYSTEM_NAME MATCHES "Linux")
set(CMAKE_EXE_LINKER_FLAGS "-export-dynamic")
include_directories(
../../../external/linux/release/include
${TP_EXTERNAL_RELEASE_DIR}/include
)
link_directories(../../../external/linux/release/lib)
ELSEIF (CMAKE_SYSTEM_NAME MATCHES "Darwin")
include_directories(
../../../external/macos/release/include
)
link_directories(../../../external/macos/release/lib)
ENDIF (CMAKE_SYSTEM_NAME MATCHES "Linux")
link_directories(${TP_EXTERNAL_RELEASE_DIR}/lib)
add_executable(tp_core ${DIR_SRCS})
IF (CMAKE_SYSTEM_NAME MATCHES "Linux")
if (OS_LINUX)
set(CMAKE_EXE_LINKER_FLAGS "-export-dynamic")
target_link_libraries(tp_core ssl crypto mbedx509 mbedtls mbedcrypto dl pthread rt util)
ELSEIF (CMAKE_SYSTEM_NAME MATCHES "Darwin")
elseif (OS_MACOS)
target_link_libraries(tp_core ssl crypto mbedx509 mbedtls mbedcrypto dl pthread util)
ENDIF (CMAKE_SYSTEM_NAME MATCHES "Linux")
endif ()

View File

@ -1,14 +1,14 @@
cmake_minimum_required(VERSION 3.5)
project(tpssh)
#set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fPIC")
#set(CMAKE_CFLAGS "${CMAKE_CFLAGS} -fPIC")
MESSAGE(STATUS "=======================================================")
MESSAGE(STATUS " libtpssh")
MESSAGE(STATUS "=======================================================")
MESSAGE(STATUS "operation system is ${CMAKE_SYSTEM}")
MESSAGE(STATUS "current source directory is ${CMAKE_CURRENT_SOURCE_DIR}")
set(CMAKE_CXX_FLAGS "-fPIC")
set(CMAKE_C_FLAGS "-fPIC")
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${Project_SOURCE_DIR}/../out/server/x64/bin")
aux_source_directory(. DIR_SSH_SRCS)
aux_source_directory(../../common DIR_SSH_SRCS)
aux_source_directory(../../../../common/libex/src DIR_SSH_SRCS)
@ -24,25 +24,16 @@ include_directories(
../../../../external/jsoncpp/include
)
IF (CMAKE_SYSTEM_NAME MATCHES "Linux")
include_directories(
../../../../external/linux/release/include
${TP_EXTERNAL_RELEASE_DIR}/include
)
link_directories(../../../../external/linux/release/lib)
ELSEIF (CMAKE_SYSTEM_NAME MATCHES "Darwin")
include_directories(
../../../../external/macos/release/include
)
link_directories(../../../../external/macos/release/lib)
ENDIF (CMAKE_SYSTEM_NAME MATCHES "Linux")
link_directories(${TP_EXTERNAL_RELEASE_DIR}/lib)
add_library(tpssh SHARED ${DIR_SSH_SRCS})
IF (CMAKE_SYSTEM_NAME MATCHES "Linux")
# target_link_libraries(tpssh ssh ssh_threads ssl crypto mbedx509 mbedtls mbedcrypto dl pthread rt util)
if (OS_LINUX)
target_link_libraries(tpssh ssh ssl crypto mbedx509 mbedtls mbedcrypto dl pthread rt util)
ELSEIF (CMAKE_SYSTEM_NAME MATCHES "Darwin")
target_link_libraries(tpssh ssh ssh_threads ssl crypto mbedx509 mbedtls mbedcrypto dl pthread util)
ENDIF (CMAKE_SYSTEM_NAME MATCHES "Linux")
elseif (OS_MACOS)
target_link_libraries(tpssh ssh ssl crypto mbedx509 mbedtls mbedcrypto dl pthread util)
endif()

View File

@ -1,11 +1,13 @@
cmake_minimum_required(VERSION 3.5)
project(tptelnet)
MESSAGE(STATUS "=======================================================")
MESSAGE(STATUS " libtptelnet")
MESSAGE(STATUS "=======================================================")
MESSAGE(STATUS "operation system is ${CMAKE_SYSTEM}")
MESSAGE(STATUS "current source directory is ${CMAKE_CURRENT_SOURCE_DIR}")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fPIC")
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${Project_SOURCE_DIR}/../out/server/x64/bin")
aux_source_directory(. DIR_TELNET_SRCS)
aux_source_directory(../../common DIR_TELNET_SRCS)
aux_source_directory(../../../../common/libex/src DIR_TELNET_SRCS)
@ -21,26 +23,15 @@ include_directories(
../../../../external/jsoncpp/include
)
IF (CMAKE_SYSTEM_NAME MATCHES "Linux")
include_directories(
../../../../external/linux/release/include
${TP_EXTERNAL_RELEASE_DIR}/include
)
link_directories(../../../../external/linux/release/lib)
ELSEIF (CMAKE_SYSTEM_NAME MATCHES "Darwin")
include_directories(
../../../../external/macos/release/include
)
link_directories(../../../../external/macos/release/lib)
ENDIF (CMAKE_SYSTEM_NAME MATCHES "Linux")
link_directories(${TP_EXTERNAL_RELEASE_DIR}/lib)
add_library(tptelnet SHARED ${DIR_TELNET_SRCS})
IF (CMAKE_SYSTEM_NAME MATCHES "Linux")
#target_link_libraries(tptelnet uv mbedx509 mbedtls mbedcrypto dl pthread rt util)
if (OS_LINUX)
target_link_libraries(tptelnet uv dl pthread rt util)
ELSEIF (CMAKE_SYSTEM_NAME MATCHES "Darwin")
#target_link_libraries(tptelnet uv mbedx509 mbedtls mbedcrypto dl pthread util)
elseif (OS_MACOS)
target_link_libraries(tptelnet uv dl pthread util)
ENDIF (CMAKE_SYSTEM_NAME MATCHES "Linux")
endif()

View File

@ -1,10 +1,13 @@
cmake_minimum_required(VERSION 3.5)
project(tpweb)
MESSAGE(STATUS "=======================================================")
MESSAGE(STATUS " libtptelnet")
MESSAGE(STATUS "=======================================================")
MESSAGE(STATUS "operation system is ${CMAKE_SYSTEM}")
MESSAGE(STATUS "current source directory is ${CMAKE_CURRENT_SOURCE_DIR}")
#set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${Project_SOURCE_DIR}/../out/server/x64/bin")
set(CMAKE_EXE_LINKER_FLAGS "-export-dynamic")
aux_source_directory(. DIR_SRCS)
@ -14,12 +17,13 @@ aux_source_directory(../../../common/pyshell/src DIR_SRCS)
include_directories(
../../../common/libex/include
../../../common/pyshell/include
../../../external/linux/release/include
../../../external/linux/release/include/python
)
link_directories(../../../external/linux/release/lib)
include_directories(
${TP_EXTERNAL_RELEASE_DIR}/include
${TP_EXTERNAL_RELEASE_DIR}/include/python
)
link_directories(${TP_EXTERNAL_RELEASE_DIR}/lib)
add_executable(tp_web ${DIR_SRCS})
#target_link_libraries(tp_web python3.7m ssl crypto dl pthread rt util)
target_link_libraries(tp_web python3.7m ssl crypto dl pthread rt util)

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -17,8 +17,9 @@
# See the README file for information on usage and redistribution.
#
from PIL import Image
from PIL import FontFile
from __future__ import print_function
from . import Image, FontFile
# --------------------------------------------------------------------
@ -119,9 +120,9 @@ class BdfFontFile(FontFile.FontFile):
# fontname = ";".join(font[1:])
# print "#", fontname
# print("#", fontname)
# for i in comments:
# print "#", i
# print("#", i)
while True:
c = bdf_char(fp)

View File

@ -0,0 +1,435 @@
"""
Blizzard Mipmap Format (.blp)
Jerome Leclanche <jerome@leclan.ch>
The contents of this file are hereby released in the public domain (CC0)
Full text of the CC0 license:
https://creativecommons.org/publicdomain/zero/1.0/
BLP1 files, used mostly in Warcraft III, are not fully supported.
All types of BLP2 files used in World of Warcraft are supported.
The BLP file structure consists of a header, up to 16 mipmaps of the
texture
Texture sizes must be powers of two, though the two dimensions do
not have to be equal; 512x256 is valid, but 512x200 is not.
The first mipmap (mipmap #0) is the full size image; each subsequent
mipmap halves both dimensions. The final mipmap should be 1x1.
BLP files come in many different flavours:
* JPEG-compressed (type == 0) - only supported for BLP1.
* RAW images (type == 1, encoding == 1). Each mipmap is stored as an
array of 8-bit values, one per pixel, left to right, top to bottom.
Each value is an index to the palette.
* DXT-compressed (type == 1, encoding == 2):
- DXT1 compression is used if alpha_encoding == 0.
- An additional alpha bit is used if alpha_depth == 1.
- DXT3 compression is used if alpha_encoding == 1.
- DXT5 compression is used if alpha_encoding == 7.
"""
import struct
from io import BytesIO
from . import Image, ImageFile
BLP_FORMAT_JPEG = 0
BLP_ENCODING_UNCOMPRESSED = 1
BLP_ENCODING_DXT = 2
BLP_ENCODING_UNCOMPRESSED_RAW_BGRA = 3
BLP_ALPHA_ENCODING_DXT1 = 0
BLP_ALPHA_ENCODING_DXT3 = 1
BLP_ALPHA_ENCODING_DXT5 = 7
def unpack_565(i):
return (
((i >> 11) & 0x1f) << 3,
((i >> 5) & 0x3f) << 2,
(i & 0x1f) << 3
)
def decode_dxt1(data, alpha=False):
"""
input: one "row" of data (i.e. will produce 4*width pixels)
"""
blocks = len(data) // 8 # number of blocks in row
ret = (bytearray(), bytearray(), bytearray(), bytearray())
for block in range(blocks):
# Decode next 8-byte block.
idx = block * 8
color0, color1, bits = struct.unpack_from("<HHI", data, idx)
r0, g0, b0 = unpack_565(color0)
r1, g1, b1 = unpack_565(color1)
# Decode this block into 4x4 pixels
# Accumulate the results onto our 4 row accumulators
for j in range(4):
for i in range(4):
# get next control op and generate a pixel
control = bits & 3
bits = bits >> 2
a = 0xFF
if control == 0:
r, g, b = r0, g0, b0
elif control == 1:
r, g, b = r1, g1, b1
elif control == 2:
if color0 > color1:
r = (2 * r0 + r1) // 3
g = (2 * g0 + g1) // 3
b = (2 * b0 + b1) // 3
else:
r = (r0 + r1) // 2
g = (g0 + g1) // 2
b = (b0 + b1) // 2
elif control == 3:
if color0 > color1:
r = (2 * r1 + r0) // 3
g = (2 * g1 + g0) // 3
b = (2 * b1 + b0) // 3
else:
r, g, b, a = 0, 0, 0, 0
if alpha:
ret[j].extend([r, g, b, a])
else:
ret[j].extend([r, g, b])
return ret
def decode_dxt3(data):
"""
input: one "row" of data (i.e. will produce 4*width pixels)
"""
blocks = len(data) // 16 # number of blocks in row
ret = (bytearray(), bytearray(), bytearray(), bytearray())
for block in range(blocks):
idx = block * 16
block = data[idx:idx + 16]
# Decode next 16-byte block.
bits = struct.unpack_from("<8B", block)
color0, color1 = struct.unpack_from("<HH", block, 8)
code, = struct.unpack_from("<I", block, 12)
r0, g0, b0 = unpack_565(color0)
r1, g1, b1 = unpack_565(color1)
for j in range(4):
high = False # Do we want the higher bits?
for i in range(4):
alphacode_index = (4 * j + i) // 2
a = bits[alphacode_index]
if high:
high = False
a >>= 4
else:
high = True
a &= 0xf
a *= 17 # We get a value between 0 and 15
color_code = (code >> 2 * (4 * j + i)) & 0x03
if color_code == 0:
r, g, b = r0, g0, b0
elif color_code == 1:
r, g, b = r1, g1, b1
elif color_code == 2:
r = (2 * r0 + r1) // 3
g = (2 * g0 + g1) // 3
b = (2 * b0 + b1) // 3
elif color_code == 3:
r = (2 * r1 + r0) // 3
g = (2 * g1 + g0) // 3
b = (2 * b1 + b0) // 3
ret[j].extend([r, g, b, a])
return ret
def decode_dxt5(data):
"""
input: one "row" of data (i.e. will produce 4 * width pixels)
"""
blocks = len(data) // 16 # number of blocks in row
ret = (bytearray(), bytearray(), bytearray(), bytearray())
for block in range(blocks):
idx = block * 16
block = data[idx:idx + 16]
# Decode next 16-byte block.
a0, a1 = struct.unpack_from("<BB", block)
bits = struct.unpack_from("<6B", block, 2)
alphacode1 = (
bits[2] | (bits[3] << 8) | (bits[4] << 16) | (bits[5] << 24)
)
alphacode2 = bits[0] | (bits[1] << 8)
color0, color1 = struct.unpack_from("<HH", block, 8)
code, = struct.unpack_from("<I", block, 12)
r0, g0, b0 = unpack_565(color0)
r1, g1, b1 = unpack_565(color1)
for j in range(4):
for i in range(4):
# get next control op and generate a pixel
alphacode_index = 3 * (4 * j + i)
if alphacode_index <= 12:
alphacode = (alphacode2 >> alphacode_index) & 0x07
elif alphacode_index == 15:
alphacode = (alphacode2 >> 15) | ((alphacode1 << 1) & 0x06)
else: # alphacode_index >= 18 and alphacode_index <= 45
alphacode = (alphacode1 >> (alphacode_index - 16)) & 0x07
if alphacode == 0:
a = a0
elif alphacode == 1:
a = a1
elif a0 > a1:
a = ((8 - alphacode) * a0 + (alphacode - 1) * a1) // 7
elif alphacode == 6:
a = 0
elif alphacode == 7:
a = 255
else:
a = ((6 - alphacode) * a0 + (alphacode - 1) * a1) // 5
color_code = (code >> 2 * (4 * j + i)) & 0x03
if color_code == 0:
r, g, b = r0, g0, b0
elif color_code == 1:
r, g, b = r1, g1, b1
elif color_code == 2:
r = (2 * r0 + r1) // 3
g = (2 * g0 + g1) // 3
b = (2 * b0 + b1) // 3
elif color_code == 3:
r = (2 * r1 + r0) // 3
g = (2 * g1 + g0) // 3
b = (2 * b1 + b0) // 3
ret[j].extend([r, g, b, a])
return ret
class BLPFormatError(NotImplementedError):
pass
class BlpImageFile(ImageFile.ImageFile):
"""
Blizzard Mipmap Format
"""
format = "BLP"
format_description = "Blizzard Mipmap Format"
def _open(self):
self.magic = self.fp.read(4)
self._read_blp_header()
if self.magic == b"BLP1":
decoder = "BLP1"
self.mode = "RGB"
elif self.magic == b"BLP2":
decoder = "BLP2"
self.mode = "RGBA" if self._blp_alpha_depth else "RGB"
else:
raise BLPFormatError("Bad BLP magic %r" % (self.magic))
self.tile = [
(decoder, (0, 0) + self.size, 0, (self.mode, 0, 1))
]
def _read_blp_header(self):
self._blp_compression, = struct.unpack("<i", self.fp.read(4))
self._blp_encoding, = struct.unpack("<b", self.fp.read(1))
self._blp_alpha_depth, = struct.unpack("<b", self.fp.read(1))
self._blp_alpha_encoding, = struct.unpack("<b", self.fp.read(1))
self._blp_mips, = struct.unpack("<b", self.fp.read(1))
self.size = struct.unpack("<II", self.fp.read(8))
if self.magic == b"BLP1":
# Only present for BLP1
self._blp_encoding, = struct.unpack("<i", self.fp.read(4))
self._blp_subtype, = struct.unpack("<i", self.fp.read(4))
self._blp_offsets = struct.unpack("<16I", self.fp.read(16 * 4))
self._blp_lengths = struct.unpack("<16I", self.fp.read(16 * 4))
class _BLPBaseDecoder(ImageFile.PyDecoder):
_pulls_fd = True
def decode(self, buffer):
try:
self.fd.seek(0)
self.magic = self.fd.read(4)
self._read_blp_header()
self._load()
except struct.error:
raise IOError("Truncated Blp file")
return 0, 0
def _read_palette(self):
ret = []
for i in range(256):
try:
b, g, r, a = struct.unpack("<4B", self.fd.read(4))
except struct.error:
break
ret.append((b, g, r, a))
return ret
def _read_blp_header(self):
self._blp_compression, = struct.unpack("<i", self.fd.read(4))
self._blp_encoding, = struct.unpack("<b", self.fd.read(1))
self._blp_alpha_depth, = struct.unpack("<b", self.fd.read(1))
self._blp_alpha_encoding, = struct.unpack("<b", self.fd.read(1))
self._blp_mips, = struct.unpack("<b", self.fd.read(1))
self.size = struct.unpack("<II", self.fd.read(8))
if self.magic == b"BLP1":
# Only present for BLP1
self._blp_encoding, = struct.unpack("<i", self.fd.read(4))
self._blp_subtype, = struct.unpack("<i", self.fd.read(4))
self._blp_offsets = struct.unpack("<16I", self.fd.read(16 * 4))
self._blp_lengths = struct.unpack("<16I", self.fd.read(16 * 4))
class BLP1Decoder(_BLPBaseDecoder):
def _load(self):
if self._blp_compression == BLP_FORMAT_JPEG:
self._decode_jpeg_stream()
elif self._blp_compression == 1:
if self._blp_encoding in (4, 5):
data = bytearray()
palette = self._read_palette()
_data = BytesIO(self.fd.read(self._blp_lengths[0]))
while True:
try:
offset, = struct.unpack("<B", _data.read(1))
except struct.error:
break
b, g, r, a = palette[offset]
data.extend([r, g, b])
self.set_as_raw(bytes(data))
else:
raise BLPFormatError(
"Unsupported BLP encoding %r" % (self._blp_encoding)
)
else:
raise BLPFormatError(
"Unsupported BLP compression %r" % (self._blp_encoding)
)
def _decode_jpeg_stream(self):
from PIL.JpegImagePlugin import JpegImageFile
jpeg_header_size, = struct.unpack("<I", self.fd.read(4))
jpeg_header = self.fd.read(jpeg_header_size)
self.fd.read(self._blp_offsets[0] - self.fd.tell()) # What IS this?
data = self.fd.read(self._blp_lengths[0])
data = jpeg_header + data
data = BytesIO(data)
image = JpegImageFile(data)
self.tile = image.tile # :/
self.fd = image.fp
self.mode = image.mode
class BLP2Decoder(_BLPBaseDecoder):
def _load(self):
palette = self._read_palette()
data = bytearray()
self.fd.seek(self._blp_offsets[0])
if self._blp_compression == 1:
# Uncompressed or DirectX compression
if self._blp_encoding == BLP_ENCODING_UNCOMPRESSED:
_data = BytesIO(self.fd.read(self._blp_lengths[0]))
while True:
try:
offset, = struct.unpack("<B", _data.read(1))
except struct.error:
break
b, g, r, a = palette[offset]
data.extend((r, g, b))
elif self._blp_encoding == BLP_ENCODING_DXT:
if self._blp_alpha_encoding == BLP_ALPHA_ENCODING_DXT1:
linesize = (self.size[0] + 3) // 4 * 8
for yb in range((self.size[1] + 3) // 4):
for d in decode_dxt1(
self.fd.read(linesize),
alpha=bool(self._blp_alpha_depth)
):
data += d
elif self._blp_alpha_encoding == BLP_ALPHA_ENCODING_DXT3:
linesize = (self.size[0] + 3) // 4 * 16
for yb in range((self.size[1] + 3) // 4):
for d in decode_dxt3(self.fd.read(linesize)):
data += d
elif self._blp_alpha_encoding == BLP_ALPHA_ENCODING_DXT5:
linesize = (self.size[0] + 3) // 4 * 16
for yb in range((self.size[1] + 3) // 4):
for d in decode_dxt5(self.fd.read(linesize)):
data += d
else:
raise BLPFormatError("Unsupported alpha encoding %r" % (
self._blp_alpha_encoding
))
else:
raise BLPFormatError(
"Unknown BLP encoding %r" % (self._blp_encoding)
)
else:
raise BLPFormatError(
"Unknown BLP compression %r" % (self._blp_compression)
)
self.set_as_raw(bytes(data))
Image.register_open(
BlpImageFile.format, BlpImageFile, lambda p: p[:4] in (b"BLP1", b"BLP2")
)
Image.register_extension(BlpImageFile.format, ".blp")
Image.register_decoder("BLP1", BLP1Decoder)
Image.register_decoder("BLP2", BLP2Decoder)

View File

@ -24,18 +24,13 @@
#
from PIL import Image, ImageFile, ImagePalette, _binary
from . import Image, ImageFile, ImagePalette
from ._binary import i8, i16le as i16, i32le as i32, \
o8, o16le as o16, o32le as o32
import math
__version__ = "0.7"
i8 = _binary.i8
i16 = _binary.i16le
i32 = _binary.i32le
o8 = _binary.o8
o16 = _binary.o16le
o32 = _binary.o32le
#
# --------------------------------------------------------------------
# Read BMP file
@ -73,7 +68,7 @@ class BmpImageFile(ImageFile.ImageFile):
read, seek = self.fp.read, self.fp.seek
if header:
seek(header)
file_info = dict()
file_info = {}
file_info['header_size'] = i32(read(4)) # read bmp header size @offset 14 (this is part of the header size)
file_info['direction'] = -1
# --------------------- If requested, read header at a specific position
@ -109,7 +104,13 @@ class BmpImageFile(ImageFile.ImageFile):
for idx, mask in enumerate(['r_mask', 'g_mask', 'b_mask', 'a_mask']):
file_info[mask] = i32(header_data[36+idx*4:40+idx*4])
else:
for mask in ['r_mask', 'g_mask', 'b_mask', 'a_mask']:
# 40 byte headers only have the three components in the bitfields masks,
# ref: https://msdn.microsoft.com/en-us/library/windows/desktop/dd183376(v=vs.85).aspx
# See also https://github.com/python-pillow/Pillow/issues/1293
# There is a 4th component in the RGBQuad, in the alpha location, but it
# is listed as a reserved component, and it is not generally an alpha channel
file_info['a_mask'] = 0x0
for mask in ['r_mask', 'g_mask', 'b_mask']:
file_info[mask] = i32(read(4))
file_info['rgb_mask'] = (file_info['r_mask'], file_info['g_mask'], file_info['b_mask'])
file_info['rgba_mask'] = (file_info['r_mask'], file_info['g_mask'], file_info['b_mask'], file_info['a_mask'])
@ -130,12 +131,13 @@ class BmpImageFile(ImageFile.ImageFile):
# ----------------- Process BMP with Bitfields compression (not palette)
if file_info['compression'] == self.BITFIELDS:
SUPPORTED = {
32: [(0xff0000, 0xff00, 0xff, 0x0), (0xff0000, 0xff00, 0xff, 0xff000000), (0x0, 0x0, 0x0, 0x0)],
32: [(0xff0000, 0xff00, 0xff, 0x0), (0xff0000, 0xff00, 0xff, 0xff000000), (0x0, 0x0, 0x0, 0x0), (0xff000000, 0xff0000, 0xff00, 0x0)],
24: [(0xff0000, 0xff00, 0xff)],
16: [(0xf800, 0x7e0, 0x1f), (0x7c00, 0x3e0, 0x1f)]
}
MASK_MODES = {
(32, (0xff0000, 0xff00, 0xff, 0x0)): "BGRX",
(32, (0xff000000, 0xff0000, 0xff00, 0x0)): "XBGR",
(32, (0xff0000, 0xff00, 0xff, 0xff000000)): "BGRA",
(32, (0x0, 0x0, 0x0, 0x0)): "BGRA",
(24, (0xff0000, 0xff00, 0xff)): "BGR",
@ -214,6 +216,7 @@ class DibImageFile(BmpImageFile):
# --------------------------------------------------------------------
# Write BMP file
SAVE = {
"1": ("1", 1, 2),
"L": ("L", 8, 256),
@ -223,15 +226,12 @@ SAVE = {
}
def _save(im, fp, filename, check=0):
def _save(im, fp, filename):
try:
rawmode, bits, colors = SAVE[im.mode]
except KeyError:
raise IOError("cannot write mode %s as BMP" % im.mode)
if check:
return check
info = im.encoderinfo
dpi = info.get("dpi", (96, 96))
@ -280,6 +280,7 @@ def _save(im, fp, filename, check=0):
# --------------------------------------------------------------------
# Registry
Image.register_open(BmpImageFile.format, BmpImageFile, _accept)
Image.register_save(BmpImageFile.format, _save)

View File

@ -9,17 +9,17 @@
# See the README file for information on usage and redistribution.
#
from PIL import Image, ImageFile
from . import Image, ImageFile
_handler = None
##
# Install application-specific BUFR image handler.
#
# @param handler Handler object.
def register_handler(handler):
"""
Install application-specific BUFR image handler.
:param handler: Handler object.
"""
global _handler
_handler = handler
@ -40,7 +40,7 @@ class BufrStubImageFile(ImageFile.StubImageFile):
offset = self.fp.tell()
if not _accept(self.fp.read(8)):
if not _accept(self.fp.read(4)):
raise SyntaxError("Not a BUFR file")
self.fp.seek(offset)

View File

@ -21,14 +21,14 @@
class ContainerIO(object):
##
# Create file object.
#
# @param file Existing file.
# @param offset Start of region, in bytes.
# @param length Size of region, in bytes.
def __init__(self, file, offset, length):
"""
Create file object.
:param file: Existing file.
:param offset: Start of region, in bytes.
:param length: Size of region, in bytes.
"""
self.fh = file
self.pos = 0
self.offset = offset
@ -41,15 +41,15 @@ class ContainerIO(object):
def isatty(self):
return 0
##
# Move file pointer.
#
# @param offset Offset in bytes.
# @param mode Starting position. Use 0 for beginning of region, 1
# for current offset, and 2 for end of region. You cannot move
# the pointer outside the defined region.
def seek(self, offset, mode=0):
"""
Move file pointer.
:param offset: Offset in bytes.
:param mode: Starting position. Use 0 for beginning of region, 1
for current offset, and 2 for end of region. You cannot move
the pointer outside the defined region.
"""
if mode == 1:
self.pos = self.pos + offset
elif mode == 2:
@ -60,23 +60,22 @@ class ContainerIO(object):
self.pos = max(0, min(self.pos, self.length))
self.fh.seek(self.offset + self.pos)
##
# Get current file pointer.
#
# @return Offset from start of region, in bytes.
def tell(self):
"""
Get current file pointer.
:returns: Offset from start of region, in bytes.
"""
return self.pos
##
# Read data.
#
# @def read(bytes=0)
# @param bytes Number of bytes to read. If omitted or zero,
# read until end of region.
# @return An 8-bit string.
def read(self, n=0):
"""
Read data.
:param n: Number of bytes to read. If omitted or zero,
read until end of region.
:returns: An 8-bit string.
"""
if n:
n = min(n, self.length - self.pos)
else:
@ -86,12 +85,12 @@ class ContainerIO(object):
self.pos = self.pos + n
return self.fh.read(n)
##
# Read a line of text.
#
# @return An 8-bit string.
def readline(self):
"""
Read a line of text.
:returns: An 8-bit string.
"""
s = ""
while True:
c = self.read(1)
@ -102,12 +101,12 @@ class ContainerIO(object):
break
return s
##
# Read multiple lines of text.
#
# @return A list of 8-bit strings.
def readlines(self):
"""
Read multiple lines of text.
:returns: A list of 8-bit strings.
"""
l = []
while True:
s = self.readline()

View File

@ -16,18 +16,16 @@
# See the README file for information on usage and redistribution.
#
from __future__ import print_function
from PIL import Image, BmpImagePlugin, _binary
from . import Image, BmpImagePlugin
from ._binary import i8, i16le as i16, i32le as i32
__version__ = "0.1"
#
# --------------------------------------------------------------------
i8 = _binary.i8
i16 = _binary.i16le
i32 = _binary.i32le
def _accept(prefix):
return prefix[:4] == b"\0\0\2\0"
@ -58,14 +56,14 @@ class CurImageFile(BmpImagePlugin.BmpImageFile):
m = s
elif i8(s[0]) > i8(m[0]) and i8(s[1]) > i8(m[1]):
m = s
# print "width", i8(s[0])
# print "height", i8(s[1])
# print "colors", i8(s[2])
# print "reserved", i8(s[3])
# print "hotspot x", i16(s[4:])
# print "hotspot y", i16(s[6:])
# print "bytes", i32(s[8:])
# print "offset", i32(s[12:])
# print("width", i8(s[0]))
# print("height", i8(s[1]))
# print("colors", i8(s[2]))
# print("reserved", i8(s[3]))
# print("hotspot x", i16(s[4:]))
# print("hotspot y", i16(s[6:]))
# print("bytes", i32(s[8:]))
# print("offset", i32(s[12:]))
if not m:
raise TypeError("No cursors were found")

View File

@ -21,15 +21,14 @@
# See the README file for information on usage and redistribution.
#
from PIL import Image, _binary
from PIL.PcxImagePlugin import PcxImageFile
from . import Image
from ._binary import i32le as i32
from .PcxImagePlugin import PcxImageFile
__version__ = "0.2"
MAGIC = 0x3ADE68B1 # QUIZ: what's this value, then?
i32 = _binary.i32le
def _accept(prefix):
return len(prefix) >= 4 and i32(prefix) == MAGIC
@ -42,6 +41,7 @@ class DcxImageFile(PcxImageFile):
format = "DCX"
format_description = "Intel DCX"
_close_exclusive_fp_after_loading = False
def _open(self):
@ -59,6 +59,7 @@ class DcxImageFile(PcxImageFile):
self._offset.append(offset)
self.__fp = self.fp
self.frame = None
self.seek(0)
@property
@ -70,8 +71,8 @@ class DcxImageFile(PcxImageFile):
return len(self._offset) > 1
def seek(self, frame):
if frame >= len(self._offset):
raise EOFError("attempt to seek outside DCX directory")
if not self._seek_check(frame):
return
self.frame = frame
self.fp = self.__fp
self.fp.seek(self._offset[frame])

View File

@ -0,0 +1,172 @@
"""
A Pillow loader for .dds files (S3TC-compressed aka DXTC)
Jerome Leclanche <jerome@leclan.ch>
Documentation:
https://web.archive.org/web/20170802060935/http://oss.sgi.com/projects/ogl-sample/registry/EXT/texture_compression_s3tc.txt
The contents of this file are hereby released in the public domain (CC0)
Full text of the CC0 license:
https://creativecommons.org/publicdomain/zero/1.0/
"""
import struct
from io import BytesIO
from . import Image, ImageFile
# Magic ("DDS ")
DDS_MAGIC = 0x20534444
# DDS flags
DDSD_CAPS = 0x1
DDSD_HEIGHT = 0x2
DDSD_WIDTH = 0x4
DDSD_PITCH = 0x8
DDSD_PIXELFORMAT = 0x1000
DDSD_MIPMAPCOUNT = 0x20000
DDSD_LINEARSIZE = 0x80000
DDSD_DEPTH = 0x800000
# DDS caps
DDSCAPS_COMPLEX = 0x8
DDSCAPS_TEXTURE = 0x1000
DDSCAPS_MIPMAP = 0x400000
DDSCAPS2_CUBEMAP = 0x200
DDSCAPS2_CUBEMAP_POSITIVEX = 0x400
DDSCAPS2_CUBEMAP_NEGATIVEX = 0x800
DDSCAPS2_CUBEMAP_POSITIVEY = 0x1000
DDSCAPS2_CUBEMAP_NEGATIVEY = 0x2000
DDSCAPS2_CUBEMAP_POSITIVEZ = 0x4000
DDSCAPS2_CUBEMAP_NEGATIVEZ = 0x8000
DDSCAPS2_VOLUME = 0x200000
# Pixel Format
DDPF_ALPHAPIXELS = 0x1
DDPF_ALPHA = 0x2
DDPF_FOURCC = 0x4
DDPF_PALETTEINDEXED8 = 0x20
DDPF_RGB = 0x40
DDPF_LUMINANCE = 0x20000
# dds.h
DDS_FOURCC = DDPF_FOURCC
DDS_RGB = DDPF_RGB
DDS_RGBA = DDPF_RGB | DDPF_ALPHAPIXELS
DDS_LUMINANCE = DDPF_LUMINANCE
DDS_LUMINANCEA = DDPF_LUMINANCE | DDPF_ALPHAPIXELS
DDS_ALPHA = DDPF_ALPHA
DDS_PAL8 = DDPF_PALETTEINDEXED8
DDS_HEADER_FLAGS_TEXTURE = (DDSD_CAPS | DDSD_HEIGHT | DDSD_WIDTH |
DDSD_PIXELFORMAT)
DDS_HEADER_FLAGS_MIPMAP = DDSD_MIPMAPCOUNT
DDS_HEADER_FLAGS_VOLUME = DDSD_DEPTH
DDS_HEADER_FLAGS_PITCH = DDSD_PITCH
DDS_HEADER_FLAGS_LINEARSIZE = DDSD_LINEARSIZE
DDS_HEIGHT = DDSD_HEIGHT
DDS_WIDTH = DDSD_WIDTH
DDS_SURFACE_FLAGS_TEXTURE = DDSCAPS_TEXTURE
DDS_SURFACE_FLAGS_MIPMAP = DDSCAPS_COMPLEX | DDSCAPS_MIPMAP
DDS_SURFACE_FLAGS_CUBEMAP = DDSCAPS_COMPLEX
DDS_CUBEMAP_POSITIVEX = DDSCAPS2_CUBEMAP | DDSCAPS2_CUBEMAP_POSITIVEX
DDS_CUBEMAP_NEGATIVEX = DDSCAPS2_CUBEMAP | DDSCAPS2_CUBEMAP_NEGATIVEX
DDS_CUBEMAP_POSITIVEY = DDSCAPS2_CUBEMAP | DDSCAPS2_CUBEMAP_POSITIVEY
DDS_CUBEMAP_NEGATIVEY = DDSCAPS2_CUBEMAP | DDSCAPS2_CUBEMAP_NEGATIVEY
DDS_CUBEMAP_POSITIVEZ = DDSCAPS2_CUBEMAP | DDSCAPS2_CUBEMAP_POSITIVEZ
DDS_CUBEMAP_NEGATIVEZ = DDSCAPS2_CUBEMAP | DDSCAPS2_CUBEMAP_NEGATIVEZ
# DXT1
DXT1_FOURCC = 0x31545844
# DXT3
DXT3_FOURCC = 0x33545844
# DXT5
DXT5_FOURCC = 0x35545844
# dxgiformat.h
DXGI_FORMAT_BC7_TYPELESS = 97
DXGI_FORMAT_BC7_UNORM = 98
DXGI_FORMAT_BC7_UNORM_SRGB = 99
class DdsImageFile(ImageFile.ImageFile):
format = "DDS"
format_description = "DirectDraw Surface"
def _open(self):
magic, header_size = struct.unpack("<II", self.fp.read(8))
if header_size != 124:
raise IOError("Unsupported header size %r" % (header_size))
header_bytes = self.fp.read(header_size - 4)
if len(header_bytes) != 120:
raise IOError("Incomplete header: %s bytes" % len(header_bytes))
header = BytesIO(header_bytes)
flags, height, width = struct.unpack("<3I", header.read(12))
self.size = (width, height)
self.mode = "RGBA"
pitch, depth, mipmaps = struct.unpack("<3I", header.read(12))
reserved = struct.unpack("<11I", header.read(44))
# pixel format
pfsize, pfflags = struct.unpack("<2I", header.read(8))
fourcc = header.read(4)
bitcount, rmask, gmask, bmask, amask = struct.unpack("<5I",
header.read(20))
data_start = header_size + 4
n = 0
if fourcc == b"DXT1":
self.pixel_format = "DXT1"
n = 1
elif fourcc == b"DXT3":
self.pixel_format = "DXT3"
n = 2
elif fourcc == b"DXT5":
self.pixel_format = "DXT5"
n = 3
elif fourcc == b"DX10":
data_start += 20
# ignoring flags which pertain to volume textures and cubemaps
dxt10 = BytesIO(self.fp.read(20))
dxgi_format, dimension = struct.unpack("<II", dxt10.read(8))
if dxgi_format in (DXGI_FORMAT_BC7_TYPELESS, DXGI_FORMAT_BC7_UNORM):
self.pixel_format = "BC7"
n = 7
elif dxgi_format == DXGI_FORMAT_BC7_UNORM_SRGB:
self.pixel_format = "BC7"
self.im_info["gamma"] = 1/2.2
n = 7
else:
raise NotImplementedError("Unimplemented DXGI format %d" %
(dxgi_format))
else:
raise NotImplementedError("Unimplemented pixel format %r" %
(fourcc))
self.tile = [
("bcn", (0, 0) + self.size, data_start, (n))
]
def load_seek(self, pos):
pass
def _validate(prefix):
return prefix[:4] == b"DDS "
Image.register_open(DdsImageFile.format, DdsImageFile, _validate)
Image.register_extension(DdsImageFile.format, ".dds")

View File

@ -22,17 +22,17 @@
import re
import io
import os
import sys
from PIL import Image, ImageFile, _binary
from . import Image, ImageFile
from ._binary import i32le as i32
from ._util import py3
__version__ = "0.5"
#
# --------------------------------------------------------------------
i32 = _binary.i32le
o32 = _binary.o32le
split = re.compile(r"^%%([^:]*):[ \t]*(.*)[ \t]*$")
field = re.compile(r"^%[%!\w]([^:]*)[ \t]*$")
@ -59,8 +59,8 @@ def has_ghostscript():
if not sys.platform.startswith('win'):
import subprocess
try:
gs = subprocess.Popen(['gs', '--version'], stdout=subprocess.PIPE)
gs.stdout.read()
with open(os.devnull, 'wb') as devnull:
subprocess.check_call(['gs', '--version'], stdout=devnull)
return True
except OSError:
# no ghostscript
@ -85,7 +85,6 @@ def Ghostscript(tile, size, fp, scale=1):
float((72.0 * size[1]) / (bbox[3]-bbox[1])))
# print("Ghostscript", scale, size, orig_size, bbox, orig_bbox, res)
import os
import subprocess
import tempfile
@ -123,6 +122,7 @@ def Ghostscript(tile, size, fp, scale=1):
"-q", # quiet mode
"-g%dx%d" % size, # set output geometry (pixels)
"-r%fx%f" % res, # set input DPI (dots per inch)
"-dBATCH", # exit after processing
"-dNOPAUSE", # don't pause between pages,
"-dSAFER", # safe mode
"-sDEVICE=ppmraw", # ppm driver
@ -130,6 +130,7 @@ def Ghostscript(tile, size, fp, scale=1):
"-c", "%d %d translate" % (-bbox[0], -bbox[1]),
# adjust for image origin
"-f", infile, # input file
"-c", "showpage", # showpage (see: https://bugs.ghostscript.com/show_bug.cgi?id=698272)
]
if gs_windows_binary is not None:
@ -139,13 +140,10 @@ def Ghostscript(tile, size, fp, scale=1):
# push data through ghostscript
try:
gs = subprocess.Popen(command, stdin=subprocess.PIPE,
stdout=subprocess.PIPE)
gs.stdin.close()
status = gs.wait()
if status:
raise IOError("gs failed (status %d)" % status)
im = Image.core.open_ppm(outfile)
with open(os.devnull, 'w+b') as devnull:
subprocess.check_call(command, stdin=devnull, stdout=devnull)
im = Image.open(outfile)
im.load()
finally:
try:
os.unlink(outfile)
@ -154,7 +152,7 @@ def Ghostscript(tile, size, fp, scale=1):
except OSError:
pass
return im
return im.im.copy()
class PSFile(object):
@ -201,7 +199,7 @@ class EpsImageFile(ImageFile.ImageFile):
format = "EPS"
format_description = "Encapsulated Postscript"
mode_map = {1: "L", 2: "LAB", 3: "RGB"}
mode_map = {1: "L", 2: "LAB", 3: "RGB", 4: "CMYK"}
def _open(self):
(length, offset) = self._find_offset(self.fp)
@ -209,12 +207,12 @@ class EpsImageFile(ImageFile.ImageFile):
# Rewrap the open file pointer in something that will
# convert line endings and decode to latin-1.
try:
if bytes is str:
# Python2, no encoding conversion necessary
fp = open(self.fp.name, "Ur")
else:
if py3:
# Python3, can use bare open command.
fp = open(self.fp.name, "Ur", encoding='latin-1')
else:
# Python2, no encoding conversion necessary
fp = open(self.fp.name, "Ur")
except:
# Expect this for bytesio/stringio
fp = PSFile(self.fp)
@ -230,9 +228,11 @@ class EpsImageFile(ImageFile.ImageFile):
#
# Load EPS header
s = fp.readline().strip('\r\n')
s_raw = fp.readline()
s = s_raw.strip('\r\n')
while s:
while s_raw:
if s:
if len(s) > 255:
raise SyntaxError("not an EPS file")
@ -274,9 +274,10 @@ class EpsImageFile(ImageFile.ImageFile):
else:
raise IOError("bad EPS header")
s = fp.readline().strip('\r\n')
s_raw = fp.readline()
s = s_raw.strip('\r\n')
if s[:1] != "%":
if s and s[:1] != "%":
break
#
@ -322,7 +323,7 @@ class EpsImageFile(ImageFile.ImageFile):
# EPS can contain binary data
# or start directly with latin coding
# more info see:
# http://partners.adobe.com/public/developer/en/ps/5002.EPSF_Spec.pdf
# https://web.archive.org/web/20160528181353/http://partners.adobe.com/public/developer/en/ps/5002.EPSF_Spec.pdf
offset = i32(s[4:8])
length = i32(s[8:12])
else:
@ -379,7 +380,7 @@ def _save(im, fp, filename, eps=1):
base_fp = fp
if fp != sys.stdout:
fp = NoCloseStream(fp)
if sys.version_info[0] > 2:
if sys.version_info.major > 2:
fp = io.TextIOWrapper(fp, encoding='latin-1')
if eps:
@ -418,11 +419,11 @@ def _save(im, fp, filename, eps=1):
#
# --------------------------------------------------------------------
Image.register_open(EpsImageFile.format, EpsImageFile, _accept)
Image.register_save(EpsImageFile.format, _save)
Image.register_extension(EpsImageFile.format, ".ps")
Image.register_extension(EpsImageFile.format, ".eps")
Image.register_extensions(EpsImageFile.format, [".ps", ".eps"])
Image.register_mime(EpsImageFile.format, "application/postscript")

View File

@ -20,6 +20,7 @@
TAGS = {
# possibly incomplete
0x000b: "ProcessingSoftware",
0x00fe: "NewSubfileType",
0x00ff: "SubfileType",
0x0100: "ImageWidth",
@ -27,12 +28,11 @@ TAGS = {
0x0102: "BitsPerSample",
0x0103: "Compression",
0x0106: "PhotometricInterpretation",
0x0107: "Threshholding",
0x0107: "Thresholding",
0x0108: "CellWidth",
0x0109: "CellLenght",
0x0109: "CellLength",
0x010a: "FillOrder",
0x010d: "DocumentName",
0x011d: "PageName",
0x010e: "ImageDescription",
0x010f: "Make",
0x0110: "Model",
@ -40,41 +40,80 @@ TAGS = {
0x0112: "Orientation",
0x0115: "SamplesPerPixel",
0x0116: "RowsPerStrip",
0x0117: "StripByteConunts",
0x0117: "StripByteCounts",
0x0118: "MinSampleValue",
0x0119: "MaxSampleValue",
0x011a: "XResolution",
0x011b: "YResolution",
0x011c: "PlanarConfiguration",
0x011d: "PageName",
0x0120: "FreeOffsets",
0x0121: "FreeByteCounts",
0x0122: "GrayResponseUnit",
0x0123: "GrayResponseCurve",
0x0124: "T4Options",
0x0125: "T6Options",
0x0128: "ResolutionUnit",
0x0129: "PageNumber",
0x012d: "TransferFunction",
0x0131: "Software",
0x0132: "DateTime",
0x013b: "Artist",
0x013c: "HostComputer",
0x013d: "Predictor",
0x013e: "WhitePoint",
0x013f: "PrimaryChromaticities",
0x0140: "ColorMap",
0x0141: "HalftoneHints",
0x0142: "TileWidth",
0x0143: "TileLength",
0x0144: "TileOffsets",
0x0145: "TileByteCounts",
0x014a: "SubIFDs",
0x014c: "InkSet",
0x014d: "InkNames",
0x014e: "NumberOfInks",
0x0150: "DotRange",
0x0151: "TargetPrinter",
0x0152: "ExtraSamples",
0x0153: "SampleFormat",
0x0154: "SMinSampleValue",
0x0155: "SMaxSampleValue",
0x0156: "TransferRange",
0x0157: "ClipPath",
0x0158: "XClipPathUnits",
0x0159: "YClipPathUnits",
0x015a: "Indexed",
0x015b: "JPEGTables",
0x015f: "OPIProxy",
0x0200: "JPEGProc",
0x0201: "JpegIFOffset",
0x0202: "JpegIFByteCount",
0x0203: "JpegRestartInterval",
0x0205: "JpegLosslessPredictors",
0x0206: "JpegPointTransforms",
0x0207: "JpegQTables",
0x0208: "JpegDCTables",
0x0209: "JpegACTables",
0x0211: "YCbCrCoefficients",
0x0212: "YCbCrSubSampling",
0x0213: "YCbCrPositioning",
0x0214: "ReferenceBlackWhite",
0x02bc: "XMLPacket",
0x1000: "RelatedImageFileFormat",
0x1001: "RelatedImageWidth",
0x1002: "RelatedImageLength",
0x4746: "Rating",
0x4749: "RatingPercent",
0x800d: "ImageID",
0x828d: "CFARepeatPatternDim",
0x828e: "CFAPattern",
0x828f: "BatteryLevel",
0x8298: "Copyright",
0x829a: "ExposureTime",
0x829d: "FNumber",
0x83bb: "IPTCNAA",
0x8649: "ImageResources",
0x8769: "ExifOffset",
0x8773: "InterColorProfile",
0x8822: "ExposureProgram",
@ -114,6 +153,11 @@ TAGS = {
0x9290: "SubsecTime",
0x9291: "SubsecTimeOriginal",
0x9292: "SubsecTimeDigitized",
0x9c9b: "XPTitle",
0x9c9c: "XPComment",
0x9c9d: "XPAuthor",
0x9c9e: "XPKeywords",
0x9c9f: "XPSubject",
0xa000: "FlashPixVersion",
0xa001: "ColorSpace",
0xa002: "ExifImageWidth",
@ -151,7 +195,85 @@ TAGS = {
0xa434: "LensModel",
0xa435: "LensSerialNumber",
0xa500: "Gamma",
0xc4a5: "PrintImageMatching",
0xc612: "DNGVersion",
0xc613: "DNGBackwardVersion",
0xc614: "UniqueCameraModel",
0xc615: "LocalizedCameraModel",
0xc616: "CFAPlaneColor",
0xc617: "CFALayout",
0xc618: "LinearizationTable",
0xc619: "BlackLevelRepeatDim",
0xc61a: "BlackLevel",
0xc61b: "BlackLevelDeltaH",
0xc61c: "BlackLevelDeltaV",
0xc61d: "WhiteLevel",
0xc61e: "DefaultScale",
0xc61f: "DefaultCropOrigin",
0xc620: "DefaultCropSize",
0xc621: "ColorMatrix1",
0xc622: "ColorMatrix2",
0xc623: "CameraCalibration1",
0xc624: "CameraCalibration2",
0xc625: "ReductionMatrix1",
0xc626: "ReductionMatrix2",
0xc627: "AnalogBalance",
0xc628: "AsShotNeutral",
0xc629: "AsShotWhiteXY",
0xc62a: "BaselineExposure",
0xc62b: "BaselineNoise",
0xc62c: "BaselineSharpness",
0xc62d: "BayerGreenSplit",
0xc62e: "LinearResponseLimit",
0xc62f: "CameraSerialNumber",
0xc630: "LensInfo",
0xc631: "ChromaBlurRadius",
0xc632: "AntiAliasStrength",
0xc633: "ShadowScale",
0xc634: "DNGPrivateData",
0xc635: "MakerNoteSafety",
0xc65a: "CalibrationIlluminant1",
0xc65b: "CalibrationIlluminant2",
0xc65c: "BestQualityScale",
0xc65d: "RawDataUniqueID",
0xc68b: "OriginalRawFileName",
0xc68c: "OriginalRawFileData",
0xc68d: "ActiveArea",
0xc68e: "MaskedAreas",
0xc68f: "AsShotICCProfile",
0xc690: "AsShotPreProfileMatrix",
0xc691: "CurrentICCProfile",
0xc692: "CurrentPreProfileMatrix",
0xc6bf: "ColorimetricReference",
0xc6f3: "CameraCalibrationSignature",
0xc6f4: "ProfileCalibrationSignature",
0xc6f6: "AsShotProfileName",
0xc6f7: "NoiseReductionApplied",
0xc6f8: "ProfileName",
0xc6f9: "ProfileHueSatMapDims",
0xc6fa: "ProfileHueSatMapData1",
0xc6fb: "ProfileHueSatMapData2",
0xc6fc: "ProfileToneCurve",
0xc6fd: "ProfileEmbedPolicy",
0xc6fe: "ProfileCopyright",
0xc714: "ForwardMatrix1",
0xc715: "ForwardMatrix2",
0xc716: "PreviewApplicationName",
0xc717: "PreviewApplicationVersion",
0xc718: "PreviewSettingsName",
0xc719: "PreviewSettingsDigest",
0xc71a: "PreviewColorSpace",
0xc71b: "PreviewDateTime",
0xc71c: "RawImageDigest",
0xc71d: "OriginalRawFileDigest",
0xc71e: "SubTileBlockSize",
0xc71f: "RowInterleaveFactor",
0xc725: "ProfileLookTableDims",
0xc726: "ProfileLookTableData",
0xc740: "OpcodeList1",
0xc741: "OpcodeList2",
0xc74e: "OpcodeList3",
0xc761: "NoiseProfile"
}
##

View File

@ -9,17 +9,17 @@
# See the README file for information on usage and redistribution.
#
from PIL import Image, ImageFile
from . import Image, ImageFile
_handler = None
##
# Install application-specific FITS image handler.
#
# @param handler Handler object.
def register_handler(handler):
"""
Install application-specific FITS image handler.
:param handler: Handler object.
"""
global _handler
_handler = handler
@ -72,5 +72,4 @@ def _save(im, fp, filename):
Image.register_open(FITSStubImageFile.format, FITSStubImageFile, _accept)
Image.register_save(FITSStubImageFile.format, _save)
Image.register_extension(FITSStubImageFile.format, ".fit")
Image.register_extension(FITSStubImageFile.format, ".fits")
Image.register_extensions(FITSStubImageFile.format, [".fit", ".fits"])

View File

@ -16,15 +16,11 @@
#
from PIL import Image, ImageFile, ImagePalette, _binary
from . import Image, ImageFile, ImagePalette
from ._binary import i8, i16le as i16, i32le as i32, o8
__version__ = "0.2"
i8 = _binary.i8
i16 = _binary.i16le
i32 = _binary.i32le
o8 = _binary.o8
#
# decoder
@ -41,6 +37,7 @@ class FliImageFile(ImageFile.ImageFile):
format = "FLI"
format_description = "Autodesk FLI/FLC Animation"
_close_exclusive_fp_after_loading = False
def _open(self):
@ -52,6 +49,9 @@ class FliImageFile(ImageFile.ImageFile):
s[20:22] == b"\x00\x00"): # reserved
raise SyntaxError("not an FLI/FLC file")
# frames
self.__framecount = i16(s[6:8])
# image characteristics
self.mode = "P"
self.size = i16(s[8:10]), i16(s[10:12])
@ -59,7 +59,7 @@ class FliImageFile(ImageFile.ImageFile):
# animation speed
duration = i32(s[16:20])
if magic == 0xAF11:
duration = (duration * 1000) / 70
duration = (duration * 1000) // 70
self.info["duration"] = duration
# look for palette
@ -89,8 +89,6 @@ class FliImageFile(ImageFile.ImageFile):
self.__frame = -1
self.__fp = self.fp
self.__rewind = self.fp.tell()
self._n_frames = None
self._is_animated = None
self.seek(0)
def _palette(self, palette, shift):
@ -113,43 +111,20 @@ class FliImageFile(ImageFile.ImageFile):
@property
def n_frames(self):
if self._n_frames is None:
current = self.tell()
try:
while True:
self.seek(self.tell() + 1)
except EOFError:
self._n_frames = self.tell() + 1
self.seek(current)
return self._n_frames
return self.__framecount
@property
def is_animated(self):
if self._is_animated is None:
current = self.tell()
try:
self.seek(1)
self._is_animated = True
except EOFError:
self._is_animated = False
self.seek(current)
return self._is_animated
return self.__framecount > 1
def seek(self, frame):
if frame == self.__frame:
if not self._seek_check(frame):
return
if frame < self.__frame:
self._seek(0)
last_frame = self.__frame
for f in range(self.__frame + 1, frame + 1):
try:
self._seek(f)
except EOFError:
self.seek(last_frame)
raise EOFError("no more images in FLI file")
def _seek(self, frame):
if frame == 0:
@ -179,10 +154,10 @@ class FliImageFile(ImageFile.ImageFile):
def tell(self):
return self.__frame
#
# registry
Image.register_open(FliImageFile.format, FliImageFile, _accept)
Image.register_extension(FliImageFile.format, ".fli")
Image.register_extension(FliImageFile.format, ".flc")
Image.register_extensions(FliImageFile.format, [".fli", ".flc"])

View File

@ -14,8 +14,10 @@
# See the README file for information on usage and redistribution.
#
from __future__ import print_function
import os
from PIL import Image, _binary
from . import Image, _binary
WIDTH = 800
@ -88,7 +90,7 @@ class FontFile(object):
x = xx
s = src[0] + x0, src[1] + y0, src[2] + x0, src[3] + y0
self.bitmap.paste(im.crop(src), s)
# print chr(i), dst, s
# print(chr(i), dst, s)
self.metrics[i] = d, dst, s
def save(self, filename):
@ -100,7 +102,7 @@ class FontFile(object):
self.bitmap.save(os.path.splitext(filename)[0] + ".pbm", "PNG")
# font metrics
fp = open(os.path.splitext(filename)[0] + ".pil", "wb")
with open(os.path.splitext(filename)[0] + ".pil", "wb") as fp:
fp.write(b"PILfont\n")
fp.write((";;;;;;%d;\n" % self.ysize).encode('ascii')) # HACK!!!
fp.write(b"DATA\n")
@ -110,6 +112,3 @@ class FontFile(object):
puti16(fp, [0] * 10)
else:
puti16(fp, m[0] + m[1] + m[2])
fp.close()
# End of file

View File

@ -15,13 +15,15 @@
# See the README file for information on usage and redistribution.
#
from __future__ import print_function
from PIL import Image, ImageFile
from PIL.OleFileIO import i8, i32, MAGIC, OleFileIO
from . import Image, ImageFile
from ._binary import i32le as i32, i8
import olefile
__version__ = "0.1"
# we map from colour field tuples to (mode, rawmode) descriptors
MODES = {
# opacity
@ -42,7 +44,7 @@ MODES = {
# --------------------------------------------------------------------
def _accept(prefix):
return prefix[:8] == MAGIC
return prefix[:8] == olefile.MAGIC
##
@ -59,7 +61,7 @@ class FpxImageFile(ImageFile.ImageFile):
# to be a FlashPix file
try:
self.ole = OleFileIO(self.fp)
self.ole = olefile.OleFileIO(self.fp)
except IOError:
raise SyntaxError("not an FPX file; invalid OLE file")
@ -112,7 +114,7 @@ class FpxImageFile(ImageFile.ImageFile):
if id in prop:
self.jpeg[i] = prop[id]
# print len(self.jpeg), "tables loaded"
# print(len(self.jpeg), "tables loaded")
self._open_subimage(1, self.maxid)
@ -141,7 +143,7 @@ class FpxImageFile(ImageFile.ImageFile):
offset = i32(s, 28)
length = i32(s, 32)
# print size, self.mode, self.rawmode
# print(size, self.mode, self.rawmode)
if size != self.size:
raise IOError("subimage mismatch")
@ -216,11 +218,12 @@ class FpxImageFile(ImageFile.ImageFile):
self.fp = self.ole.openstream(self.stream[:2] +
["Subimage 0000 Data"])
ImageFile.ImageFile.load(self)
return ImageFile.ImageFile.load(self)
#
# --------------------------------------------------------------------
Image.register_open(FpxImageFile.format, FpxImageFile, _accept)
Image.register_extension(FpxImageFile.format, ".fpx")

View File

@ -0,0 +1,94 @@
"""
A Pillow loader for .ftc and .ftu files (FTEX)
Jerome Leclanche <jerome@leclan.ch>
The contents of this file are hereby released in the public domain (CC0)
Full text of the CC0 license:
https://creativecommons.org/publicdomain/zero/1.0/
Independence War 2: Edge Of Chaos - Texture File Format - 16 October 2001
The textures used for 3D objects in Independence War 2: Edge Of Chaos are in a
packed custom format called FTEX. This file format uses file extensions FTC and FTU.
* FTC files are compressed textures (using standard texture compression).
* FTU files are not compressed.
Texture File Format
The FTC and FTU texture files both use the same format. This
has the following structure:
{header}
{format_directory}
{data}
Where:
{header} = { u32:magic, u32:version, u32:width, u32:height, u32:mipmap_count, u32:format_count }
* The "magic" number is "FTEX".
* "width" and "height" are the dimensions of the texture.
* "mipmap_count" is the number of mipmaps in the texture.
* "format_count" is the number of texture formats (different versions of the same texture) in this file.
{format_directory} = format_count * { u32:format, u32:where }
The format value is 0 for DXT1 compressed textures and 1 for 24-bit RGB uncompressed textures.
The texture data for a format starts at the position "where" in the file.
Each set of texture data in the file has the following structure:
{data} = format_count * { u32:mipmap_size, mipmap_size * { u8 } }
* "mipmap_size" is the number of bytes in that mip level. For compressed textures this is the
size of the texture data compressed with DXT1. For 24 bit uncompressed textures, this is 3 * width * height.
Following this are the image bytes for that mipmap level.
Note: All data is stored in little-Endian (Intel) byte order.
"""
import struct
from io import BytesIO
from . import Image, ImageFile
MAGIC = b"FTEX"
FORMAT_DXT1 = 0
FORMAT_UNCOMPRESSED = 1
class FtexImageFile(ImageFile.ImageFile):
format = "FTEX"
format_description = "Texture File Format (IW2:EOC)"
def _open(self):
magic = struct.unpack("<I", self.fp.read(4))
version = struct.unpack("<i", self.fp.read(4))
self.size = struct.unpack("<2i", self.fp.read(8))
mipmap_count, format_count = struct.unpack("<2i", self.fp.read(8))
self.mode = "RGB"
# Only support single-format files. I don't know of any multi-format file.
assert format_count == 1
format, where = struct.unpack("<2i", self.fp.read(8))
self.fp.seek(where)
mipmap_size, = struct.unpack("<i", self.fp.read(4))
data = self.fp.read(mipmap_size)
if format == FORMAT_DXT1:
self.mode = "RGBA"
self.tile = [("bcn", (0, 0) + self.size, 0, (1))]
elif format == FORMAT_UNCOMPRESSED:
self.tile = [("raw", (0, 0) + self.size, 0, ('RGB', 0, 1))]
else:
raise ValueError("Invalid texture compression format: %r" % (format))
self.fp.close()
self.fp = BytesIO(data)
def load_seek(self, pos):
pass
def _validate(prefix):
return prefix[:4] == MAGIC
Image.register_open(FtexImageFile.format, FtexImageFile, _validate)
Image.register_extensions(FtexImageFile.format, [".ftc", ".ftu"])

View File

@ -1,25 +1,35 @@
#
# The Python Imaging Library
# $Id$
#
# load a GIMP brush file
#
# History:
# 96-03-14 fl Created
# 16-01-08 es Version 2
#
# Copyright (c) Secret Labs AB 1997.
# Copyright (c) Fredrik Lundh 1996.
# Copyright (c) Eric Soroos 2016.
#
# See the README file for information on usage and redistribution.
#
#
# See https://github.com/GNOME/gimp/blob/master/devel-docs/gbr.txt for
# format documentation.
#
# This code Interprets version 1 and 2 .gbr files.
# Version 1 files are obsolete, and should not be used for new
# brushes.
# Version 2 files are saved by GIMP v2.8 (at least)
# Version 3 files have a format specifier of 18 for 16bit floats in
# the color depth field. This is currently unsupported by Pillow.
from PIL import Image, ImageFile, _binary
i32 = _binary.i32be
from . import Image, ImageFile
from ._binary import i32be as i32
def _accept(prefix):
return len(prefix) >= 8 and i32(prefix) >= 20 and i32(prefix[4:8]) == 1
return len(prefix) >= 8 and i32(prefix[:4]) >= 20 and i32(prefix[4:8]) in (1, 2)
##
@ -31,41 +41,54 @@ class GbrImageFile(ImageFile.ImageFile):
format_description = "GIMP brush file"
def _open(self):
header_size = i32(self.fp.read(4))
version = i32(self.fp.read(4))
if header_size < 20 or version != 1:
if header_size < 20:
raise SyntaxError("not a GIMP brush")
if version not in (1, 2):
raise SyntaxError("Unsupported GIMP brush version: %s" % version)
width = i32(self.fp.read(4))
height = i32(self.fp.read(4))
color_depth = i32(self.fp.read(4))
if width <= 0 or height <= 0 or color_depth != 1:
if width <= 0 or height <= 0:
raise SyntaxError("not a GIMP brush")
if color_depth not in (1, 4):
raise SyntaxError("Unsupported GIMP brush color depth: %s" % color_depth)
comment = self.fp.read(header_size - 20)[:-1]
if version == 1:
comment_length = header_size-20
else:
comment_length = header_size-28
magic_number = self.fp.read(4)
if magic_number != b'GIMP':
raise SyntaxError("not a GIMP brush, bad magic number")
self.info['spacing'] = i32(self.fp.read(4))
comment = self.fp.read(comment_length)[:-1]
if color_depth == 1:
self.mode = "L"
else:
self.mode = 'RGBA'
self.size = width, height
self.info["comment"] = comment
# Since the brush is so small, we read the data immediately
self.data = self.fp.read(width * height)
# Image might not be small
Image._decompression_bomb_check(self.size)
# Data is an uncompressed block of w * h * bytes/pixel
self._data_size = width * height * color_depth
def load(self):
if not self.data:
return
# create an image out of the brush data block
self.im = Image.core.new(self.mode, self.size)
self.im.frombytes(self.data)
self.data = b""
self.frombytes(self.fp.read(self._data_size))
#
# registry
Image.register_open(GbrImageFile.format, GbrImageFile, _accept)
Image.register_open(GbrImageFile.format, GbrImageFile, _accept)
Image.register_extension(GbrImageFile.format, ".gbr")

View File

@ -23,19 +23,11 @@
# purposes only.
from PIL import ImageFile, ImagePalette, _binary
from PIL._util import isPath
from . import ImageFile, ImagePalette
from ._binary import i8, i16be as i16, i32be as i32
__version__ = "0.1"
try:
import builtins
except ImportError:
import __builtin__
builtins = __builtin__
i16 = _binary.i16be
##
# Image plugin for the GD uncompressed format. Note that this format
@ -51,42 +43,41 @@ class GdImageFile(ImageFile.ImageFile):
def _open(self):
# Header
s = self.fp.read(775)
s = self.fp.read(1037)
if not i16(s[:2]) in [65534, 65535]:
raise SyntaxError("Not a valid GD 2.x .gd file")
self.mode = "L" # FIXME: "P"
self.size = i16(s[0:2]), i16(s[2:4])
self.size = i16(s[2:4]), i16(s[4:6])
trueColor = i8(s[6])
trueColorOffset = 2 if trueColor else 0
# transparency index
tindex = i16(s[5:7])
tindex = i32(s[7+trueColorOffset:7+trueColorOffset+4])
if tindex < 256:
self.info["transparent"] = tindex
self.info["transparency"] = tindex
self.palette = ImagePalette.raw("RGB", s[7:])
self.palette = ImagePalette.raw("XBGR", s[7+trueColorOffset+4:7+trueColorOffset+4+256*4])
self.tile = [("raw", (0, 0)+self.size, 775, ("L", 0, -1))]
self.tile = [("raw", (0, 0)+self.size, 7+trueColorOffset+4+256*4, ("L", 0, 1))]
##
# Load texture from a GD image file.
#
# @param filename GD file name, or an opened file handle.
# @param mode Optional mode. In this version, if the mode argument
# is given, it must be "r".
# @return An image instance.
# @exception IOError If the image could not be read.
def open(fp, mode="r"):
"""
Load texture from a GD image file.
:param filename: GD file name, or an opened file handle.
:param mode: Optional mode. In this version, if the mode argument
is given, it must be "r".
:returns: An image instance.
:raises IOError: If the image could not be read.
"""
if mode != "r":
raise ValueError("bad mode")
if isPath(fp):
filename = fp
fp = builtins.open(fp, "rb")
else:
filename = ""
try:
return GdImageFile(fp, filename)
return GdImageFile(fp)
except SyntaxError:
raise IOError("cannot identify this image file")

View File

@ -24,21 +24,14 @@
# See the README file for information on usage and redistribution.
#
from PIL import Image, ImageFile, ImagePalette, \
ImageChops, ImageSequence, _binary
from . import Image, ImageFile, ImagePalette, ImageChops, ImageSequence
from ._binary import i8, i16le as i16, o8, o16le as o16
import itertools
__version__ = "0.9"
# --------------------------------------------------------------------
# Helpers
i8 = _binary.i8
i16 = _binary.i16le
o8 = _binary.o8
o16 = _binary.o16le
# --------------------------------------------------------------------
# Identify/read GIF files
@ -54,6 +47,8 @@ class GifImageFile(ImageFile.ImageFile):
format = "GIF"
format_description = "Compuserve GIF"
_close_exclusive_fp_after_loading = False
global_palette = None
def data(self):
@ -107,6 +102,9 @@ class GifImageFile(ImageFile.ImageFile):
@property
def is_animated(self):
if self._is_animated is None:
if self._n_frames is not None:
self._is_animated = self._n_frames != 1
else:
current = self.tell()
try:
@ -119,7 +117,7 @@ class GifImageFile(ImageFile.ImageFile):
return self._is_animated
def seek(self, frame):
if frame == self.__frame:
if not self._seek_check(frame):
return
if frame < self.__frame:
self._seek(0)
@ -198,6 +196,11 @@ class GifImageFile(ImageFile.ImageFile):
# correct, but it seems to prevent the last
# frame from looking odd for some animations
self.disposal_method = dispose_bits
elif i8(s) == 254:
#
# comment extension
#
self.info["comment"] = block
elif i8(s) == 255:
#
# application extension
@ -257,7 +260,7 @@ class GifImageFile(ImageFile.ImageFile):
# only dispose the extent in this frame
if self.dispose:
self.dispose = self.dispose.crop(self.dispose_extent)
self.dispose = self._crop(self.dispose, self.dispose_extent)
except (AttributeError, KeyError):
pass
@ -280,7 +283,7 @@ class GifImageFile(ImageFile.ImageFile):
if self._prev_im and self.disposal_method == 1:
# we do this by pasting the updated area onto the previous
# frame which we then use as the current image content
updated = self.im.crop(self.dispose_extent)
updated = self._crop(self.im, self.dispose_extent)
self._prev_im.paste(updated, self.dispose_extent,
updated.convert('RGBA'))
self.im = self._prev_im
@ -289,52 +292,173 @@ class GifImageFile(ImageFile.ImageFile):
# --------------------------------------------------------------------
# Write GIF files
try:
import _imaging_gif
except ImportError:
_imaging_gif = None
RAWMODE = {
"1": "L",
"L": "L",
"P": "P",
"P": "P"
}
def _convert_mode(im, initial_call=False):
# convert on the fly (EXPERIMENTAL -- I'm not sure PIL
# should automatically convert images on save...)
def _normalize_mode(im, initial_call=False):
"""
Takes an image (or frame), returns an image in a mode that is appropriate
for saving in a Gif.
It may return the original image, or it may return an image converted to
palette or 'L' mode.
UNDONE: What is the point of mucking with the initial call palette, for
an image that shouldn't have a palette, or it would be a mode 'P' and
get returned in the RAWMODE clause.
:param im: Image object
:param initial_call: Default false, set to true for a single frame.
:returns: Image object
"""
if im.mode in RAWMODE:
im.load()
return im
if Image.getmodebase(im.mode) == "RGB":
if initial_call:
palette_size = 256
if im.palette:
palette_size = len(im.palette.getdata()[1]) // 3
return im.convert("P", palette=1, colors=palette_size)
return im.convert("P", palette=Image.ADAPTIVE, colors=palette_size)
else:
return im.convert("P")
return im.convert("L")
def _normalize_palette(im, palette, info):
"""
Normalizes the palette for image.
- Sets the palette to the incoming palette, if provided.
- Ensures that there's a palette for L mode images
- Optimizes the palette if necessary/desired.
:param im: Image object
:param palette: bytes object containing the source palette, or ....
:param info: encoderinfo
:returns: Image object
"""
source_palette = None
if palette:
# a bytes palette
if isinstance(palette, (bytes, bytearray, list)):
source_palette = bytearray(palette[:768])
if isinstance(palette, ImagePalette.ImagePalette):
source_palette = bytearray(itertools.chain.from_iterable(
zip(palette.palette[:256],
palette.palette[256:512],
palette.palette[512:768])))
if im.mode == "P":
if not source_palette:
source_palette = im.im.getpalette("RGB")[:768]
else: # L-mode
if not source_palette:
source_palette = bytearray(i//3 for i in range(768))
im.palette = ImagePalette.ImagePalette("RGB",
palette=source_palette)
used_palette_colors = _get_optimize(im, info)
if used_palette_colors is not None:
return im.remap_palette(used_palette_colors, source_palette)
im.palette.palette = source_palette
return im
def _write_single_frame(im, fp, palette):
im_out = _normalize_mode(im, True)
im_out = _normalize_palette(im_out, palette, im.encoderinfo)
for s in _get_global_header(im_out, im.encoderinfo):
fp.write(s)
# local image header
flags = 0
if get_interlace(im):
flags = flags | 64
_write_local_header(fp, im, (0, 0), flags)
im_out.encoderconfig = (8, get_interlace(im))
ImageFile._save(im_out, fp, [("gif", (0, 0)+im.size, 0,
RAWMODE[im_out.mode])])
fp.write(b"\0") # end of image data
def _write_multiple_frames(im, fp, palette):
duration = im.encoderinfo.get("duration", None)
disposal = im.encoderinfo.get('disposal', None)
im_frames = []
frame_count = 0
for imSequence in itertools.chain([im], im.encoderinfo.get("append_images", [])):
for im_frame in ImageSequence.Iterator(imSequence):
# a copy is required here since seek can still mutate the image
im_frame = _normalize_mode(im_frame.copy())
im_frame = _normalize_palette(im_frame, palette, im.encoderinfo)
encoderinfo = im.encoderinfo.copy()
if isinstance(duration, (list, tuple)):
encoderinfo['duration'] = duration[frame_count]
if isinstance(disposal, (list, tuple)):
encoderinfo["disposal"] = disposal[frame_count]
frame_count += 1
if im_frames:
# delta frame
previous = im_frames[-1]
if _get_palette_bytes(im_frame) == _get_palette_bytes(previous['im']):
delta = ImageChops.subtract_modulo(im_frame,
previous['im'])
else:
delta = ImageChops.subtract_modulo(im_frame.convert('RGB'),
previous['im'].convert('RGB'))
bbox = delta.getbbox()
if not bbox:
# This frame is identical to the previous frame
if duration:
previous['encoderinfo']['duration'] += encoderinfo['duration']
continue
else:
bbox = None
im_frames.append({
'im': im_frame,
'bbox': bbox,
'encoderinfo': encoderinfo
})
if len(im_frames) > 1:
for frame_data in im_frames:
im_frame = frame_data['im']
if not frame_data['bbox']:
# global header
for s in _get_global_header(im_frame,
frame_data['encoderinfo']):
fp.write(s)
offset = (0, 0)
else:
# compress difference
frame_data['encoderinfo']['include_color_table'] = True
im_frame = im_frame.crop(frame_data['bbox'])
offset = frame_data['bbox'][:2]
_write_frame_data(fp, im_frame, offset, frame_data['encoderinfo'])
return True
def _save_all(im, fp, filename):
_save(im, fp, filename, save_all=True)
def _save(im, fp, filename, save_all=False):
im.encoderinfo.update(im.info)
if _imaging_gif:
# call external driver
try:
_imaging_gif.save(im, fp, filename)
return
except IOError:
pass # write uncompressed file
if im.mode in RAWMODE:
im_out = im.copy()
else:
im_out = _convert_mode(im, True)
for k, v in im.info.items():
im.encoderinfo.setdefault(k, v)
# header
try:
palette = im.encoderinfo["palette"]
@ -342,58 +466,8 @@ def _save(im, fp, filename, save_all=False):
palette = None
im.encoderinfo["optimize"] = im.encoderinfo.get("optimize", True)
if save_all:
previous = None
first_frame = None
for im_frame in ImageSequence.Iterator(im):
im_frame = _convert_mode(im_frame)
# To specify duration, add the time in milliseconds to getdata(),
# e.g. getdata(im_frame, duration=1000)
if not previous:
# global header
first_frame = getheader(im_frame, palette, im.encoderinfo)[0]
first_frame += getdata(im_frame, (0, 0), **im.encoderinfo)
else:
if first_frame:
for s in first_frame:
fp.write(s)
first_frame = None
# delta frame
delta = ImageChops.subtract_modulo(im_frame, previous.copy())
bbox = delta.getbbox()
if bbox:
# compress difference
for s in getdata(im_frame.crop(bbox),
bbox[:2], **im.encoderinfo):
fp.write(s)
else:
# FIXME: what should we do in this case?
pass
previous = im_frame
if first_frame:
save_all = False
if not save_all:
header = getheader(im_out, palette, im.encoderinfo)[0]
for s in header:
fp.write(s)
flags = 0
if get_interlace(im):
flags = flags | 64
# local image header
_get_local_header(fp, im, (0, 0), flags)
im_out.encoderconfig = (8, get_interlace(im))
ImageFile._save(im_out, fp, [("gif", (0, 0)+im.size, 0,
RAWMODE[im_out.mode])])
fp.write(b"\0") # end of image data
if not save_all or not _write_multiple_frames(im, fp, palette):
_write_single_frame(im, fp, palette)
fp.write(b";") # end of file
@ -402,10 +476,7 @@ def _save(im, fp, filename, save_all=False):
def get_interlace(im):
try:
interlace = im.encoderinfo["interlace"]
except KeyError:
interlace = 1
interlace = im.encoderinfo.get("interlace", 1)
# workaround for @PIL153
if min(im.size) < 16:
@ -414,7 +485,7 @@ def get_interlace(im):
return interlace
def _get_local_header(fp, im, offset, flags):
def _write_local_header(fp, im, offset, flags):
transparent_color_exists = False
try:
transparency = im.encoderinfo["transparency"]
@ -425,36 +496,41 @@ def _get_local_header(fp, im, offset, flags):
# optimize the block away if transparent color is not used
transparent_color_exists = True
if _get_optimize(im, im.encoderinfo):
used_palette_colors = _get_used_palette_colors(im)
used_palette_colors = _get_optimize(im, im.encoderinfo)
if used_palette_colors is not None:
# adjust the transparency index after optimize
if len(used_palette_colors) < 256:
for i in range(len(used_palette_colors)):
if used_palette_colors[i] == transparency:
transparency = i
transparent_color_exists = True
break
else:
try:
transparency = used_palette_colors.index(transparency)
except ValueError:
transparent_color_exists = False
if "duration" in im.encoderinfo:
duration = int(im.encoderinfo["duration"] / 10)
else:
duration = 0
if transparent_color_exists or duration != 0:
transparency_flag = 1 if transparent_color_exists else 0
disposal = int(im.encoderinfo.get('disposal', 0))
if transparent_color_exists or duration != 0 or disposal:
packed_flag = 1 if transparent_color_exists else 0
packed_flag |= disposal << 2
if not transparent_color_exists:
transparency = 0
fp.write(b"!" +
o8(249) + # extension intro
o8(4) + # length
o8(transparency_flag) + # transparency info present
o8(packed_flag) + # packed fields
o16(duration) + # duration
o8(transparency) + # transparency index
o8(0))
if "comment" in im.encoderinfo and 1 <= len(im.encoderinfo["comment"]) <= 255:
fp.write(b"!" +
o8(254) + # extension intro
o8(len(im.encoderinfo["comment"])) +
im.encoderinfo["comment"] +
o8(0))
if "loop" in im.encoderinfo:
number_of_loops = im.encoderinfo["loop"]
fp.write(b"!" +
@ -465,17 +541,30 @@ def _get_local_header(fp, im, offset, flags):
o8(1) +
o16(number_of_loops) + # number of loops
o8(0))
include_color_table = im.encoderinfo.get('include_color_table')
if include_color_table:
palette = im.encoderinfo.get("palette", None)
palette_bytes = _get_palette_bytes(im)
color_table_size = _get_color_table_size(palette_bytes)
if color_table_size:
flags = flags | 128 # local color table flag
flags = flags | color_table_size
fp.write(b"," +
o16(offset[0]) + # offset
o16(offset[1]) +
o16(im.size[0]) + # size
o16(im.size[1]) +
o8(flags) + # flags
o8(8)) # bits
o8(flags)) # flags
if include_color_table and color_table_size:
fp.write(_get_header_palette(palette_bytes))
fp.write(o8(8)) # bits
def _save_netpbm(im, fp, filename):
# Unused by default.
# To use, uncomment the register_save call at the end of the file.
#
# If you need real GIF compression and/or RGB quantization, you
# can use the external NETPBM/PBMPLUS utilities. See comments
@ -483,25 +572,21 @@ def _save_netpbm(im, fp, filename):
import os
from subprocess import Popen, check_call, PIPE, CalledProcessError
import tempfile
file = im._dump()
with open(filename, 'wb') as f:
if im.mode != "RGB":
with open(filename, 'wb') as f:
stderr = tempfile.TemporaryFile()
check_call(["ppmtogif", file], stdout=f, stderr=stderr)
with open(os.devnull, 'wb') as devnull:
check_call(["ppmtogif", file], stdout=f, stderr=devnull)
else:
with open(filename, 'wb') as f:
# Pipe ppmquant output into ppmtogif
# "ppmquant 256 %s | ppmtogif > %s" % (file, filename)
quant_cmd = ["ppmquant", "256", file]
togif_cmd = ["ppmtogif"]
stderr = tempfile.TemporaryFile()
quant_proc = Popen(quant_cmd, stdout=PIPE, stderr=stderr)
stderr = tempfile.TemporaryFile()
togif_proc = Popen(togif_cmd, stdin=quant_proc.stdout, stdout=f,
stderr=stderr)
with open(os.devnull, 'wb') as devnull:
quant_proc = Popen(quant_cmd, stdout=PIPE, stderr=devnull)
togif_proc = Popen(togif_cmd, stdin=quant_proc.stdout,
stdout=f, stderr=devnull)
# Allow ppmquant to receive SIGPIPE if ppmtogif exits
quant_proc.stdout.close()
@ -520,129 +605,184 @@ def _save_netpbm(im, fp, filename):
pass
# --------------------------------------------------------------------
# GIF utilities
# Force optimization so that we can test performance against
# cases where it took lots of memory and time previously.
_FORCE_OPTIMIZE = False
def _get_optimize(im, info):
return im.mode in ("P", "L") and info and info.get("optimize", 0)
"""
Palette optimization is a potentially expensive operation.
This function determines if the palette should be optimized using
some heuristics, then returns the list of palette entries in use.
def _get_used_palette_colors(im):
used_palette_colors = []
:param im: Image object
:param info: encoderinfo
:returns: list of indexes of palette entries in use, or None
"""
if im.mode in ("P", "L") and info and info.get("optimize", 0):
# Potentially expensive operation.
# The palette saves 3 bytes per color not used, but palette
# lengths are restricted to 3*(2**N) bytes. Max saving would
# be 768 -> 6 bytes if we went all the way down to 2 colors.
# * If we're over 128 colors, we can't save any space.
# * If there aren't any holes, it's not worth collapsing.
# * If we have a 'large' image, the palette is in the noise.
# create the new palette if not every color is used
optimise = _FORCE_OPTIMIZE or im.mode == 'L'
if optimise or im.width * im.height < 512 * 512:
# check which colors are used
i = 0
for count in im.histogram():
used_palette_colors = []
for i, count in enumerate(im.histogram()):
if count:
used_palette_colors.append(i)
i += 1
if optimise or (len(used_palette_colors) <= 128 and
max(used_palette_colors) > len(used_palette_colors)):
return used_palette_colors
def getheader(im, palette=None, info=None):
"""Return a list of strings representing a GIF header"""
# Header Block
# http://www.matthewflickinger.com/lab/whatsinagif/bits_and_bytes.asp
version = b"87a"
for extensionKey in ["transparency", "duration", "loop"]:
if info and extensionKey in info and \
not (extensionKey == "duration" and info[extensionKey] == 0):
version = b"89a"
break
else:
if im.info.get("version") == "89a":
version = b"89a"
header = [
b"GIF"+version + # signature + version
o16(im.size[0]) + # canvas width
o16(im.size[1]) # canvas height
]
if im.mode == "P":
if palette and isinstance(palette, bytes):
source_palette = palette[:768]
else:
source_palette = im.im.getpalette("RGB")[:768]
else: # L-mode
if palette and isinstance(palette, bytes):
source_palette = palette[:768]
else:
source_palette = bytearray([i//3 for i in range(768)])
used_palette_colors = palette_bytes = None
if _get_optimize(im, info):
used_palette_colors = _get_used_palette_colors(im)
# create the new palette if not every color is used
if len(used_palette_colors) < 256:
palette_bytes = b""
new_positions = {}
i = 0
# pick only the used colors from the palette
for oldPosition in used_palette_colors:
palette_bytes += source_palette[oldPosition*3:oldPosition*3+3]
new_positions[oldPosition] = i
i += 1
# replace the palette color id of all pixel with the new id
image_bytes = bytearray(im.tobytes())
for i in range(len(image_bytes)):
image_bytes[i] = new_positions[image_bytes[i]]
im.frombytes(bytes(image_bytes))
new_palette_bytes = (palette_bytes +
(768 - len(palette_bytes)) * b'\x00')
im.putpalette(new_palette_bytes)
im.palette = ImagePalette.ImagePalette("RGB",
palette=palette_bytes,
size=len(palette_bytes))
if not palette_bytes:
palette_bytes = source_palette
# Logical Screen Descriptor
def _get_color_table_size(palette_bytes):
# calculate the palette size for the header
import math
color_table_size = int(math.ceil(math.log(len(palette_bytes)//3, 2)))-1
if color_table_size < 0:
color_table_size = 0
# size of global color table + global color table flag
header.append(o8(color_table_size + 128))
# background + reserved/aspect
if info and "background" in info:
background = info["background"]
elif "background" in im.info:
# This elif is redundant within GifImagePlugin
# since im.info parameters are bundled into the info dictionary
# However, external scripts may call getheader directly
# So this maintains earlier behaviour
background = im.info["background"]
else:
background = 0
header.append(o8(background) + o8(0))
# end of Logical Screen Descriptor
return color_table_size
def _get_header_palette(palette_bytes):
"""
Returns the palette, null padded to the next power of 2 (*3) bytes
suitable for direct inclusion in the GIF header
:param palette_bytes: Unpadded palette bytes, in RGBRGB form
:returns: Null padded palette
"""
color_table_size = _get_color_table_size(palette_bytes)
# add the missing amount of bytes
# the palette has to be 2<<n in size
actual_target_size_diff = (2 << color_table_size) - len(palette_bytes)//3
if actual_target_size_diff > 0:
palette_bytes += o8(0) * 3 * actual_target_size_diff
return palette_bytes
def _get_palette_bytes(im):
"""
Gets the palette for inclusion in the gif header
:param im: Image object
:returns: Bytes, len<=768 suitable for inclusion in gif header
"""
return im.palette.palette
def _get_global_header(im, info):
"""Return a list of strings representing a GIF header"""
# Header Block
# http://www.matthewflickinger.com/lab/whatsinagif/bits_and_bytes.asp
version = b"87a"
for extensionKey in ["transparency", "duration", "loop", "comment"]:
if info and extensionKey in info:
if ((extensionKey == "duration" and info[extensionKey] == 0) or
(extensionKey == "comment" and not (1 <= len(info[extensionKey]) <= 255))):
continue
version = b"89a"
break
else:
if im.info.get("version") == b"89a":
version = b"89a"
palette_bytes = _get_palette_bytes(im)
color_table_size = _get_color_table_size(palette_bytes)
background = info["background"] if "background" in info else 0
return [
b"GIF"+version + # signature + version
o16(im.size[0]) + # canvas width
o16(im.size[1]), # canvas height
# Logical Screen Descriptor
# size of global color table + global color table flag
o8(color_table_size + 128), # packed fields
# background + reserved/aspect
o8(background) + o8(0),
# Global Color Table
_get_header_palette(palette_bytes)
]
def _write_frame_data(fp, im_frame, offset, params):
try:
im_frame.encoderinfo = params
# local image header
_write_local_header(fp, im_frame, offset, 0)
ImageFile._save(im_frame, fp, [("gif", (0, 0)+im_frame.size, 0,
RAWMODE[im_frame.mode])])
fp.write(b"\0") # end of image data
finally:
del im_frame.encoderinfo
# --------------------------------------------------------------------
# Legacy GIF utilities
def getheader(im, palette=None, info=None):
"""
Legacy Method to get Gif data from image.
Warning:: May modify image data.
:param im: Image object
:param palette: bytes object containing the source palette, or ....
:param info: encoderinfo
:returns: tuple of(list of header items, optimized palette)
"""
used_palette_colors = _get_optimize(im, info)
if info is None:
info = {}
if "background" not in info and "background" in im.info:
info["background"] = im.info["background"]
im_mod = _normalize_palette(im, palette, info)
im.palette = im_mod.palette
im.im = im_mod.im
header = _get_global_header(im, info)
# Header + Logical Screen Descriptor + Global Color Table
header.append(palette_bytes)
return header, used_palette_colors
# To specify duration, add the time in milliseconds to getdata(),
# e.g. getdata(im_frame, duration=1000)
def getdata(im, offset=(0, 0), **params):
"""Return a list of strings representing this image.
The first string is a local image header, the rest contains
encoded image data."""
"""
Legacy Method
Return a list of strings representing this image.
The first string is a local image header, the rest contains
encoded image data.
:param im: Image object
:param offset: Tuple of (x, y) pixels. Defaults to (0,0)
:param \\**params: E.g. duration or other encoder info parameters
:returns: List of Bytes containing gif encoded frame data
"""
class Collector(object):
data = []
@ -653,18 +793,7 @@ def getdata(im, offset=(0, 0), **params):
fp = Collector()
try:
im.encoderinfo = params
# local image header
_get_local_header(fp, im, offset, 0)
ImageFile._save(im, fp, [("gif", (0, 0)+im.size, 0, RAWMODE[im.mode])])
fp.write(b"\0") # end of image data
finally:
del im.encoderinfo
_write_frame_data(fp, im, offset, params)
return fp.data

View File

@ -14,7 +14,7 @@
#
from math import pi, log, sin, sqrt
from PIL._binary import o8
from ._binary import o8
# --------------------------------------------------------------------
# Stuff to translate curve segments to palette values (derived from
@ -55,6 +55,7 @@ def sphere_increasing(middle, pos):
def sphere_decreasing(middle, pos):
return 1.0 - sqrt(1.0 - linear(middle, pos) ** 2)
SEGMENTS = [linear, curved, sine, sphere_increasing, sphere_decreasing]

View File

@ -15,7 +15,7 @@
#
import re
from PIL._binary import o8
from ._binary import o8
##
@ -41,7 +41,7 @@ class GimpPaletteFile(object):
if not s:
break
# skip fields and comment lines
if re.match(b"\w+:|#", s):
if re.match(br"\w+:|#", s):
continue
if len(s) > 100:
raise SyntaxError("bad palette file")

View File

@ -9,17 +9,18 @@
# See the README file for information on usage and redistribution.
#
from PIL import Image, ImageFile
from . import Image, ImageFile
from ._binary import i8
_handler = None
##
# Install application-specific GRIB image handler.
#
# @param handler Handler object.
def register_handler(handler):
"""
Install application-specific GRIB image handler.
:param handler: Handler object.
"""
global _handler
_handler = handler
@ -28,7 +29,7 @@ def register_handler(handler):
# Image adapter
def _accept(prefix):
return prefix[0:4] == b"GRIB" and prefix[7] == b'\x01'
return prefix[0:4] == b"GRIB" and i8(prefix[7]) == 1
class GribStubImageFile(ImageFile.StubImageFile):

View File

@ -9,17 +9,17 @@
# See the README file for information on usage and redistribution.
#
from PIL import Image, ImageFile
from . import Image, ImageFile
_handler = None
##
# Install application-specific HDF5 image handler.
#
# @param handler Handler object.
def register_handler(handler):
"""
Install application-specific HDF5 image handler.
:param handler: Handler object.
"""
global _handler
_handler = handler
@ -69,5 +69,4 @@ def _save(im, fp, filename):
Image.register_open(HDF5StubImageFile.format, HDF5StubImageFile, _accept)
Image.register_save(HDF5StubImageFile.format, _save)
Image.register_extension(HDF5StubImageFile.format, ".h5")
Image.register_extension(HDF5StubImageFile.format, ".hdf")
Image.register_extensions(HDF5StubImageFile.format, [".h5", ".hdf"])

View File

@ -2,7 +2,7 @@
# The Python Imaging Library.
# $Id$
#
# Mac OS X icns file decoder, based on icns.py by Bob Ippolito.
# macOS icns file decoder, based on icns.py by Bob Ippolito.
#
# history:
# 2004-10-09 fl Turned into a PIL plugin; removed 2.3 dependencies.
@ -15,7 +15,8 @@
# See the README file for information on usage and redistribution.
#
from PIL import Image, ImageFile, PngImagePlugin, _binary
from PIL import Image, ImageFile, PngImagePlugin
from PIL._binary import i8
import io
import os
import shutil
@ -27,8 +28,6 @@ enable_jpeg2k = hasattr(Image.core, 'jp2klib_version')
if enable_jpeg2k:
from PIL import Jpeg2KImagePlugin
i8 = _binary.i8
HEADERSIZE = 8
@ -302,36 +301,39 @@ def _save(im, fp, filename):
"""
Saves the image as a series of PNG files,
that are then converted to a .icns file
using the OS X command line utility 'iconutil'.
using the macOS command line utility 'iconutil'.
OS X only.
macOS only.
"""
if hasattr(fp, "flush"):
fp.flush()
# create the temporary set of pngs
iconset = tempfile.mkdtemp('.iconset')
provided_images = {im.width: im
for im in im.encoderinfo.get("append_images", [])}
last_w = None
last_im = None
for w in [16, 32, 128, 256, 512]:
prefix = 'icon_{}x{}'.format(w, w)
first_path = os.path.join(iconset, prefix+'.png')
if last_w == w:
im_scaled = last_im
shutil.copyfile(second_path, first_path)
else:
im_scaled = im.resize((w, w), Image.LANCZOS)
im_scaled.save(os.path.join(iconset, prefix+'.png'))
im_w = provided_images.get(w, im.resize((w, w), Image.LANCZOS))
im_w.save(first_path)
im_scaled = im.resize((w*2, w*2), Image.LANCZOS)
im_scaled.save(os.path.join(iconset, prefix+'@2x.png'))
last_im = im_scaled
second_path = os.path.join(iconset, prefix+'@2x.png')
im_w2 = provided_images.get(w*2, im.resize((w*2, w*2), Image.LANCZOS))
im_w2.save(second_path)
last_w = w*2
# iconutil -c icns -o {} {}
from subprocess import Popen, PIPE, CalledProcessError
convert_cmd = ["iconutil", "-c", "icns", "-o", filename, iconset]
stderr = tempfile.TemporaryFile()
convert_proc = Popen(convert_cmd, stdout=PIPE, stderr=stderr)
with open(os.devnull, 'wb') as devnull:
convert_proc = Popen(convert_cmd, stdout=PIPE, stderr=devnull)
convert_proc.stdout.close()
@ -343,6 +345,7 @@ def _save(im, fp, filename):
if retcode:
raise CalledProcessError(retcode, convert_cmd)
Image.register_open(IcnsImageFile.format, IcnsImageFile,
lambda x: x[:4] == b'icns')
Image.register_extension(IcnsImageFile.format, '.icns')
@ -354,13 +357,18 @@ if sys.platform == 'darwin':
if __name__ == '__main__':
if len(sys.argv) < 2:
print("Syntax: python IcnsImagePlugin.py [file]")
sys.exit()
imf = IcnsImageFile(open(sys.argv[1], 'rb'))
for size in imf.info['sizes']:
imf.size = size
imf.load()
im = imf.im
im.save('out-%s-%s-%s.png' % size)
im = Image.open(open(sys.argv[1], "rb"))
im = Image.open(sys.argv[1])
im.save("out.png")
if sys.platform == 'windows':
os.startfile("out.png")

View File

@ -15,17 +15,18 @@
# This plugin is a refactored version of Win32IconImagePlugin by Bryan Davis
# <casadebender@gmail.com>.
# https://code.google.com/p/casadebender/wiki/Win32IconImagePlugin
# https://code.google.com/archive/p/casadebender/wikis/Win32IconImagePlugin.wiki
#
# Icon format references:
# * https://en.wikipedia.org/wiki/ICO_(file_format)
# * http://msdn.microsoft.com/en-us/library/ms997538.aspx
# * https://msdn.microsoft.com/en-us/library/ms997538.aspx
import struct
from io import BytesIO
from PIL import Image, ImageFile, BmpImagePlugin, PngImagePlugin, _binary
from . import Image, ImageFile, BmpImagePlugin, PngImagePlugin
from ._binary import i8, i16le as i16, i32le as i32
from math import log, ceil
__version__ = "0.1"
@ -33,10 +34,6 @@ __version__ = "0.1"
#
# --------------------------------------------------------------------
i8 = _binary.i8
i16 = _binary.i16le
i32 = _binary.i32le
_MAGIC = b"\0\0\1\0"
@ -44,16 +41,19 @@ def _save(im, fp, filename):
fp.write(_MAGIC) # (2+2)
sizes = im.encoderinfo.get("sizes",
[(16, 16), (24, 24), (32, 32), (48, 48),
(64, 64), (128, 128), (255, 255)])
(64, 64), (128, 128), (256, 256)])
width, height = im.size
filter(lambda x: False if (x[0] > width or x[1] > height or
x[0] > 255 or x[1] > 255) else True, sizes)
sizes = filter(lambda x: False if (x[0] > width or x[1] > height or
x[0] > 256 or x[1] > 256) else True,
sizes)
sizes = list(sizes)
fp.write(struct.pack("<H", len(sizes))) # idCount(2)
offset = fp.tell() + len(sizes)*16
for size in sizes:
width, height = size
fp.write(struct.pack("B", width)) # bWidth(1)
fp.write(struct.pack("B", height)) # bHeight(1)
# 0 means 256
fp.write(struct.pack("B", width if width < 256 else 0)) # bWidth(1)
fp.write(struct.pack("B", height if height < 256 else 0)) # bHeight(1)
fp.write(b"\0") # bColorCount(1)
fp.write(b"\0") # bReserved(1)
fp.write(b"\0\0") # wPlanes(2)
@ -139,7 +139,7 @@ class IcoFile(object):
"""
Get a list of all available icon sizes and color depths.
"""
return set((h['width'], h['height']) for h in self.entry)
return {(h['width'], h['height']) for h in self.entry}
def getimage(self, size, bpp=False):
"""
@ -176,8 +176,8 @@ class IcoFile(object):
# figure out where AND mask image starts
mode = a[0]
bpp = 8
for k in BmpImagePlugin.BIT2MODE.keys():
if mode == BmpImagePlugin.BIT2MODE[k][1]:
for k, v in BmpImagePlugin.BIT2MODE.items():
if mode == v[1]:
bpp = k
break
@ -215,13 +215,13 @@ class IcoFile(object):
total_bytes = int((w * im.size[1]) / 8)
self.buf.seek(and_mask_offset)
maskData = self.buf.read(total_bytes)
mask_data = self.buf.read(total_bytes)
# convert raw data to image
mask = Image.frombuffer(
'1', # 1 bpp
im.size, # (w, h)
maskData, # source chars
mask_data, # source chars
'raw', # raw decoder
('1;I', int(w/8), -1) # 1bpp inverted, padded, reversed
)
@ -252,7 +252,7 @@ class IcoImageFile(ImageFile.ImageFile):
This plugin is a refactored version of Win32IconImagePlugin by Bryan Davis
<casadebender@gmail.com>.
https://code.google.com/p/casadebender/wiki/Win32IconImagePlugin
https://code.google.com/archive/p/casadebender/wikis/Win32IconImagePlugin.wiki
"""
format = "ICO"
format_description = "Windows Icon"
@ -278,6 +278,7 @@ class IcoImageFile(ImageFile.ImageFile):
#
# --------------------------------------------------------------------
Image.register_open(IcoImageFile.format, IcoImageFile, _accept)
Image.register_save(IcoImageFile.format, _save)
Image.register_extension(IcoImageFile.format, ".ico")

View File

@ -27,8 +27,8 @@
import re
from PIL import Image, ImageFile, ImagePalette
from PIL._binary import i8
from . import Image, ImageFile, ImagePalette
from ._binary import i8
__version__ = "0.7"
@ -109,6 +109,7 @@ class ImImageFile(ImageFile.ImageFile):
format = "IM"
format_description = "IFUNC Image Memory"
_close_exclusive_fp_after_loading = False
def _open(self):
@ -269,11 +270,7 @@ class ImImageFile(ImageFile.ImageFile):
return self.info[FRAMES] > 1
def seek(self, frame):
if frame < 0 or frame >= self.info[FRAMES]:
raise EOFError("seek outside sequence")
if self.frame == frame:
if not self._seek_check(frame):
return
self.frame = frame
@ -291,13 +288,13 @@ class ImImageFile(ImageFile.ImageFile):
self.tile = [("raw", (0, 0)+self.size, offs, (self.rawmode, 0, -1))]
def tell(self):
return self.frame
#
# --------------------------------------------------------------------
# Save IM files
SAVE = {
# mode: (im type, raw mode)
"1": ("0 1", "1"),
@ -318,20 +315,14 @@ SAVE = {
}
def _save(im, fp, filename, check=0):
def _save(im, fp, filename):
try:
image_type, rawmode = SAVE[im.mode]
except KeyError:
raise ValueError("Cannot save %s images as IM" % im.mode)
try:
frames = im.encoderinfo["frames"]
except KeyError:
frames = 1
if check:
return check
frames = im.encoderinfo.get("frames", 1)
fp.write(("Image type: %s image\r\n" % image_type).encode('ascii'))
if filename:
@ -349,6 +340,7 @@ def _save(im, fp, filename, check=0):
# --------------------------------------------------------------------
# Registry
Image.register_open(ImImageFile.format, ImImageFile)
Image.register_save(ImImageFile.format, _save)

File diff suppressed because it is too large Load Diff

View File

@ -15,7 +15,7 @@
# See the README file for information on usage and redistribution.
#
from PIL import Image
from . import Image
def constant(image, value):

View File

@ -1,7 +1,7 @@
# The Python Imaging Library.
# $Id$
# Optional color managment support, based on Kevin Cazabon's PyCMS
# Optional color management support, based on Kevin Cazabon's PyCMS
# library.
# History:
@ -18,6 +18,16 @@
from __future__ import print_function
import sys
from PIL import Image
try:
from PIL import _imagingcms
except ImportError as ex:
# Allow error import for doc purposes, but error out when accessing
# anything in core.
from _util import deferred_error
_imagingcms = deferred_error(ex)
from PIL._util import isStringType
DESCRIPTION = """
pyCMS
@ -85,16 +95,6 @@ VERSION = "1.0.0 pil"
# --------------------------------------------------------------------.
from PIL import Image
try:
from PIL import _imagingcms
except ImportError as ex:
# Allow error import for doc purposes, but error out when accessing
# anything in core.
from _util import deferred_error
_imagingcms = deferred_error(ex)
from PIL._util import isStringType
core = _imagingcms
#
@ -162,8 +162,10 @@ class ImageCmsProfile(object):
self._set(core.profile_open(profile), profile)
elif hasattr(profile, "read"):
self._set(core.profile_frombytes(profile.read()))
elif isinstance(profile, _imagingcms.CmsProfile):
self._set(profile)
else:
self._set(profile) # assume it's already a profile
raise TypeError("Invalid type for Profile")
def _set(self, profile, filename=None):
self.profile = profile
@ -188,10 +190,12 @@ class ImageCmsProfile(object):
class ImageCmsTransform(Image.ImagePointHandler):
# Transform. This can be used with the procedural API, or with the
# standard Image.point() method.
#
# Will return the output profile in the output.info['icc_profile'].
"""
Transform. This can be used with the procedural API, or with the standard
Image.point() method.
Will return the output profile in the output.info['icc_profile'].
"""
def __init__(self, input, output, input_mode, output_mode,
intent=INTENT_PERCEPTUAL, proof=None,
@ -359,7 +363,7 @@ def getOpenProfile(profileFilename):
The PyCMSProfile object can be passed back into pyCMS for use in creating
transforms and such (as in ImageCms.buildTransformFromOpenProfiles()).
If profileFilename is not a vaild filename for an ICC profile, a PyCMSError
If profileFilename is not a valid filename for an ICC profile, a PyCMSError
will be raised.
:param profileFilename: String, as a valid filename path to the ICC profile
@ -550,6 +554,7 @@ def buildProofTransform(
except (IOError, TypeError, ValueError) as v:
raise PyCMSError(v)
buildTransformFromOpenProfiles = buildTransform
buildProofTransformFromOpenProfiles = buildProofTransform
@ -590,7 +595,8 @@ def applyTransform(im, transform, inPlace=0):
with the transform applied is returned (and im is not changed). The
default is False.
:returns: Either None, or a new PIL Image object, depending on the value of
inPlace. The profile will be returned in the image's info['icc_profile'].
inPlace. The profile will be returned in the image's
info['icc_profile'].
:exception PyCMSError:
"""
@ -947,24 +953,3 @@ def versions():
VERSION, core.littlecms_version,
sys.version.split()[0], Image.VERSION
)
# --------------------------------------------------------------------
if __name__ == "__main__":
# create a cheap manual from the __doc__ strings for the functions above
print(__doc__)
for f in dir(sys.modules[__name__]):
doc = None
try:
exec("doc = %s.__doc__" % (f))
if "pyCMS" in doc:
# so we don't get the __doc__ string for imported modules
print("=" * 80)
print("%s" % f)
print(doc)
except (AttributeError, TypeError):
pass
# End of file

View File

@ -17,7 +17,7 @@
# See the README file for information on usage and redistribution.
#
from PIL import Image
from . import Image
import re
@ -31,50 +31,63 @@ def getrgb(color):
:param color: A color string
:return: ``(red, green, blue[, alpha])``
"""
try:
rgb = colormap[color]
except KeyError:
try:
# fall back on case-insensitive lookup
rgb = colormap[color.lower()]
except KeyError:
rgb = None
# found color in cache
color = color.lower()
rgb = colormap.get(color, None)
if rgb:
if isinstance(rgb, tuple):
return rgb
colormap[color] = rgb = getrgb(rgb)
return rgb
# check for known string formats
m = re.match("#\w\w\w$", color)
if m:
if re.match('#[a-f0-9]{3}$', color):
return (
int(color[1]*2, 16),
int(color[2]*2, 16),
int(color[3]*2, 16)
int(color[3]*2, 16),
)
m = re.match("#\w\w\w\w\w\w$", color)
if m:
if re.match('#[a-f0-9]{4}$', color):
return (
int(color[1]*2, 16),
int(color[2]*2, 16),
int(color[3]*2, 16),
int(color[4]*2, 16),
)
if re.match('#[a-f0-9]{6}$', color):
return (
int(color[1:3], 16),
int(color[3:5], 16),
int(color[5:7], 16)
int(color[5:7], 16),
)
m = re.match("rgb\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\)$", color)
if re.match('#[a-f0-9]{8}$', color):
return (
int(color[1:3], 16),
int(color[3:5], 16),
int(color[5:7], 16),
int(color[7:9], 16),
)
m = re.match(r"rgb\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\)$", color)
if m:
return (
int(m.group(1)),
int(m.group(2)),
int(m.group(3))
)
m = re.match("rgb\(\s*(\d+)%\s*,\s*(\d+)%\s*,\s*(\d+)%\s*\)$", color)
m = re.match(r"rgb\(\s*(\d+)%\s*,\s*(\d+)%\s*,\s*(\d+)%\s*\)$", color)
if m:
return (
int((int(m.group(1)) * 255) / 100.0 + 0.5),
int((int(m.group(2)) * 255) / 100.0 + 0.5),
int((int(m.group(3)) * 255) / 100.0 + 0.5)
)
m = re.match("hsl\(\s*(\d+)\s*,\s*(\d+)%\s*,\s*(\d+)%\s*\)$", color)
m = re.match(r"hsl\(\s*(\d+\.?\d*)\s*,\s*(\d+\.?\d*)%\s*,\s*(\d+\.?\d*)%\s*\)$", color)
if m:
from colorsys import hls_to_rgb
rgb = hls_to_rgb(
@ -87,7 +100,22 @@ def getrgb(color):
int(rgb[1] * 255 + 0.5),
int(rgb[2] * 255 + 0.5)
)
m = re.match("rgba\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\)$",
m = re.match(r"hs[bv]\(\s*(\d+\.?\d*)\s*,\s*(\d+\.?\d*)%\s*,\s*(\d+\.?\d*)%\s*\)$", color)
if m:
from colorsys import hsv_to_rgb
rgb = hsv_to_rgb(
float(m.group(1)) / 360.0,
float(m.group(2)) / 100.0,
float(m.group(3)) / 100.0,
)
return (
int(rgb[0] * 255 + 0.5),
int(rgb[1] * 255 + 0.5),
int(rgb[2] * 255 + 0.5)
)
m = re.match(r"rgba\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\)$",
color)
if m:
return (
@ -125,8 +153,9 @@ def getcolor(color, mode):
return color + (alpha,)
return color
colormap = {
# X11 colour table (from "CSS3 module: Color working draft"), with
# X11 colour table from https://drafts.csswg.org/css-color-4/, with
# gray/grey spelling issues fixed. This is a superset of HTML 4.0
# colour names used in CSS 1.
"aliceblue": "#f0f8ff",
@ -248,6 +277,7 @@ colormap = {
"plum": "#dda0dd",
"powderblue": "#b0e0e6",
"purple": "#800080",
"rebeccapurple": "#663399",
"red": "#ff0000",
"rosybrown": "#bc8f8f",
"royalblue": "#4169e1",

View File

@ -31,31 +31,31 @@
#
import numbers
import warnings
from PIL import Image, ImageColor
from PIL._util import isStringType
from . import Image, ImageColor
from ._util import isStringType
##
# A simple 2D drawing interface for PIL images.
# <p>
# Application code should use the <b>Draw</b> factory, instead of
# directly.
"""
A simple 2D drawing interface for PIL images.
<p>
Application code should use the <b>Draw</b> factory, instead of
directly.
"""
class ImageDraw(object):
##
# Create a drawing instance.
#
# @param im The image to draw in.
# @param mode Optional mode to use for color values. For RGB
# images, this argument can be RGB or RGBA (to blend the
# drawing into the image). For all other modes, this argument
# must be the same as the image mode. If omitted, the mode
# defaults to the mode of the image.
def __init__(self, im, mode=None):
"""
Create a drawing instance.
:param im: The image to draw in.
:param mode: Optional mode to use for color values. For RGB
images, this argument can be RGB or RGBA (to blend the
drawing into the image). For all other modes, this argument
must be the same as the image mode. If omitted, the mode
defaults to the mode of the image.
"""
im.load()
if im.readonly:
im._copy() # make it writeable
@ -86,27 +86,14 @@ class ImageDraw(object):
self.fill = 0
self.font = None
def setink(self, ink):
raise Exception("setink() has been removed. " +
"Please use keyword arguments instead.")
def setfill(self, onoff):
raise Exception("setfill() has been removed. " +
"Please use keyword arguments instead.")
def setfont(self, font):
warnings.warn("setfont() is deprecated. " +
"Please set the attribute directly instead.")
# compatibility
self.font = font
##
# Get the current default font.
def getfont(self):
"""
Get the current default font.
:returns: An image font."""
if not self.font:
# FIXME: should add a font repository
from PIL import ImageFont
from . import ImageFont
self.font = ImageFont.load_default()
return self.font
@ -131,18 +118,14 @@ class ImageDraw(object):
fill = self.draw.draw_ink(fill, self.mode)
return ink, fill
##
# Draw an arc.
def arc(self, xy, start, end, fill=None):
"""Draw an arc."""
ink, fill = self._getink(fill)
if ink is not None:
self.draw.draw_arc(xy, start, end, ink)
##
# Draw a bitmap.
def bitmap(self, xy, bitmap, fill=None):
"""Draw a bitmap."""
bitmap.load()
ink, fill = self._getink(fill)
if ink is None:
@ -150,39 +133,30 @@ class ImageDraw(object):
if ink is not None:
self.draw.draw_bitmap(xy, bitmap.im, ink)
##
# Draw a chord.
def chord(self, xy, start, end, fill=None, outline=None):
"""Draw a chord."""
ink, fill = self._getink(outline, fill)
if fill is not None:
self.draw.draw_chord(xy, start, end, fill, 1)
if ink is not None:
self.draw.draw_chord(xy, start, end, ink, 0)
##
# Draw an ellipse.
def ellipse(self, xy, fill=None, outline=None):
"""Draw an ellipse."""
ink, fill = self._getink(outline, fill)
if fill is not None:
self.draw.draw_ellipse(xy, fill, 1)
if ink is not None:
self.draw.draw_ellipse(xy, ink, 0)
##
# Draw a line, or a connected sequence of line segments.
def line(self, xy, fill=None, width=0):
"""Draw a line, or a connected sequence of line segments."""
ink, fill = self._getink(fill)
if ink is not None:
self.draw.draw_lines(xy, ink, width)
##
# (Experimental) Draw a shape.
def shape(self, shape, fill=None, outline=None):
# experimental
"""(Experimental) Draw a shape."""
shape.close()
ink, fill = self._getink(outline, fill)
if fill is not None:
@ -190,61 +164,52 @@ class ImageDraw(object):
if ink is not None:
self.draw.draw_outline(shape, ink, 0)
##
# Draw a pieslice.
def pieslice(self, xy, start, end, fill=None, outline=None):
"""Draw a pieslice."""
ink, fill = self._getink(outline, fill)
if fill is not None:
self.draw.draw_pieslice(xy, start, end, fill, 1)
if ink is not None:
self.draw.draw_pieslice(xy, start, end, ink, 0)
##
# Draw one or more individual pixels.
def point(self, xy, fill=None):
"""Draw one or more individual pixels."""
ink, fill = self._getink(fill)
if ink is not None:
self.draw.draw_points(xy, ink)
##
# Draw a polygon.
def polygon(self, xy, fill=None, outline=None):
"""Draw a polygon."""
ink, fill = self._getink(outline, fill)
if fill is not None:
self.draw.draw_polygon(xy, fill, 1)
if ink is not None:
self.draw.draw_polygon(xy, ink, 0)
##
# Draw a rectangle.
def rectangle(self, xy, fill=None, outline=None):
"""Draw a rectangle."""
ink, fill = self._getink(outline, fill)
if fill is not None:
self.draw.draw_rectangle(xy, fill, 1)
if ink is not None:
self.draw.draw_rectangle(xy, ink, 0)
##
# Draw text.
def _multiline_check(self, text):
split_character = "\n" if isinstance(text, type("")) else b"\n"
"""Draw text."""
split_character = "\n" if isinstance(text, str) else b"\n"
return split_character in text
def _multiline_split(self, text):
split_character = "\n" if isinstance(text, type("")) else b"\n"
split_character = "\n" if isinstance(text, str) else b"\n"
return text.split(split_character)
def text(self, xy, text, fill=None, font=None, anchor=None):
def text(self, xy, text, fill=None, font=None, anchor=None,
*args, **kwargs):
if self._multiline_check(text):
return self.multiline_text(xy, text, fill, font, anchor)
return self.multiline_text(xy, text, fill, font, anchor,
*args, **kwargs)
ink, fill = self._getink(fill)
if font is None:
font = self.getfont()
@ -252,17 +217,17 @@ class ImageDraw(object):
ink = fill
if ink is not None:
try:
mask, offset = font.getmask2(text, self.fontmode)
mask, offset = font.getmask2(text, self.fontmode, *args, **kwargs)
xy = xy[0] + offset[0], xy[1] + offset[1]
except AttributeError:
try:
mask = font.getmask(text, self.fontmode)
mask = font.getmask(text, self.fontmode, *args, **kwargs)
except TypeError:
mask = font.getmask(text)
self.draw.draw_bitmap(xy, mask, ink)
def multiline_text(self, xy, text, fill=None, font=None, anchor=None,
spacing=4, align="left"):
spacing=4, align="left", direction=None, features=None):
widths = []
max_width = 0
lines = self._multiline_split(text)
@ -281,47 +246,51 @@ class ImageDraw(object):
left += (max_width - widths[idx])
else:
assert False, 'align must be "left", "center" or "right"'
self.text((left, top), line, fill, font, anchor)
self.text((left, top), line, fill, font, anchor,
direction=direction, features=features)
top += line_spacing
left = xy[0]
##
# Get the size of a given string, in pixels.
def textsize(self, text, font=None):
def textsize(self, text, font=None, spacing=4, direction=None,
features=None):
"""Get the size of a given string, in pixels."""
if self._multiline_check(text):
return self.multiline_textsize(text, font)
return self.multiline_textsize(text, font, spacing,
direction, features)
if font is None:
font = self.getfont()
return font.getsize(text)
return font.getsize(text, direction, features)
def multiline_textsize(self, text, font=None, spacing=4):
def multiline_textsize(self, text, font=None, spacing=4, direction=None,
features=None):
max_width = 0
lines = self._multiline_split(text)
line_spacing = self.textsize('A', font=font)[1] + spacing
for line in lines:
line_width, line_height = self.textsize(line, font)
line_width, line_height = self.textsize(line, font, spacing,
direction, features)
max_width = max(max_width, line_width)
return max_width, len(lines)*line_spacing
return max_width, len(lines)*line_spacing - spacing
##
# A simple 2D drawing interface for PIL images.
#
# @param im The image to draw in.
# @param mode Optional mode to use for color values. For RGB
# images, this argument can be RGB or RGBA (to blend the
# drawing into the image). For all other modes, this argument
# must be the same as the image mode. If omitted, the mode
# defaults to the mode of the image.
def Draw(im, mode=None):
"""
A simple 2D drawing interface for PIL images.
:param im: The image to draw in.
:param mode: Optional mode to use for color values. For RGB
images, this argument can be RGB or RGBA (to blend the
drawing into the image). For all other modes, this argument
must be the same as the image mode. If omitted, the mode
defaults to the mode of the image.
"""
try:
return im.getdraw(mode)
except AttributeError:
return ImageDraw(im, mode)
# experimental access to the outline API
try:
Outline = Image.core.outline
@ -329,52 +298,56 @@ except AttributeError:
Outline = None
##
# (Experimental) A more advanced 2D drawing interface for PIL images,
# based on the WCK interface.
#
# @param im The image to draw in.
# @param hints An optional list of hints.
# @return A (drawing context, drawing resource factory) tuple.
def getdraw(im=None, hints=None):
"""
(Experimental) A more advanced 2D drawing interface for PIL images,
based on the WCK interface.
:param im: The image to draw in.
:param hints: An optional list of hints.
:returns: A (drawing context, drawing resource factory) tuple.
"""
# FIXME: this needs more work!
# FIXME: come up with a better 'hints' scheme.
handler = None
if not hints or "nicest" in hints:
try:
from PIL import _imagingagg as handler
from . import _imagingagg as handler
except ImportError:
pass
if handler is None:
from PIL import ImageDraw2 as handler
from . import ImageDraw2 as handler
if im:
im = handler.Draw(im)
return im, handler
##
# (experimental) Fills a bounded region with a given color.
#
# @param image Target image.
# @param xy Seed position (a 2-item coordinate tuple).
# @param value Fill color.
# @param border Optional border value. If given, the region consists of
# pixels with a color different from the border color. If not given,
# the region consists of pixels having the same color as the seed
# pixel.
def floodfill(image, xy, value, border=None, thresh=0):
"""
(experimental) Fills a bounded region with a given color.
def floodfill(image, xy, value, border=None):
"Fill bounded region."
:param image: Target image.
:param xy: Seed position (a 2-item coordinate tuple). See
:ref:`coordinate-system`.
:param value: Fill color.
:param border: Optional border value. If given, the region consists of
pixels with a color different from the border color. If not given,
the region consists of pixels having the same color as the seed
pixel.
:param thresh: Optional threshold value which specifies a maximum
tolerable difference of a pixel value from the 'background' in
order for it to be replaced. Useful for filling regions of non-
homogeneous, but similar, colors.
"""
# based on an implementation by Eric S. Raymond
pixel = image.load()
x, y = xy
try:
background = pixel[x, y]
if background == value:
if _color_diff(value, background) <= thresh:
return # seed point already has fill color
pixel[x, y] = value
except IndexError:
except (ValueError, IndexError):
return # seed point outside image
edge = [(x, y)]
if border is None:
@ -387,7 +360,7 @@ def floodfill(image, xy, value, border=None):
except IndexError:
pass
else:
if p == background:
if _color_diff(p, background) <= thresh:
pixel[s, t] = value
newedge.append((s, t))
edge = newedge
@ -405,3 +378,10 @@ def floodfill(image, xy, value, border=None):
pixel[s, t] = value
newedge.append((s, t))
edge = newedge
def _color_diff(rgb1, rgb2):
"""
Uses 1-norm distance to calculate difference between two rgb values.
"""
return abs(rgb1[0]-rgb2[0]) + abs(rgb1[1]-rgb2[1]) + abs(rgb1[2]-rgb2[2])

View File

@ -16,7 +16,7 @@
# See the README file for information on usage and redistribution.
#
from PIL import Image, ImageColor, ImageDraw, ImageFont, ImagePath
from . import Image, ImageColor, ImageDraw, ImageFont, ImagePath
class Pen(object):
@ -98,9 +98,6 @@ class Draw(object):
def rectangle(self, xy, *options):
self.render("rectangle", xy, *options)
def symbol(self, xy, symbol, *options):
raise NotImplementedError("not in this version")
def text(self, xy, text, font):
if self.transform:
xy = ImagePath.Path(xy)

View File

@ -18,7 +18,7 @@
# See the README file for information on usage and redistribution.
#
from PIL import Image, ImageFilter, ImageStat
from . import Image, ImageFilter, ImageStat
class _Enhance(object):
@ -67,7 +67,7 @@ class Contrast(_Enhance):
self.degenerate = Image.new("L", image.size, mean).convert(image.mode)
if 'A' in image.getbands():
self.degenerate.putalpha(image.split()[-1])
self.degenerate.putalpha(image.getchannel('A'))
class Brightness(_Enhance):
@ -82,7 +82,7 @@ class Brightness(_Enhance):
self.degenerate = Image.new(image.mode, image.size, 0)
if 'A' in image.getbands():
self.degenerate.putalpha(image.split()[-1])
self.degenerate.putalpha(image.getchannel('A'))
class Sharpness(_Enhance):
@ -97,4 +97,4 @@ class Sharpness(_Enhance):
self.degenerate = image.filter(ImageFilter.SMOOTH)
if 'A' in image.getbands():
self.degenerate.putalpha(image.split()[-1])
self.degenerate.putalpha(image.getchannel('A'))

View File

@ -27,8 +27,8 @@
# See the README file for information on usage and redistribution.
#
from PIL import Image
from PIL._util import isPath
from . import Image
from ._util import isPath
import io
import os
import sys
@ -78,6 +78,8 @@ class ImageFile(Image.Image):
def __init__(self, fp=None, filename=None):
Image.Image.__init__(self)
self._min_frame = 0
self.tile = None
self.readonly = 1 # until we know better
@ -88,10 +90,13 @@ class ImageFile(Image.Image):
# filename
self.fp = open(fp, "rb")
self.filename = fp
self._exclusive_fp = True
else:
# stream
self.fp = fp
self.filename = filename
# can be overridden
self._exclusive_fp = None
try:
self._open()
@ -100,6 +105,9 @@ class ImageFile(Image.Image):
KeyError, # unsupported mode
EOFError, # got header but not the first frame
struct.error) as v:
# close the file only if we have opened it this constructor
if self._exclusive_fp:
self.fp.close()
raise SyntaxError(v)
if not self.mode or self.size[0] <= 0:
@ -110,11 +118,18 @@ class ImageFile(Image.Image):
pass
def get_format_mimetype(self):
if self.format is None:
return
return Image.MIME.get(self.format.upper())
def verify(self):
"Check file integrity"
# raise exception if something's wrong. must be called
# directly after open, and closes file when finished.
if self._exclusive_fp:
self.fp.close()
self.fp = None
def load(self):
@ -150,32 +165,34 @@ class ImageFile(Image.Image):
if use_mmap:
# try memory mapping
d, e, o, a = self.tile[0]
if d == "raw" and a[0] == self.mode and a[0] in Image._MAPMODES:
decoder_name, extents, offset, args = self.tile[0]
if decoder_name == "raw" and len(args) >= 3 and args[0] == self.mode \
and args[0] in Image._MAPMODES:
try:
if hasattr(Image.core, "map"):
# use built-in mapper
# use built-in mapper WIN32 only
self.map = Image.core.map(self.filename)
self.map.seek(o)
self.map.seek(offset)
self.im = self.map.readimage(
self.mode, self.size, a[1], a[2]
self.mode, self.size, args[1], args[2]
)
else:
# use mmap, if possible
import mmap
fp = open(self.filename, "r+")
size = os.path.getsize(self.filename)
# FIXME: on Unix, use PROT_READ etc
self.map = mmap.mmap(fp.fileno(), size)
with open(self.filename, "r") as fp:
self.map = mmap.mmap(fp.fileno(), 0, access=mmap.ACCESS_READ)
self.im = Image.core.map_buffer(
self.map, self.size, d, e, o, a
self.map, self.size, decoder_name, extents, offset, args
)
readonly = 1
# After trashing self.im, we might need to reload the palette data.
if self.palette:
self.palette.dirty = 1
except (AttributeError, EnvironmentError, ImportError):
self.map = None
self.load_prepare()
err_code = -3 # initialize to unknown error
if not self.map:
# sort tiles in file order
self.tile.sort(key=_tilesort)
@ -186,13 +203,16 @@ class ImageFile(Image.Image):
except AttributeError:
prefix = b""
for d, e, o, a in self.tile:
d = Image._getdecoder(self.mode, d, a, self.decoderconfig)
seek(o)
for decoder_name, extents, offset, args in self.tile:
decoder = Image._getdecoder(self.mode, decoder_name,
args, self.decoderconfig)
try:
d.setimage(self.im, e)
except ValueError:
continue
seek(offset)
decoder.setimage(self.im, extents)
if decoder.pulls_fd:
decoder.setfd(self.fp)
status, err_code = decoder.decode(b"")
else:
b = prefix
while True:
try:
@ -203,45 +223,36 @@ class ImageFile(Image.Image):
else:
raise IOError("image file is truncated")
if not s and not d.handles_eof: # truncated jpeg
self.tile = []
# JpegDecode needs to clean things up here either way
# If we don't destroy the decompressor,
# we have a memory leak.
d.cleanup()
if not s: # truncated jpeg
if LOAD_TRUNCATED_IMAGES:
break
else:
self.tile = []
raise IOError("image file is truncated "
"(%d bytes not processed)" % len(b))
b = b + s
n, e = d.decode(b)
n, err_code = decoder.decode(b)
if n < 0:
break
b = b[n:]
# Need to cleanup here to prevent leaks in PyPy
d.cleanup()
finally:
# Need to cleanup here to prevent leaks
decoder.cleanup()
self.tile = []
self.readonly = readonly
self.fp = None # might be shared
if not self.map and not LOAD_TRUNCATED_IMAGES and e < 0:
# still raised if decoder fails to return anything
raise_ioerror(e)
# post processing
if hasattr(self, "tile_post_rotate"):
# FIXME: This is a hack to handle rotated PCD's
self.im = self.im.rotate(self.tile_post_rotate)
self.size = self.im.size
self.load_end()
if self._exclusive_fp and self._close_exclusive_fp_after_loading:
self.fp.close()
self.fp = None
if not self.map and not LOAD_TRUNCATED_IMAGES and err_code < 0:
# still raised if decoder fails to return anything
raise_ioerror(err_code)
return Image.Image.load(self)
def load_prepare(self):
@ -265,6 +276,16 @@ class ImageFile(Image.Image):
# def load_read(self, bytes):
# pass
def _seek_check(self, frame):
if (frame < self._min_frame or
# Only check upper limit on frames if additional seek operations
# are not required to do so
(not (hasattr(self, "_n_frames") and self._n_frames is None) and
frame >= self.n_frames+self._min_frame)):
raise EOFError("attempt to seek outside sequence")
return self.tell() != frame
class StubImageFile(ImageFile):
"""
@ -300,8 +321,6 @@ class Parser(object):
"""
Incremental image parser. This class implements the standard
feed/close consumer interface.
In Python 2.x, this is an old-style class.
"""
incremental = None
image = None
@ -372,11 +391,8 @@ class Parser(object):
# attempt to open this file
try:
try:
fp = io.BytesIO(self.data)
with io.BytesIO(self.data) as fp:
im = Image.open(fp)
finally:
fp.close() # explicitly close the virtual file
except IOError:
# traceback.print_exc()
pass # not enough data
@ -403,6 +419,12 @@ class Parser(object):
self.image = im
def __enter__(self):
return self
def __exit__(self, *args):
self.close()
def close(self):
"""
(Consumer) Close the stream.
@ -424,12 +446,11 @@ class Parser(object):
if self.data:
# incremental parsing not possible; reopen the file
# not that we have all data
with io.BytesIO(self.data) as fp:
try:
fp = io.BytesIO(self.data)
self.image = Image.open(fp)
finally:
self.image.load()
fp.close() # explicitly close the virtual file
return self.image
@ -466,6 +487,10 @@ def _save(im, fp, tile, bufsize=0):
if o > 0:
fp.seek(o, 0)
e.setimage(im.im, b)
if e.pushes_fd:
e.setfd(fp)
l, s = e.encode_to_pyfd()
else:
while True:
l, s, d = e.encode(bufsize)
fp.write(d)
@ -481,6 +506,10 @@ def _save(im, fp, tile, bufsize=0):
if o > 0:
fp.seek(o, 0)
e.setimage(im.im, b)
if e.pushes_fd:
e.setfd(fp)
l, s = e.encode_to_pyfd()
else:
s = e.encode_to_file(fh, bufsize)
if s < 0:
raise IOError("encoder error %d when writing image file" % s)
@ -511,3 +540,128 @@ def _safe_read(fp, size):
data.append(block)
size -= len(block)
return b"".join(data)
class PyCodecState(object):
def __init__(self):
self.xsize = 0
self.ysize = 0
self.xoff = 0
self.yoff = 0
def extents(self):
return (self.xoff, self.yoff,
self.xoff+self.xsize, self.yoff+self.ysize)
class PyDecoder(object):
"""
Python implementation of a format decoder. Override this class and
add the decoding logic in the `decode` method.
See :ref:`Writing Your Own File Decoder in Python<file-decoders-py>`
"""
_pulls_fd = False
def __init__(self, mode, *args):
self.im = None
self.state = PyCodecState()
self.fd = None
self.mode = mode
self.init(args)
def init(self, args):
"""
Override to perform decoder specific initialization
:param args: Array of args items from the tile entry
:returns: None
"""
self.args = args
@property
def pulls_fd(self):
return self._pulls_fd
def decode(self, buffer):
"""
Override to perform the decoding process.
:param buffer: A bytes object with the data to be decoded. If `handles_eof`
is set, then `buffer` will be empty and `self.fd` will be set.
:returns: A tuple of (bytes consumed, errcode). If finished with decoding
return <0 for the bytes consumed. Err codes are from `ERRORS`
"""
raise NotImplementedError()
def cleanup(self):
"""
Override to perform decoder specific cleanup
:returns: None
"""
pass
def setfd(self, fd):
"""
Called from ImageFile to set the python file-like object
:param fd: A python file-like object
:returns: None
"""
self.fd = fd
def setimage(self, im, extents=None):
"""
Called from ImageFile to set the core output image for the decoder
:param im: A core image object
:param extents: a 4 tuple of (x0, y0, x1, y1) defining the rectangle
for this tile
:returns: None
"""
# following c code
self.im = im
if extents:
(x0, y0, x1, y1) = extents
else:
(x0, y0, x1, y1) = (0, 0, 0, 0)
if x0 == 0 and x1 == 0:
self.state.xsize, self.state.ysize = self.im.size
else:
self.state.xoff = x0
self.state.yoff = y0
self.state.xsize = x1 - x0
self.state.ysize = y1 - y0
if self.state.xsize <= 0 or self.state.ysize <= 0:
raise ValueError("Size cannot be negative")
if (self.state.xsize + self.state.xoff > self.im.size[0] or
self.state.ysize + self.state.yoff > self.im.size[1]):
raise ValueError("Tile cannot extend outside image")
def set_as_raw(self, data, rawmode=None):
"""
Convenience method to set the internal image from a stream of raw data
:param data: Bytes to be set
:param rawmode: The rawmode to be used for the decoder. If not specified,
it will default to the mode of the image
:returns: None
"""
if not rawmode:
rawmode = self.mode
d = Image._getdecoder(self.mode, 'raw', (rawmode))
d.setimage(self.im, self.state.extents())
s = d.decode(data)
if s[0] >= 0:
raise ValueError("not enough image data")
if s[1] != 0:
raise ValueError("cannot decode image data")

View File

@ -15,14 +15,25 @@
# See the README file for information on usage and redistribution.
#
from __future__ import division
import functools
try:
import numpy
except ImportError: # pragma: no cover
numpy = None
class Filter(object):
pass
class Kernel(Filter):
class MultibandFilter(Filter):
pass
class Kernel(MultibandFilter):
"""
Create a convolution kernel. The current version only
supports 3x3 and 5x5 integer and floating point kernels.
@ -39,6 +50,7 @@ class Kernel(Filter):
:param offset: Offset. If given, this value is added to the result,
after it has been divided by the scale factor.
"""
name = "Kernel"
def __init__(self, size, kernel, scale=None, offset=0):
if scale is None:
@ -126,7 +138,6 @@ class MaxFilter(RankFilter):
class ModeFilter(Filter):
"""
Create a mode filter. Picks the most frequent pixel value in a box with the
given size. Pixel values that occur only once or twice are ignored; if no
pixel value occurs more than twice, the original pixel value is preserved.
@ -142,7 +153,7 @@ class ModeFilter(Filter):
return image.modefilter(self.size)
class GaussianBlur(Filter):
class GaussianBlur(MultibandFilter):
"""Gaussian blur filter.
:param radius: Blur radius.
@ -156,7 +167,27 @@ class GaussianBlur(Filter):
return image.gaussian_blur(self.radius)
class UnsharpMask(Filter):
class BoxBlur(MultibandFilter):
"""Blurs the image by setting each pixel to the average value of the pixels
in a square box extending radius pixels in each direction.
Supports float radius of arbitrary size. Uses an optimized implementation
which runs in linear time relative to the size of the image
for any radius value.
:param radius: Size of the box in one direction. Radius 0 does not blur,
returns an identical image. Radius 1 takes 1 pixel
in each direction, i.e. 9 pixels in total.
"""
name = "BoxBlur"
def __init__(self, radius):
self.radius = radius
def filter(self, image):
return image.box_blur(self.radius)
class UnsharpMask(MultibandFilter):
"""Unsharp mask filter.
See Wikipedia's entry on `digital unsharp masking`_ for an explanation of
@ -246,6 +277,15 @@ class FIND_EDGES(BuiltinFilter):
)
class SHARPEN(BuiltinFilter):
name = "Sharpen"
filterargs = (3, 3), 16, 0, (
-2, -2, -2,
-2, 32, -2,
-2, -2, -2
)
class SMOOTH(BuiltinFilter):
name = "Smooth"
filterargs = (3, 3), 13, 0, (
@ -266,10 +306,181 @@ class SMOOTH_MORE(BuiltinFilter):
)
class SHARPEN(BuiltinFilter):
name = "Sharpen"
filterargs = (3, 3), 16, 0, (
-2, -2, -2,
-2, 32, -2,
-2, -2, -2
)
class Color3DLUT(MultibandFilter):
"""Three-dimensional color lookup table.
Transforms 3-channel pixels using the values of the channels as coordinates
in the 3D lookup table and interpolating the nearest elements.
This method allows you to apply almost any color transformation
in constant time by using pre-calculated decimated tables.
.. versionadded:: 5.2.0
:param size: Size of the table. One int or tuple of (int, int, int).
Minimal size in any dimension is 2, maximum is 65.
:param table: Flat lookup table. A list of ``channels * size**3``
float elements or a list of ``size**3`` channels-sized
tuples with floats. Channels are changed first,
then first dimension, then second, then third.
Value 0.0 corresponds lowest value of output, 1.0 highest.
:param channels: Number of channels in the table. Could be 3 or 4.
Default is 3.
:param target_mode: A mode for the result image. Should have not less
than ``channels`` channels. Default is ``None``,
which means that mode wouldn't be changed.
"""
name = "Color 3D LUT"
def __init__(self, size, table, channels=3, target_mode=None, **kwargs):
if channels not in (3, 4):
raise ValueError("Only 3 or 4 output channels are supported")
self.size = size = self._check_size(size)
self.channels = channels
self.mode = target_mode
# Hidden flag `_copy_table=False` could be used to avoid extra copying
# of the table if the table is specially made for the constructor.
copy_table = kwargs.get('_copy_table', True)
items = size[0] * size[1] * size[2]
wrong_size = False
if numpy and isinstance(table, numpy.ndarray):
if copy_table:
table = table.copy()
if table.shape in [(items * channels,), (items, channels),
(size[2], size[1], size[0], channels)]:
table = table.reshape(items * channels)
else:
wrong_size = True
else:
if copy_table:
table = list(table)
# Convert to a flat list
if table and isinstance(table[0], (list, tuple)):
table, raw_table = [], table
for pixel in raw_table:
if len(pixel) != channels:
raise ValueError(
"The elements of the table should "
"have a length of {}.".format(channels))
table.extend(pixel)
if wrong_size or len(table) != items * channels:
raise ValueError(
"The table should have either channels * size**3 float items "
"or size**3 items of channels-sized tuples with floats. "
"Table should be: {}x{}x{}x{}. Actual length: {}".format(
channels, size[0], size[1], size[2], len(table)))
self.table = table
@staticmethod
def _check_size(size):
try:
_, _, _ = size
except ValueError:
raise ValueError("Size should be either an integer or "
"a tuple of three integers.")
except TypeError:
size = (size, size, size)
size = [int(x) for x in size]
for size1D in size:
if not 2 <= size1D <= 65:
raise ValueError("Size should be in [2, 65] range.")
return size
@classmethod
def generate(cls, size, callback, channels=3, target_mode=None):
"""Generates new LUT using provided callback.
:param size: Size of the table. Passed to the constructor.
:param callback: Function with three parameters which correspond
three color channels. Will be called ``size**3``
times with values from 0.0 to 1.0 and should return
a tuple with ``channels`` elements.
:param channels: The number of channels which should return callback.
:param target_mode: Passed to the constructor of the resulting
lookup table.
"""
size1D, size2D, size3D = cls._check_size(size)
if channels not in (3, 4):
raise ValueError("Only 3 or 4 output channels are supported")
table = [0] * (size1D * size2D * size3D * channels)
idx_out = 0
for b in range(size3D):
for g in range(size2D):
for r in range(size1D):
table[idx_out:idx_out + channels] = callback(
r / (size1D-1), g / (size2D-1), b / (size3D-1))
idx_out += channels
return cls((size1D, size2D, size3D), table, channels=channels,
target_mode=target_mode, _copy_table=False)
def transform(self, callback, with_normals=False, channels=None,
target_mode=None):
"""Transforms the table values using provided callback and returns
a new LUT with altered values.
:param callback: A function which takes old lookup table values
and returns a new set of values. The number
of arguments which function should take is
``self.channels`` or ``3 + self.channels``
if ``with_normals`` flag is set.
Should return a tuple of ``self.channels`` or
``channels`` elements if it is set.
:param with_normals: If true, ``callback`` will be called with
coordinates in the color cube as the first
three arguments. Otherwise, ``callback``
will be called only with actual color values.
:param channels: The number of channels in the resulting lookup table.
:param target_mode: Passed to the constructor of the resulting
lookup table.
"""
if channels not in (None, 3, 4):
raise ValueError("Only 3 or 4 output channels are supported")
ch_in = self.channels
ch_out = channels or ch_in
size1D, size2D, size3D = self.size
table = [0] * (size1D * size2D * size3D * ch_out)
idx_in = 0
idx_out = 0
for b in range(size3D):
for g in range(size2D):
for r in range(size1D):
values = self.table[idx_in:idx_in + ch_in]
if with_normals:
values = callback(r / (size1D-1), g / (size2D-1),
b / (size3D-1), *values)
else:
values = callback(*values)
table[idx_out:idx_out + ch_out] = values
idx_in += ch_in
idx_out += ch_out
return type(self)(self.size, table, channels=ch_out,
target_mode=target_mode or self.mode,
_copy_table=False)
def __repr__(self):
r = [
"{} from {}".format(self.__class__.__name__,
self.table.__class__.__name__),
"size={:d}x{:d}x{:d}".format(*self.size),
"channels={:d}".format(self.channels),
]
if self.mode:
r.append("target_mode={}".format(self.mode))
return "<{}>".format(" ".join(r))
def filter(self, image):
from . import Image
return image.color_lut_3d(
self.mode or image.mode, Image.LINEAR, self.channels,
self.size[0], self.size[1], self.size[2], self.table)

View File

@ -25,22 +25,27 @@
# See the README file for information on usage and redistribution.
#
from PIL import Image
from PIL._util import isDirectory, isPath
from . import Image
from ._util import isDirectory, isPath, py3
import os
import sys
LAYOUT_BASIC = 0
LAYOUT_RAQM = 1
class _imagingft_not_installed(object):
# module placeholder
def __getattr__(self, id):
raise ImportError("The _imagingft C module is not installed")
try:
from PIL import _imagingft as core
from . import _imagingft as core
except ImportError:
core = _imagingft_not_installed()
# FIXME: add support for pilfont2 format (see FontFile.py)
# --------------------------------------------------------------------
@ -62,8 +67,7 @@ class ImageFont(object):
def _load_pilfont(self, filename):
fp = open(filename, "rb")
with open(filename, "rb") as fp:
for ext in (".png", ".gif", ".pbm"):
try:
fullname = os.path.splitext(filename)[0] + ext
@ -104,9 +108,11 @@ class ImageFont(object):
self.font = Image.core.font(image.im, data)
# delegate critical operations to internal type
self.getsize = self.font.getsize
self.getmask = self.font.getmask
def getsize(self, text, *args, **kwargs):
return self.font.getsize(text)
def getmask(self, text, mode="", *args, **kwargs):
return self.font.getmask(text, mode)
##
@ -116,7 +122,8 @@ class ImageFont(object):
class FreeTypeFont(object):
"FreeType font wrapper (requires _imagingft service)"
def __init__(self, font=None, size=10, index=0, encoding=""):
def __init__(self, font=None, size=10, index=0, encoding="",
layout_engine=None):
# FIXME: use service provider instead
self.path = font
@ -124,12 +131,25 @@ class FreeTypeFont(object):
self.index = index
self.encoding = encoding
if layout_engine not in (LAYOUT_BASIC, LAYOUT_RAQM):
layout_engine = LAYOUT_BASIC
if core.HAVE_RAQM:
layout_engine = LAYOUT_RAQM
if layout_engine == LAYOUT_RAQM and not core.HAVE_RAQM:
layout_engine = LAYOUT_BASIC
self.layout_engine = layout_engine
if isPath(font):
self.font = core.getfont(font, size, index, encoding)
self.font = core.getfont(font, size, index, encoding, layout_engine=layout_engine)
else:
self.font_bytes = font.read()
self.font = core.getfont(
"", size, index, encoding, self.font_bytes)
"", size, index, encoding, self.font_bytes, layout_engine)
def _multiline_split(self, text):
split_character = "\n" if isinstance(text, str) else b"\n"
return text.split(split_character)
def getname(self):
return self.font.family, self.font.style
@ -137,23 +157,34 @@ class FreeTypeFont(object):
def getmetrics(self):
return self.font.ascent, self.font.descent
def getsize(self, text):
size, offset = self.font.getsize(text)
def getsize(self, text, direction=None, features=None):
size, offset = self.font.getsize(text, direction, features)
return (size[0] + offset[0], size[1] + offset[1])
def getsize_multiline(self, text, direction=None, spacing=4, features=None):
max_width = 0
lines = self._multiline_split(text)
line_spacing = self.getsize('A')[1] + spacing
for line in lines:
line_width, line_height = self.getsize(line, direction, features)
max_width = max(max_width, line_width)
return max_width, len(lines)*line_spacing - spacing
def getoffset(self, text):
return self.font.getsize(text)[1]
def getmask(self, text, mode=""):
return self.getmask2(text, mode)[0]
def getmask(self, text, mode="", direction=None, features=None):
return self.getmask2(text, mode, direction=direction, features=features)[0]
def getmask2(self, text, mode="", fill=Image.core.fill):
size, offset = self.font.getsize(text)
def getmask2(self, text, mode="", fill=Image.core.fill, direction=None, features=None, *args, **kwargs):
size, offset = self.font.getsize(text, direction, features)
im = fill("L", size, 0)
self.font.render(text, im.id, mode == "1")
self.font.render(text, im.id, mode == "1", direction, features)
return im, offset
def font_variant(self, font=None, size=None, index=None, encoding=None):
def font_variant(self, font=None, size=None, index=None, encoding=None,
layout_engine=None):
"""
Create a copy of this FreeTypeFont object,
using any specified arguments to override the settings.
@ -166,34 +197,35 @@ class FreeTypeFont(object):
return FreeTypeFont(font=self.path if font is None else font,
size=self.size if size is None else size,
index=self.index if index is None else index,
encoding=self.encoding if encoding is None else
encoding)
##
# Wrapper that creates a transposed font from any existing font
# object.
#
# @param font A font object.
# @param orientation An optional orientation. If given, this should
# be one of Image.FLIP_LEFT_RIGHT, Image.FLIP_TOP_BOTTOM,
# Image.ROTATE_90, Image.ROTATE_180, or Image.ROTATE_270.
encoding=self.encoding if encoding is None else encoding,
layout_engine=self.layout_engine if layout_engine is None else layout_engine
)
class TransposedFont(object):
"Wrapper for writing rotated or mirrored text"
def __init__(self, font, orientation=None):
"""
Wrapper that creates a transposed font from any existing font
object.
:param font: A font object.
:param orientation: An optional orientation. If given, this should
be one of Image.FLIP_LEFT_RIGHT, Image.FLIP_TOP_BOTTOM,
Image.ROTATE_90, Image.ROTATE_180, or Image.ROTATE_270.
"""
self.font = font
self.orientation = orientation # any 'transpose' argument, or None
def getsize(self, text):
def getsize(self, text, *args, **kwargs):
w, h = self.font.getsize(text)
if self.orientation in (Image.ROTATE_90, Image.ROTATE_270):
return h, w
return w, h
def getmask(self, text, mode=""):
im = self.font.getmask(text, mode)
def getmask(self, text, mode="", *args, **kwargs):
im = self.font.getmask(text, mode, *args, **kwargs)
if self.orientation is not None:
return im.transpose(self.orientation)
return im
@ -213,17 +245,19 @@ def load(filename):
return f
def truetype(font=None, size=10, index=0, encoding=""):
def truetype(font=None, size=10, index=0, encoding="",
layout_engine=None):
"""
Load a TrueType or OpenType font file, and create a font object.
This function loads a font object from the given file, and creates
a font object for a font of the given size.
Load a TrueType or OpenType font from a file or file-like object,
and create a font object.
This function loads a font object from the given file or file-like
object, and creates a font object for a font of the given size.
This function requires the _imagingft service.
:param font: A truetype font file. Under Windows, if the file
is not found in this filename, the loader also looks in
Windows :file:`fonts/` directory.
:param font: A filename or file-like object containing a TrueType font.
Under Windows, if the file is not found in this filename,
the loader also looks in Windows :file:`fonts/` directory.
:param size: The requested size, in points.
:param index: Which font face to load (default is first available face).
:param encoding: Which font encoding to use (default is Unicode). Common
@ -231,12 +265,14 @@ def truetype(font=None, size=10, index=0, encoding=""):
Symbol), "ADOB" (Adobe Standard), "ADBE" (Adobe Expert),
and "armn" (Apple Roman). See the FreeType documentation
for more information.
:param layout_engine: Which layout engine to use, if available:
`ImageFont.LAYOUT_BASIC` or `ImageFont.LAYOUT_RAQM`.
:return: A font object.
:exception IOError: If the file could not be read.
"""
try:
return FreeTypeFont(font, size, index, encoding)
return FreeTypeFont(font, size, index, encoding, layout_engine)
except IOError:
ttf_filename = os.path.basename(font)
@ -267,16 +303,16 @@ def truetype(font=None, size=10, index=0, encoding=""):
for walkfilename in walkfilenames:
if ext and walkfilename == ttf_filename:
fontpath = os.path.join(walkroot, walkfilename)
return FreeTypeFont(fontpath, size, index, encoding)
return FreeTypeFont(fontpath, size, index, encoding, layout_engine)
elif not ext and os.path.splitext(walkfilename)[0] == ttf_filename:
fontpath = os.path.join(walkroot, walkfilename)
if os.path.splitext(fontpath)[1] == '.ttf':
return FreeTypeFont(fontpath, size, index, encoding)
return FreeTypeFont(fontpath, size, index, encoding, layout_engine)
if not ext and first_font_with_a_different_extension is None:
first_font_with_a_different_extension = fontpath
if first_font_with_a_different_extension:
return FreeTypeFont(first_font_with_a_different_extension, size,
index, encoding)
index, encoding, layout_engine)
raise
@ -292,10 +328,10 @@ def load_path(filename):
for directory in sys.path:
if isDirectory(directory):
if not isinstance(filename, str):
if bytes is str:
filename = filename.encode("utf-8")
else:
if py3:
filename = filename.decode("utf-8")
else:
filename = filename.encode("utf-8")
try:
return load(os.path.join(directory, filename))
except IOError:
@ -315,7 +351,7 @@ def load_default():
f = ImageFont()
f._load_pilfont_data(
# courB08
BytesIO(base64.decodestring(b'''
BytesIO(base64.b64decode(b'''
UElMZm9udAo7Ozs7OzsxMDsKREFUQQoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
@ -407,7 +443,7 @@ AJsAEQAGAAAAAP/6AAX//wCbAAoAoAAPAAYAAAAA//oABQABAKAACgClABEABgAA////+AAGAAAA
pQAKAKwAEgAGAAD////4AAYAAACsAAoAswASAAYAAP////gABgAAALMACgC6ABIABgAA////+QAG
AAAAugAKAMEAEQAGAAD////4AAYAAgDBAAoAyAAUAAYAAP////kABQACAMgACgDOABMABgAA////
+QAGAAIAzgAKANUAEw==
''')), Image.open(BytesIO(base64.decodestring(b'''
''')), Image.open(BytesIO(base64.b64decode(b'''
iVBORw0KGgoAAAANSUhEUgAAAx4AAAAUAQAAAAArMtZoAAAEwElEQVR4nABlAJr/AHVE4czCI/4u
Mc4b7vuds/xzjz5/3/7u/n9vMe7vnfH/9++vPn/xyf5zhxzjt8GHw8+2d83u8x27199/nxuQ6Od9
M43/5z2I+9n9ZtmDBwMQECDRQw/eQIQohJXxpBCNVE6QCCAAAAD//wBlAJr/AgALyj1t/wINwq0g
@ -433,5 +469,3 @@ Gc/eeW7BwPj5+QGZhANUswMAAAD//2JgqGBgYGBgqEMXlvhMPUsAAAAA//8iYDd1AAAAAP//AwDR
w7IkEbzhVQAAAABJRU5ErkJggg==
'''))))
return f
# End of file

View File

@ -2,7 +2,7 @@
# The Python Imaging Library
# $Id$
#
# screen grabber (OS X and Windows only)
# screen grabber (macOS and Windows only)
#
# History:
# 2001-04-26 fl created
@ -15,11 +15,11 @@
# See the README file for information on usage and redistribution.
#
from PIL import Image
from . import Image
import sys
if sys.platform not in ["win32", "darwin"]:
raise ImportError("ImageGrab is OS X and Windows only")
raise ImportError("ImageGrab is macOS and Windows only")
if sys.platform == "win32":
grabber = Image.core.grabscreen
@ -31,17 +31,17 @@ elif sys.platform == "darwin":
def grab(bbox=None):
if sys.platform == "darwin":
f, file = tempfile.mkstemp('.png')
os.close(f)
subprocess.call(['screencapture', '-x', file])
im = Image.open(file)
fh, filepath = tempfile.mkstemp('.png')
os.close(fh)
subprocess.call(['screencapture', '-x', filepath])
im = Image.open(filepath)
im.load()
os.unlink(file)
os.unlink(filepath)
else:
size, data = grabber()
im = Image.frombytes(
"RGB", size, data,
# RGB, 32-bit line padding, origo in lower left corner
# RGB, 32-bit line padding, origin lower left corner
"raw", "BGR", (size[0]*3 + 3) & -4, -1
)
if bbox:
@ -51,11 +51,30 @@ def grab(bbox=None):
def grabclipboard():
if sys.platform == "darwin":
raise NotImplementedError("Method is not implemented on OS X")
debug = 0 # temporary interface
data = Image.core.grabclipboard(debug)
fh, filepath = tempfile.mkstemp('.jpg')
os.close(fh)
commands = [
"set theFile to (open for access POSIX file \""+filepath+"\" with write permission)",
"try",
"write (the clipboard as JPEG picture) to theFile",
"end try",
"close access theFile"
]
script = ["osascript"]
for command in commands:
script += ["-e", command]
subprocess.call(script)
im = None
if os.stat(filepath).st_size != 0:
im = Image.open(filepath)
im.load()
os.unlink(filepath)
return im
else:
data = Image.core.grabclipboard()
if isinstance(data, bytes):
from PIL import BmpImagePlugin
from . import BmpImagePlugin
import io
return BmpImagePlugin.DibImageFile(io.BytesIO(data))
return data

View File

@ -15,8 +15,8 @@
# See the README file for information on usage and redistribution.
#
from PIL import Image
from PIL import _imagingmath
from . import Image, _imagingmath
from ._util import py3
try:
import builtins
@ -32,7 +32,7 @@ def _isconstant(v):
class _Operand(object):
# wraps an image operand, providing standard operators
"""Wraps an image operand, providing standard operators"""
def __init__(self, im):
self.im = im
@ -101,7 +101,7 @@ class _Operand(object):
# an image is "true" if it contains at least one non-zero pixel
return self.im.getbbox() is not None
if bytes is str:
if not py3:
# Provide __nonzero__ for pre-Py3k
__nonzero__ = __bool__
del __bool__
@ -152,7 +152,7 @@ class _Operand(object):
def __rpow__(self, other):
return self.apply("pow", other, self)
if bytes is str:
if not py3:
# Provide __div__ and __rdiv__ for pre-Py3k
__div__ = __truediv__
__rdiv__ = __rtruediv__
@ -236,6 +236,7 @@ def imagemath_max(self, other):
def imagemath_convert(self, mode):
return _Operand(self.im.convert(mode))
ops = {}
for k, v in list(globals().items()):
if k[:10] == "imagemath_":

View File

@ -14,13 +14,11 @@
#
# mode descriptor cache
_modes = {}
_modes = None
##
# Wrapper for mode strings.
class ModeDescriptor(object):
"""Wrapper for mode strings."""
def __init__(self, mode, bands, basemode, basetype):
self.mode = mode
@ -32,21 +30,26 @@ class ModeDescriptor(object):
return self.mode
##
# Gets a mode descriptor for the given mode.
def getmode(mode):
"""Gets a mode descriptor for the given mode."""
global _modes
if not _modes:
# initialize mode cache
from PIL import Image
from . import Image
modes = {}
# core modes
for m, (basemode, basetype, bands) in Image._MODEINFO.items():
_modes[m] = ModeDescriptor(m, bands, basemode, basetype)
modes[m] = ModeDescriptor(m, bands, basemode, basetype)
# extra experimental modes
_modes["LA"] = ModeDescriptor("LA", ("L", "A"), "L", "L")
_modes["PA"] = ModeDescriptor("PA", ("P", "A"), "RGB", "L")
modes["RGBa"] = ModeDescriptor("RGBa", ("R", "G", "B", "a"), "RGB", "L")
modes["LA"] = ModeDescriptor("LA", ("L", "A"), "L", "L")
modes["La"] = ModeDescriptor("La", ("L", "a"), "L", "L")
modes["PA"] = ModeDescriptor("PA", ("P", "A"), "RGB", "L")
# mapping modes
_modes["I;16"] = ModeDescriptor("I;16", "I", "L", "L")
_modes["I;16L"] = ModeDescriptor("I;16L", "I", "L", "L")
_modes["I;16B"] = ModeDescriptor("I;16B", "I", "L", "L")
modes["I;16"] = ModeDescriptor("I;16", "I", "L", "L")
modes["I;16L"] = ModeDescriptor("I;16L", "I", "L", "L")
modes["I;16B"] = ModeDescriptor("I;16B", "I", "L", "L")
# set global mode cache atomically
_modes = modes
return _modes[mode]

View File

@ -5,8 +5,9 @@
#
# Copyright (c) 2014 Dov Grobgeld <dov.grobgeld@gmail.com>
from PIL import Image
from PIL import _imagingmorph
from __future__ import print_function
from . import Image, _imagingmorph
import re
LUT_SIZE = 1 << 9
@ -78,7 +79,7 @@ class LutBuilder(object):
def build_default_lut(self):
symbols = [0, 1]
m = 1 << 4 # pos of current pixel
self.lut = bytearray([symbols[(i & m) > 0] for i in range(LUT_SIZE)])
self.lut = bytearray(symbols[(i & m) > 0] for i in range(LUT_SIZE))
def get_lut(self):
return self.lut
@ -88,7 +89,7 @@ class LutBuilder(object):
string permuted according to the permutation list.
"""
assert(len(permutation) == 9)
return ''.join([pattern[p] for p in permutation])
return ''.join(pattern[p] for p in permutation)
def _pattern_permute(self, basic_pattern, options, basic_result):
"""pattern_permute takes a basic pattern and its result and clones
@ -122,7 +123,7 @@ class LutBuilder(object):
.replace('0', 'Z')
.replace('1', '0')
.replace('Z', '1'))
res = '%d' % (1-int(res))
res = 1-int(res)
patterns.append((pattern, res))
return patterns
@ -152,14 +153,14 @@ class LutBuilder(object):
# # Debugging
# for p, r in patterns:
# print p,r
# print '--'
# print(p, r)
# print('--')
# compile the patterns into regular expressions for speed
for i in range(len(patterns)):
p = patterns[i][0].replace('.', 'X').replace('X', '[01]')
for i, pattern in enumerate(patterns):
p = pattern[0].replace('.', 'X').replace('X', '[01]')
p = re.compile(p)
patterns[i] = (p, patterns[i][1])
patterns[i] = (p, pattern[1])
# Step through table and find patterns that match.
# Note that all the patterns are searched. The last one
@ -210,7 +211,7 @@ class MorphOp(object):
an image.
Returns a list of tuples of (x,y) coordinates
of all matching pixels."""
of all matching pixels. See :ref:`coordinate-system`."""
if self.lut is None:
raise Exception('No operator loaded')
@ -222,7 +223,7 @@ class MorphOp(object):
"""Get a list of all turned on pixels in a binary image
Returns a list of tuples of (x,y) coordinates
of all matching pixels."""
of all matching pixels. See :ref:`coordinate-system`."""
if image.mode != 'L':
raise Exception('Image must be binary, meaning it must use mode L')
@ -233,7 +234,7 @@ class MorphOp(object):
with open(filename, 'rb') as f:
self.lut = bytearray(f.read())
if len(self.lut) != 8192:
if len(self.lut) != LUT_SIZE:
self.lut = None
raise Exception('Wrong size operator file!')
@ -247,5 +248,3 @@ class MorphOp(object):
def set_lut(self, lut):
"""Set the lut from an external source"""
self.lut = lut
# End of file

View File

@ -17,10 +17,11 @@
# See the README file for information on usage and redistribution.
#
from PIL import Image
from PIL._util import isStringType
from . import Image
from ._util import isStringType
import operator
import functools
import warnings
#
@ -39,7 +40,7 @@ def _border(border):
def _color(color, mode):
if isStringType(color):
from PIL import ImageColor
from . import ImageColor
color = ImageColor.getcolor(color, mode)
return color
@ -178,6 +179,28 @@ def crop(image, border=0):
)
def scale(image, factor, resample=Image.NEAREST):
"""
Returns a rescaled image by a specific factor given in parameter.
A factor greater than 1 expands the image, between 0 and 1 contracts the
image.
:param image: The image to rescale.
:param factor: The expansion factor, as a float.
:param resample: An optional resampling filter. Same values possible as
in the PIL.Image.resize function.
:returns: An :py:class:`~PIL.Image.Image` object.
"""
if factor == 1:
return image.copy()
elif factor <= 0:
raise ValueError("the factor must be greater than 0")
else:
size = (int(round(factor * image.width)),
int(round(factor * image.height)))
return image.resize(size, resample)
def deform(image, deformer, resample=Image.BILINEAR):
"""
Deform the image.
@ -185,7 +208,8 @@ def deform(image, deformer, resample=Image.BILINEAR):
:param image: The image to deform.
:param deformer: A deformer object. Any object that implements a
**getmesh** method can be used.
:param resample: What resampling filter to use.
:param resample: An optional resampling filter. Same values possible as
in the PIL.Image.transform function.
:return: An image.
"""
return image.transform(
@ -248,6 +272,7 @@ def fit(image, size, method=Image.NEAREST, bleed=0.0, centering=(0.5, 0.5)):
This function was contributed by Kevin Cazabon.
:param image: The image to size and crop.
:param size: The requested output size in pixels, given as a
(width, height) tuple.
:param method: What resampling method to use. Default is
@ -415,6 +440,13 @@ def solarize(image, threshold=128):
def gaussian_blur(im, radius=None):
""" PIL_usm.gblur(im, [radius])"""
warnings.warn(
'PIL.ImageOps.gaussian_blur is deprecated. '
'Use PIL.ImageFilter.GaussianBlur instead. '
'This function will be removed in a future version.',
DeprecationWarning
)
if radius is None:
radius = 5.0
@ -422,12 +454,30 @@ def gaussian_blur(im, radius=None):
return im.im.gaussian_blur(radius)
gblur = gaussian_blur
def gblur(im, radius=None):
""" PIL_usm.gblur(im, [radius])"""
warnings.warn(
'PIL.ImageOps.gblur is deprecated. '
'Use PIL.ImageFilter.GaussianBlur instead. '
'This function will be removed in a future version.',
DeprecationWarning
)
return gaussian_blur(im, radius)
def unsharp_mask(im, radius=None, percent=None, threshold=None):
""" PIL_usm.usm(im, [radius, percent, threshold])"""
warnings.warn(
'PIL.ImageOps.unsharp_mask is deprecated. '
'Use PIL.ImageFilter.UnsharpMask instead. '
'This function will be removed in a future version.',
DeprecationWarning
)
if radius is None:
radius = 5.0
if percent is None:
@ -439,7 +489,18 @@ def unsharp_mask(im, radius=None, percent=None, threshold=None):
return im.im.unsharp_mask(radius, percent, threshold)
usm = unsharp_mask
def usm(im, radius=None, percent=None, threshold=None):
""" PIL_usm.usm(im, [radius, percent, threshold])"""
warnings.warn(
'PIL.ImageOps.usm is deprecated. '
'Use PIL.ImageFilter.UnsharpMask instead. '
'This function will be removed in a future version.',
DeprecationWarning
)
return unsharp_mask(im, radius, percent, threshold)
def box_blur(image, radius):
@ -456,6 +517,13 @@ def box_blur(image, radius):
in each direction, i.e. 9 pixels in total.
:return: An image.
"""
warnings.warn(
'PIL.ImageOps.box_blur is deprecated. '
'Use PIL.ImageFilter.BoxBlur instead. '
'This function will be removed in a future version.',
DeprecationWarning
)
image.load()
return image._new(image.im.box_blur(radius))

View File

@ -17,7 +17,7 @@
#
import array
from PIL import ImageColor
from . import ImageColor, GimpPaletteFile, GimpGradientFile, PaletteFile
class ImagePalette(object):
@ -38,7 +38,7 @@ class ImagePalette(object):
def __init__(self, mode="RGB", palette=None, size=0):
self.mode = mode
self.rawmode = None # if set, palette contains raw data
self.palette = palette or list(range(256))*len(self.mode)
self.palette = palette or bytearray(range(256))*len(self.mode)
self.colors = {}
self.dirty = None
if ((size == 0 and len(self.mode)*256 != len(self.palette)) or
@ -98,7 +98,7 @@ class ImagePalette(object):
except KeyError:
# allocate new color slot
if isinstance(self.palette, bytes):
self.palette = [int(x) for x in self.palette]
self.palette = bytearray(self.palette)
index = len(self.colors)
if index >= 256:
raise ValueError("cannot allocate more than 256 colors")
@ -194,44 +194,23 @@ def load(filename):
# FIXME: supports GIMP gradients only
fp = open(filename, "rb")
with open(filename, "rb") as fp:
lut = None
if not lut:
for paletteHandler in [
GimpPaletteFile.GimpPaletteFile,
GimpGradientFile.GimpGradientFile,
PaletteFile.PaletteFile
]:
try:
from PIL import GimpPaletteFile
fp.seek(0)
p = GimpPaletteFile.GimpPaletteFile(fp)
lut = p.getpalette()
lut = paletteHandler(fp).getpalette()
if lut:
break
except (SyntaxError, ValueError):
# import traceback
# traceback.print_exc()
pass
if not lut:
try:
from PIL import GimpGradientFile
fp.seek(0)
p = GimpGradientFile.GimpGradientFile(fp)
lut = p.getpalette()
except (SyntaxError, ValueError):
# import traceback
# traceback.print_exc()
pass
if not lut:
try:
from PIL import PaletteFile
fp.seek(0)
p = PaletteFile.PaletteFile(fp)
lut = p.getpalette()
except (SyntaxError, ValueError):
# import traceback
# traceback.print_exc()
pass
if not lut:
else:
raise IOError("cannot load palette")
return lut # data, rawmode

View File

@ -14,53 +14,7 @@
# See the README file for information on usage and redistribution.
#
from PIL import Image
from . import Image
# the Python class below is overridden by the C implementation.
class Path(object):
def __init__(self, xy):
pass
##
# Compacts the path, by removing points that are close to each
# other. This method modifies the path in place.
def compact(self, distance=2):
pass
##
# Gets the bounding box.
def getbbox(self):
pass
##
# Maps the path through a function.
def map(self, function):
pass
##
# Converts the path to Python list.
#
# @param flat By default, this function returns a list of 2-tuples
# [(x, y), ...]. If this argument is true, it returns a flat
# list [x, y, ...] instead.
# @return A list of coordinates.
def tolist(self, flat=0):
pass
##
# Transforms the path.
def transform(self, matrix):
pass
# override with C implementation
Path = Image.core.path

View File

@ -16,28 +16,36 @@
# See the README file for information on usage and redistribution.
#
from PIL import Image
from PIL._util import isPath
from . import Image
from ._util import isPath, py3
from io import BytesIO
import sys
qt_is_installed = True
qt_version = None
qt_versions = [
['5', 'PyQt5'],
['4', 'PyQt4'],
['side', 'PySide']
]
# If a version has already been imported, attempt it first
qt_versions.sort(key=lambda qt_version: qt_version[1] in sys.modules, reverse=True)
for qt_version, qt_module in qt_versions:
try:
if qt_module == 'PyQt5':
from PyQt5.QtGui import QImage, qRgba, QPixmap
from PyQt5.QtCore import QBuffer, QIODevice
qt_version = '5'
except ImportError:
try:
elif qt_module == 'PyQt4':
from PyQt4.QtGui import QImage, qRgba, QPixmap
from PyQt4.QtCore import QBuffer, QIODevice
qt_version = '4'
except ImportError:
try:
elif qt_module == 'PySide':
from PySide.QtGui import QImage, qRgba, QPixmap
from PySide.QtCore import QBuffer, QIODevice
qt_version = 'side'
except ImportError:
except (ImportError, RuntimeError):
continue
qt_is_installed = True
break
else:
qt_is_installed = False
qt_version = None
def rgb(r, g, b, a=255):
@ -47,10 +55,11 @@ def rgb(r, g, b, a=255):
return (qRgba(r, g, b, a) & 0xffffffff)
# :param im A PIL Image object, or a file name
# (given either as Python string or a PyQt string object)
def fromqimage(im):
"""
:param im: A PIL Image object, or a file name
(given either as Python string or a PyQt string object)
"""
buffer = QBuffer()
buffer.open(QIODevice.ReadWrite)
# preserve alha channel with png
@ -122,10 +131,10 @@ def _toqclass_helper(im):
# handle filename, if given instead of image name
if hasattr(im, "toUtf8"):
# FIXME - is this really the best way to do this?
if str is bytes:
im = unicode(im.toUtf8(), "utf-8")
else:
if py3:
im = str(im.toUtf8(), "utf-8")
else:
im = unicode(im.toUtf8(), "utf-8")
if isPath(im):
im = Image.open(im)
@ -156,26 +165,31 @@ def _toqclass_helper(im):
else:
raise ValueError("unsupported image mode %r" % im.mode)
# must keep a reference, or Qt will crash!
__data = data or align8to32(im.tobytes(), im.size[0], im.mode)
return {
'data': __data, 'im': im, 'format': format, 'colortable': colortable
}
##
# An PIL image wrapper for Qt. This is a subclass of PyQt's QImage
# class.
#
# @param im A PIL Image object, or a file name (given either as Python
# string or a PyQt string object).
if qt_is_installed:
class ImageQt(QImage):
def __init__(self, im):
"""
An PIL image wrapper for Qt. This is a subclass of PyQt's QImage
class.
:param im: A PIL Image object, or a file name (given either as Python
string or a PyQt string object).
"""
im_data = _toqclass_helper(im)
# must keep a reference, or Qt will crash!
# All QImage constructors that take data operate on an existing
# buffer, so this buffer has to hang on for the life of the image.
# Fixes https://github.com/python-pillow/Pillow/issues/1370
self.__data = im_data['data']
QImage.__init__(self,
im_data['data'], im_data['im'].size[0],
self.__data, im_data['im'].size[0],
im_data['im'].size[1], im_data['format'])
if im_data['colortable']:
self.setColorTable(im_data['colortable'])

View File

@ -32,11 +32,25 @@ class Iterator(object):
if not hasattr(im, "seek"):
raise AttributeError("im must have seek method")
self.im = im
self.position = 0
def __getitem__(self, ix):
try:
if ix:
self.im.seek(ix)
return self.im
except EOFError:
raise IndexError # end of sequence
def __iter__(self):
return self
def __next__(self):
try:
self.im.seek(self.position)
self.position += 1
return self.im
except EOFError:
raise StopIteration
def next(self):
return self.__next__()

View File

@ -18,7 +18,7 @@ from PIL import Image
import os
import sys
if sys.version_info >= (3, 3):
if sys.version_info.major >= 3:
from shlex import quote
else:
from pipes import quote
@ -38,25 +38,23 @@ def register(viewer, order=1):
_viewers.insert(0, viewer)
##
# Displays a given image.
#
# @param image An image object.
# @param title Optional title. Not all viewers can display the title.
# @param **options Additional viewer options.
# @return True if a suitable viewer was found, false otherwise.
def show(image, title=None, **options):
r"""
Display a given image.
:param image: An image object.
:param title: Optional title. Not all viewers can display the title.
:param \**options: Additional viewer options.
:returns: True if a suitable viewer was found, false otherwise.
"""
for viewer in _viewers:
if viewer.show(image, title=title, **options):
return 1
return 0
##
# Base class for viewers.
class Viewer(object):
"""Base class for viewers."""
# main api
@ -71,7 +69,7 @@ class Viewer(object):
# FIXME: auto-contrast if max() > 255?
else:
base = Image.getmodebase(image.mode)
if base != image.mode and image.mode != "1":
if base != image.mode and image.mode != "1" and image.mode != "RGBA":
image = image.convert(base)
return self.show_image(image, **options)
@ -79,29 +77,31 @@ class Viewer(object):
# hook methods
format = None
options = {}
def get_format(self, image):
# return format name, or None to save as PGM/PPM
"""Return format name, or None to save as PGM/PPM"""
return self.format
def get_command(self, file, **options):
raise NotImplementedError
def save_image(self, image):
# save to temporary file, and return filename
return image._dump(format=self.get_format(image))
"""Save to temporary file, and return filename"""
return image._dump(format=self.get_format(image), **self.options)
def show_image(self, image, **options):
# display given image
"""Display given image"""
return self.show_file(self.save_image(image), **options)
def show_file(self, file, **options):
# display given file
"""Display given file"""
os.system(self.get_command(file, **options))
return 1
# --------------------------------------------------------------------
if sys.platform == "win32":
class WindowsViewer(Viewer):
@ -117,7 +117,8 @@ if sys.platform == "win32":
elif sys.platform == "darwin":
class MacViewer(Viewer):
format = "BMP"
format = "PNG"
options = {'compress_level': 1}
def get_command(self, file, **options):
# on darwin open returns immediately resulting in the temp
@ -139,12 +140,14 @@ else:
return None
for dirname in path.split(os.pathsep):
filename = os.path.join(dirname, executable)
if os.path.isfile(filename):
# FIXME: make sure it's executable
if os.path.isfile(filename) and os.access(filename, os.X_OK):
return filename
return None
class UnixViewer(Viewer):
format = "PNG"
options = {'compress_level': 1}
def show_file(self, file, **options):
command, executable = self.get_command_ex(file, **options)
command = "(%s %s; rm -f %s)&" % (command, quote(file),
@ -162,6 +165,14 @@ else:
if which("display"):
register(DisplayViewer)
class EogViewer(UnixViewer):
def get_command_ex(self, file, **options):
command = executable = "eog"
return command, executable
if which("eog"):
register(EogViewer)
class XVViewer(UnixViewer):
def get_command_ex(self, file, title=None, **options):
# note: xv is pretty outdated. most modern systems have
@ -175,5 +186,9 @@ else:
register(XVViewer)
if __name__ == "__main__":
# usage: python ImageShow.py imagefile [title]
if len(sys.argv) < 2:
print("Syntax: python ImageShow.py imagefile [title]")
sys.exit()
print(show(Image.open(sys.argv[1]), *sys.argv[2:]))

View File

@ -144,4 +144,5 @@ class Stat(object):
v.append(math.sqrt(self.var[i]))
return v
Global = Stat # compatibility

View File

@ -25,14 +25,22 @@
# See the README file for information on usage and redistribution.
#
try:
import tkinter
except ImportError:
import Tkinter
tkinter = Tkinter
del Tkinter
import sys
from PIL import Image
if sys.version_info.major > 2:
import tkinter
else:
import Tkinter as tkinter
# required for pypy, which always has cffi installed
try:
from cffi import FFI
ffi = FFI()
except ImportError:
pass
from . import Image
from io import BytesIO
# --------------------------------------------------------------------
@ -53,6 +61,16 @@ def _pilbitmap_check():
return _pilbitmap_ok
def _get_image_from_kw(kw):
source = None
if "file" in kw:
source = kw.pop("file")
elif "data" in kw:
source = BytesIO(kw.pop("data"))
if source:
return Image.open(source)
# --------------------------------------------------------------------
# PhotoImage
@ -80,13 +98,7 @@ class PhotoImage(object):
# Tk compatibility: file or data
if image is None:
if "file" in kw:
image = Image.open(kw["file"])
del kw["file"]
elif "data" in kw:
from io import BytesIO
image = Image.open(BytesIO(kw["data"]))
del kw["data"]
image = _get_image_from_kw(kw)
if hasattr(image, "mode") and hasattr(image, "size"):
# got an image instead of a mode
@ -157,8 +169,8 @@ class PhotoImage(object):
mode does not match, the image is converted to the mode of
the bitmap image.
:param box: A 4-tuple defining the left, upper, right, and lower pixel
coordinate. If None is given instead of a tuple, all of
the image is assumed.
coordinate. See :ref:`coordinate-system`. If None is given
instead of a tuple, all of the image is assumed.
"""
# convert to blittable
@ -177,8 +189,14 @@ class PhotoImage(object):
except tkinter.TclError:
# activate Tkinter hook
try:
from PIL import _imagingtk
from . import _imagingtk
try:
if hasattr(tk, 'interp'):
# Pypy is using a ffi cdata element
# (Pdb) self.tk.interp
# <cdata 'Tcl_Interp *' 0x3061b50>
_imagingtk.tkinit(int(ffi.cast("uintptr_t", tk.interp)), 1)
else:
_imagingtk.tkinit(tk.interpaddr(), 1)
except AttributeError:
_imagingtk.tkinit(id(tk), 0)
@ -192,7 +210,6 @@ class PhotoImage(object):
class BitmapImage(object):
"""
A Tkinter-compatible bitmap image. This can be used everywhere Tkinter
expects an image object.
@ -209,13 +226,7 @@ class BitmapImage(object):
# Tk compatibility: file or data
if image is None:
if "file" in kw:
image = Image.open(kw["file"])
del kw["file"]
elif "data" in kw:
from io import BytesIO
image = Image.open(BytesIO(kw["data"]))
del kw["data"]
image = _get_image_from_kw(kw)
self.__mode = image.mode
self.__size = image.size
@ -266,14 +277,14 @@ class BitmapImage(object):
def getimage(photo):
""" This function is unimplemented """
"""Copies the contents of a PhotoImage to a PIL image memory."""
photo.tk.call("PyImagingPhotoGet", photo)
# --------------------------------------------------------------------
# Helper for the Image.show method.
def _show(image, title):
"""Helper for the Image.show method."""
class UI(tkinter.Label):
def __init__(self, master, im):

View File

@ -13,7 +13,7 @@
# See the README file for information on usage and redistribution.
#
from PIL import Image
from . import Image
class Transform(Image.ImageTransformHandler):
@ -29,75 +29,70 @@ class Transform(Image.ImageTransformHandler):
return image.transform(size, method, data, **options)
##
# Define an affine image transform.
# <p>
# This function takes a 6-tuple (<i>a, b, c, d, e, f</i>) which
# contain the first two rows from an affine transform matrix. For
# each pixel (<i>x, y</i>) in the output image, the new value is
# taken from a position (a <i>x</i> + b <i>y</i> + c,
# d <i>x</i> + e <i>y</i> + f) in the input image, rounded to
# nearest pixel.
# <p>
# This function can be used to scale, translate, rotate, and shear the
# original image.
#
# @def AffineTransform(matrix)
# @param matrix A 6-tuple (<i>a, b, c, d, e, f</i>) containing
# the first two rows from an affine transform matrix.
# @see Image#Image.transform
class AffineTransform(Transform):
"""
Define an affine image transform.
This function takes a 6-tuple (a, b, c, d, e, f) which contain the first
two rows from an affine transform matrix. For each pixel (x, y) in the
output image, the new value is taken from a position (a x + b y + c,
d x + e y + f) in the input image, rounded to nearest pixel.
This function can be used to scale, translate, rotate, and shear the
original image.
See :py:meth:`~PIL.Image.Image.transform`
:param matrix: A 6-tuple (a, b, c, d, e, f) containing the first two rows
from an affine transform matrix.
"""
method = Image.AFFINE
##
# Define a transform to extract a subregion from an image.
# <p>
# Maps a rectangle (defined by two corners) from the image to a
# rectangle of the given size. The resulting image will contain
# data sampled from between the corners, such that (<i>x0, y0</i>)
# in the input image will end up at (0,0) in the output image,
# and (<i>x1, y1</i>) at <i>size</i>.
# <p>
# This method can be used to crop, stretch, shrink, or mirror an
# arbitrary rectangle in the current image. It is slightly slower than
# <b>crop</b>, but about as fast as a corresponding <b>resize</b>
# operation.
#
# @def ExtentTransform(bbox)
# @param bbox A 4-tuple (<i>x0, y0, x1, y1</i>) which specifies
# two points in the input image's coordinate system.
# @see Image#Image.transform
class ExtentTransform(Transform):
"""
Define a transform to extract a subregion from an image.
Maps a rectangle (defined by two corners) from the image to a rectangle of
the given size. The resulting image will contain data sampled from between
the corners, such that (x0, y0) in the input image will end up at (0,0) in
the output image, and (x1, y1) at size.
This method can be used to crop, stretch, shrink, or mirror an arbitrary
rectangle in the current image. It is slightly slower than crop, but about
as fast as a corresponding resize operation.
See :py:meth:`~PIL.Image.Image.transform`
:param bbox: A 4-tuple (x0, y0, x1, y1) which specifies two points in the
input image's coordinate system. See :ref:`coordinate-system`.
"""
method = Image.EXTENT
##
# Define an quad image transform.
# <p>
# Maps a quadrilateral (a region defined by four corners) from the
# image to a rectangle of the given size.
#
# @def QuadTransform(xy)
# @param xy An 8-tuple (<i>x0, y0, x1, y1, x2, y2, y3, y3</i>) which
# contain the upper left, lower left, lower right, and upper right
# corner of the source quadrilateral.
# @see Image#Image.transform
class QuadTransform(Transform):
"""
Define a quad image transform.
Maps a quadrilateral (a region defined by four corners) from the image to a
rectangle of the given size.
See :py:meth:`~PIL.Image.Image.transform`
:param xy: An 8-tuple (x0, y0, x1, y1, x2, y2, x3, y3) which contain the
upper left, lower left, lower right, and upper right corner of the
source quadrilateral.
"""
method = Image.QUAD
##
# Define an mesh image transform. A mesh transform consists of one
# or more individual quad transforms.
#
# @def MeshTransform(data)
# @param data A list of (bbox, quad) tuples.
# @see Image#Image.transform
class MeshTransform(Transform):
"""
Define a mesh image transform. A mesh transform consists of one or more
individual quad transforms.
See :py:meth:`~PIL.Image.Image.transform`
:param data: A list of (bbox, quad) tuples.
"""
method = Image.MESH

View File

@ -17,7 +17,7 @@
# See the README file for information on usage and redistribution.
#
from PIL import Image
from . import Image
class HDC(object):
@ -154,8 +154,9 @@ class Dib(object):
If the mode does not match, the image is converted to the
mode of the bitmap image.
:param box: A 4-tuple defining the left, upper, right, and
lower pixel coordinate. If None is given instead of a
tuple, all of the image is assumed.
lower pixel coordinate. See :ref:`coordinate-system`. If
None is given instead of a tuple, all of the image is
assumed.
"""
im.load()
if self.mode != im.mode:
@ -182,19 +183,9 @@ class Dib(object):
"""
return self.image.tobytes()
def fromstring(self, *args, **kw):
raise Exception("fromstring() has been removed. " +
"Please use frombytes() instead.")
def tostring(self, *args, **kw):
raise Exception("tostring() has been removed. " +
"Please use tobytes() instead.")
##
# Create a Window with the given title size.
class Window(object):
"""Create a Window with the given title size."""
def __init__(self, title="PIL", width=None, height=None):
self.hwnd = Image.core.createwindow(
@ -223,10 +214,8 @@ class Window(object):
Image.core.eventloop()
##
# Create an image window which displays the given image.
class ImageWindow(Window):
"""Create an image window which displays the given image."""
def __init__(self, image, title="PIL"):
if not isinstance(image, Dib):

View File

@ -17,7 +17,7 @@
import re
from PIL import Image, ImageFile
from . import Image, ImageFile
__version__ = "0.2"
@ -69,7 +69,7 @@ class ImtImageFile(ImageFile.ImageFile):
s = s + self.fp.readline()
if len(s) == 1 or len(s) > 100:
break
if s[0] == b"*":
if s[0] == ord(b"*"):
continue # comment
m = field.match(s)

View File

@ -17,17 +17,13 @@
from __future__ import print_function
from PIL import Image, ImageFile, _binary
from . import Image, ImageFile
from ._binary import i8, i16be as i16, i32be as i32, o8
import os
import tempfile
__version__ = "0.3"
i8 = _binary.i8
i16 = _binary.i16be
i32 = _binary.i32be
o8 = _binary.o8
COMPRESSION = {
1: "raw",
5: "jpeg"
@ -99,7 +95,7 @@ class IptcImageFile(ImageFile.ImageFile):
tagdata = self.fp.read(size)
else:
tagdata = None
if tag in list(self.info.keys()):
if tag in self.info:
if isinstance(self.info[tag], list):
self.info[tag].append(tagdata)
else:
@ -107,7 +103,7 @@ class IptcImageFile(ImageFile.ImageFile):
else:
self.info[tag] = tagdata
# print tag, self.info[tag]
# print(tag, self.info[tag])
# mode
layers = i8(self.info[(3, 60)][0])
@ -168,14 +164,9 @@ class IptcImageFile(ImageFile.ImageFile):
o.close()
try:
try:
# fast
self.im = Image.core.open_ppm(outfile)
except:
# slightly slower
im = Image.open(outfile)
im.load()
self.im = im.im
_im = Image.open(outfile)
_im.load()
self.im = _im.im
finally:
try:
os.unlink(outfile)
@ -188,16 +179,15 @@ Image.register_open(IptcImageFile.format, IptcImageFile)
Image.register_extension(IptcImageFile.format, ".iim")
##
# Get IPTC information from TIFF, JPEG, or IPTC file.
#
# @param im An image containing IPTC data.
# @return A dictionary containing IPTC information, or None if
# no IPTC information block was found.
def getiptcinfo(im):
"""
Get IPTC information from TIFF, JPEG, or IPTC file.
from PIL import TiffImagePlugin, JpegImagePlugin
:param im: An image containing IPTC data.
:returns: A dictionary containing IPTC information, or None if
no IPTC information block was found.
"""
from . import TiffImagePlugin, JpegImagePlugin
import io
data = None

View File

@ -12,7 +12,7 @@
#
# See the README file for information on usage and redistribution.
#
from PIL import Image, ImageFile
from . import Image, ImageFile
import struct
import os
import io
@ -29,13 +29,13 @@ def _parse_codestream(fp):
siz = hdr + fp.read(lsiz - 2)
lsiz, rsiz, xsiz, ysiz, xosiz, yosiz, xtsiz, ytsiz, \
xtosiz, ytosiz, csiz \
= struct.unpack('>HHIIIIIIIIH', siz[:38])
= struct.unpack_from('>HHIIIIIIIIH', siz)
ssiz = [None]*csiz
xrsiz = [None]*csiz
yrsiz = [None]*csiz
for i in range(csiz):
ssiz[i], xrsiz[i], yrsiz[i] \
= struct.unpack('>BBB', siz[36 + 3 * i:39 + 3 * i])
= struct.unpack_from('>BBB', siz, 36 + 3 * i)
size = (xsiz - xosiz, ysiz - yosiz)
if csiz == 1:
@ -84,6 +84,7 @@ def _parse_jp2_header(fp):
size = None
mode = None
bpc = None
nc = None
hio = io.BytesIO(header)
while True:
@ -113,9 +114,9 @@ def _parse_jp2_header(fp):
mode = 'RGBA'
break
elif tbox == b'colr':
meth, prec, approx = struct.unpack('>BBB', content[:3])
meth, prec, approx = struct.unpack_from('>BBB', content)
if meth == 1:
cs = struct.unpack('>I', content[3:7])[0]
cs = struct.unpack_from('>I', content, 3)[0]
if cs == 16: # sRGB
if nc == 1 and (bpc & 0x7f) > 8:
mode = 'I;16'
@ -141,6 +142,9 @@ def _parse_jp2_header(fp):
mode = 'RGBA'
break
if size is None or mode is None:
raise SyntaxError("Malformed jp2 header")
return (size, mode)
##
@ -203,7 +207,7 @@ class Jpeg2KImageFile(ImageFile.ImageFile):
t3 = (t[3][0], self.reduce, self.layers, t[3][3], t[3][4])
self.tile = [(t[0], (0, 0) + self.size, t[2], t3)]
ImageFile.ImageFile.load(self)
return ImageFile.ImageFile.load(self)
def _accept(prefix):
@ -262,15 +266,11 @@ def _save(im, fp, filename):
# ------------------------------------------------------------
# Registry stuff
Image.register_open(Jpeg2KImageFile.format, Jpeg2KImageFile, _accept)
Image.register_save(Jpeg2KImageFile.format, _save)
Image.register_extension(Jpeg2KImageFile.format, '.jp2')
Image.register_extension(Jpeg2KImageFile.format, '.j2k')
Image.register_extension(Jpeg2KImageFile.format, '.jpc')
Image.register_extension(Jpeg2KImageFile.format, '.jpf')
Image.register_extension(Jpeg2KImageFile.format, '.jpx')
Image.register_extension(Jpeg2KImageFile.format, '.j2c')
Image.register_extensions(Jpeg2KImageFile.format, [".jp2", ".j2k", ".jpc", ".jpf", ".jpx", ".j2c"])
Image.register_mime(Jpeg2KImageFile.format, 'image/jp2')
Image.register_mime(Jpeg2KImageFile.format, 'image/jpx')

View File

@ -32,19 +32,16 @@
# See the README file for information on usage and redistribution.
#
from __future__ import print_function
import array
import struct
import io
import warnings
from struct import unpack_from
from PIL import Image, ImageFile, TiffImagePlugin, _binary
from PIL.JpegPresets import presets
from PIL._util import isStringType
i8 = _binary.i8
o8 = _binary.o8
i16 = _binary.i16be
i32 = _binary.i32be
from . import Image, ImageFile, TiffImagePlugin
from ._binary import i8, o8, i16be as i16
from .JpegPresets import presets
from ._util import isStringType
__version__ = "0.6"
@ -86,6 +83,7 @@ def APP(self, marker):
self.info["jfif_unit"] = jfif_unit
self.info["jfif_density"] = jfif_density
elif marker == 0xFFE1 and s[:5] == b"Exif\0":
if "exif" not in self.info:
# extract Exif information (incomplete)
self.info["exif"] = s # FIXME: value will change
elif marker == 0xFFE2 and s[:5] == b"FPXR\0":
@ -120,6 +118,26 @@ def APP(self, marker):
# plus constant header size
self.info["mpoffset"] = self.fp.tell() - n + 4
# If DPI isn't in JPEG header, fetch from EXIF
if "dpi" not in self.info and "exif" in self.info:
try:
exif = self._getexif()
resolution_unit = exif[0x0128]
x_resolution = exif[0x011A]
try:
dpi = x_resolution[0] / x_resolution[1]
except TypeError:
dpi = x_resolution
if resolution_unit == 3: # cm
# 1 dpcm = 2.54 dpi
dpi *= 2.54
self.info["dpi"] = dpi, dpi
except (KeyError, SyntaxError, ZeroDivisionError):
# SyntaxError for invalid/unreadable exif
# KeyError for dpi not included
# ZeroDivisionError for invalid dpi rational value
self.info["dpi"] = 72, 72
def COM(self, marker):
#
@ -195,7 +213,7 @@ def DQT(self, marker):
raise SyntaxError("bad quantization table marker")
v = i8(s[0])
if v//16 == 0:
self.quantization[v & 15] = array.array("b", s[1:65])
self.quantization[v & 15] = array.array("B", s[1:65])
s = s[65:]
else:
return # FIXME: add code to read 16-bit tables!
@ -316,7 +334,7 @@ class JpegImageFile(ImageFile.ImageFile):
if i in MARKER:
name, description, handler = MARKER[i]
# print hex(i), name, description
# print(hex(i), name, description)
if handler is not None:
handler(self, i)
if i == 0xFFDA: # start of scan
@ -331,14 +349,35 @@ class JpegImageFile(ImageFile.ImageFile):
elif i == 0 or i == 0xFFFF:
# padded marker or junk; move on
s = b"\xff"
elif i == 0xFF00: # Skip extraneous data (escaped 0xFF)
s = self.fp.read(1)
else:
raise SyntaxError("no marker found")
def load_read(self, read_bytes):
"""
internal: read more image data
For premature EOF and LOAD_TRUNCATED_IMAGES adds EOI marker
so libjpeg can finish decoding
"""
s = self.fp.read(read_bytes)
if not s and ImageFile.LOAD_TRUNCATED_IMAGES:
# Premature EOF.
# Pretend file is finished adding EOI marker
return b"\xFF\xD9"
return s
def draft(self, mode, size):
if len(self.tile) != 1:
return
# Protect from second call
if self.decoderconfig:
return
d, e, o, a = self.tile[0]
scale = 0
@ -347,7 +386,7 @@ class JpegImageFile(ImageFile.ImageFile):
a = mode, ""
if size:
scale = max(self.size[0] // size[0], self.size[1] // size[1])
scale = min(self.size[0] // size[0], self.size[1] // size[1])
for s in [8, 4, 2, 1]:
if scale >= s:
break
@ -375,7 +414,9 @@ class JpegImageFile(ImageFile.ImageFile):
raise ValueError("Invalid Filename")
try:
self.im = Image.core.open_ppm(path)
_im = Image.open(path)
_im.load()
self.im = _im.im
finally:
try:
os.unlink(path)
@ -399,12 +440,13 @@ def _fixup_dict(src_dict):
# returns a dict with any single item tuples/lists as individual values
def _fixup(value):
try:
if len(value) == 1 and type(value) != type({}):
if len(value) == 1 and not isinstance(value, dict):
return value[0]
except: pass
except:
pass
return value
return dict([(k, _fixup(v)) for k, v in src_dict.items()])
return {k: _fixup(v) for k, v in src_dict.items()}
def _getexif(self):
@ -483,8 +525,8 @@ def _getmp(self):
try:
rawmpentries = mp[0xB002]
for entrynum in range(0, quant):
unpackedentry = unpack_from(
'{0}LLLHH'.format(endianness), rawmpentries, entrynum * 16)
unpackedentry = struct.unpack_from(
'{}LLLHH'.format(endianness), rawmpentries, entrynum * 16)
labels = ('Attribute', 'Size', 'DataOffset', 'EntryNo1',
'EntryNo2')
mpentry = dict(zip(labels, unpackedentry))
@ -532,7 +574,6 @@ RAWMODE = {
"1": "L",
"L": "L",
"RGB": "RGB",
"RGBA": "RGB",
"RGBX": "RGB",
"CMYK": "CMYK;I", # assume adobe conventions
"YCbCr": "YCbCr",
@ -583,7 +624,7 @@ def _save(im, fp, filename):
info = im.encoderinfo
dpi = info.get("dpi", (0, 0))
dpi = [int(round(x)) for x in info.get("dpi", (0, 0))]
quality = info.get("quality", 0)
subsampling = info.get("subsampling", -1)
@ -610,7 +651,11 @@ def _save(im, fp, filename):
subsampling = 0
elif subsampling == "4:2:2":
subsampling = 1
elif subsampling == "4:2:0":
subsampling = 2
elif subsampling == "4:1:1":
# For compatibility. Before Pillow 4.3, 4:1:1 actually meant 4:2:0.
# Set 4:2:0 if someone is still using that value.
subsampling = 2
elif subsampling == "keep":
if im.format != "JPEG":
@ -639,8 +684,8 @@ def _save(im, fp, filename):
for idx, table in enumerate(qtables):
try:
if len(table) != 64:
raise
table = array.array('b', table)
raise TypeError
table = array.array('B', table)
except TypeError:
raise ValueError("Invalid quantization table")
else:
@ -672,15 +717,20 @@ def _save(im, fp, filename):
o8(len(markers)) + marker)
i += 1
# get keyword arguments
im.encoderconfig = (
quality,
# "progressive" is the official name, but older documentation
# says "progression"
# FIXME: issue a warning if the wrong form is used (post-1.1.7)
"progressive" in info or "progression" in info,
progressive = (info.get("progressive", False) or
info.get("progression", False))
optimize = info.get("optimize", False)
# get keyword arguments
im.encoderconfig = (
quality,
progressive,
info.get("smooth", 0),
"optimize" in info,
optimize,
info.get("streamtype", 0),
dpi[0], dpi[1],
subsampling,
@ -690,20 +740,24 @@ def _save(im, fp, filename):
)
# if we optimize, libjpeg needs a buffer big enough to hold the whole image
# in a shot. Guessing on the size, at im.size bytes. (raw pizel size is
# in a shot. Guessing on the size, at im.size bytes. (raw pixel size is
# channels*size, this is a value that's been used in a django patch.
# https://github.com/matthewwithanm/django-imagekit/issues/50
bufsize = 0
if "optimize" in info or "progressive" in info or "progression" in info:
if optimize or progressive:
# CMYK can be bigger
if im.mode == 'CMYK':
bufsize = 4 * im.size[0] * im.size[1]
# keep sets quality to 0, but the actual value may be high.
if quality >= 95 or quality == 0:
elif quality >= 95 or quality == 0:
bufsize = 2 * im.size[0] * im.size[1]
else:
bufsize = im.size[0] * im.size[1]
# The exif info needs to be written as one block, + APP1, + one spare byte.
# Ensure that our buffer is big enough
bufsize = max(ImageFile.MAXBLOCK, bufsize, len(info.get("exif", b"")) + 5)
# Ensure that our buffer is big enough. Same with the icc_profile block.
bufsize = max(ImageFile.MAXBLOCK, bufsize, len(info.get("exif", b"")) + 5,
len(extra) + 1)
ImageFile._save(im, fp, [("jpeg", (0, 0)+im.size, 0, rawmode)], bufsize)
@ -745,9 +799,6 @@ def jpeg_factory(fp=None, filename=None):
Image.register_open(JpegImageFile.format, jpeg_factory, _accept)
Image.register_save(JpegImageFile.format, _save)
Image.register_extension(JpegImageFile.format, ".jfif")
Image.register_extension(JpegImageFile.format, ".jpe")
Image.register_extension(JpegImageFile.format, ".jpg")
Image.register_extension(JpegImageFile.format, ".jpeg")
Image.register_extensions(JpegImageFile.format, [".jfif", ".jpe", ".jpg", ".jpeg"])
Image.register_mime(JpegImageFile.format, "image/jpeg")

View File

@ -30,7 +30,7 @@ for chroma information than for luma information.
(ref.: https://en.wikipedia.org/wiki/Chroma_subsampling)
Possible subsampling values are 0, 1 and 2 that correspond to 4:4:4, 4:2:2 and
4:1:1 (or 4:2:0?).
4:2:0.
You can get the subsampling of a JPEG with the
`JpegImagePlugin.get_subsampling(im)` function.
@ -62,12 +62,12 @@ The tables format between im.quantization and quantization in presets differ in
You can convert the dict format to the preset format with the
`JpegImagePlugin.convert_dict_qtables(dict_qtables)` function.
Libjpeg ref.: http://www.jpegcameras.com/libjpeg/libjpeg-3.html
Libjpeg ref.: https://web.archive.org/web/20120328125543/http://www.jpegcameras.com/libjpeg/libjpeg-3.html
"""
presets = {
'web_low': {'subsampling': 2, # "4:1:1"
'web_low': {'subsampling': 2, # "4:2:0"
'quantization': [
[20, 16, 25, 39, 50, 46, 62, 68,
16, 18, 23, 38, 38, 53, 65, 68,
@ -86,7 +86,7 @@ presets = {
68, 68, 68, 68, 68, 68, 68, 68,
68, 68, 68, 68, 68, 68, 68, 68]
]},
'web_medium': {'subsampling': 2, # "4:1:1"
'web_medium': {'subsampling': 2, # "4:2:0"
'quantization': [
[16, 11, 11, 16, 23, 27, 31, 30,
11, 12, 12, 15, 20, 23, 23, 30,
@ -162,7 +162,7 @@ presets = {
3, 3, 3, 3, 3, 3, 3, 3,
3, 3, 3, 3, 3, 3, 3, 3]
]},
'low': {'subsampling': 2, # "4:1:1"
'low': {'subsampling': 2, # "4:2:0"
'quantization': [
[18, 14, 14, 21, 30, 35, 34, 17,
14, 16, 16, 19, 26, 23, 12, 12,
@ -181,7 +181,7 @@ presets = {
17, 12, 12, 12, 12, 12, 12, 12,
17, 12, 12, 12, 12, 12, 12, 12]
]},
'medium': {'subsampling': 2, # "4:1:1"
'medium': {'subsampling': 2, # "4:2:0"
'quantization': [
[12, 8, 8, 12, 17, 21, 24, 17,
8, 9, 9, 11, 15, 19, 12, 12,

View File

@ -17,7 +17,7 @@
#
import struct
from PIL import Image, ImageFile
from . import Image, ImageFile
__version__ = "0.2"
@ -66,6 +66,7 @@ class McIdasImageFile(ImageFile.ImageFile):
self.tile = [("raw", (0, 0) + self.size, offset, (rawmode, stride, 1))]
# --------------------------------------------------------------------
# registry

View File

@ -17,8 +17,9 @@
#
from PIL import Image, TiffImagePlugin
from PIL.OleFileIO import MAGIC, OleFileIO
from . import Image, TiffImagePlugin
import olefile
__version__ = "0.1"
@ -28,7 +29,7 @@ __version__ = "0.1"
def _accept(prefix):
return prefix[:8] == MAGIC
return prefix[:8] == olefile.MAGIC
##
@ -38,6 +39,7 @@ class MicImageFile(TiffImagePlugin.TiffImageFile):
format = "MIC"
format_description = "Microsoft Image Composer"
_close_exclusive_fp_after_loading = False
def _open(self):
@ -45,7 +47,7 @@ class MicImageFile(TiffImagePlugin.TiffImageFile):
# to be a Microsoft Image Composer file
try:
self.ole = OleFileIO(self.fp)
self.ole = olefile.OleFileIO(self.fp)
except IOError:
raise SyntaxError("not an MIC file; invalid OLE file")
@ -63,7 +65,7 @@ class MicImageFile(TiffImagePlugin.TiffImageFile):
raise SyntaxError("not an MIC file; no image entries")
self.__fp = self.fp
self.frame = 0
self.frame = None
if len(self.images) > 1:
self.category = Image.CONTAINER
@ -79,7 +81,8 @@ class MicImageFile(TiffImagePlugin.TiffImageFile):
return len(self.images) > 1
def seek(self, frame):
if not self._seek_check(frame):
return
try:
filename = self.images[frame]
except IndexError:
@ -95,6 +98,7 @@ class MicImageFile(TiffImagePlugin.TiffImageFile):
return self.frame
#
# --------------------------------------------------------------------

View File

@ -14,8 +14,8 @@
#
from PIL import Image, ImageFile
from PIL._binary import i8
from . import Image, ImageFile
from ._binary import i8
__version__ = "0.1"
@ -80,7 +80,6 @@ class MpegImageFile(ImageFile.ImageFile):
Image.register_open(MpegImageFile.format, MpegImageFile)
Image.register_extension(MpegImageFile.format, ".mpg")
Image.register_extension(MpegImageFile.format, ".mpeg")
Image.register_extensions(MpegImageFile.format, [".mpg", ".mpeg"])
Image.register_mime(MpegImageFile.format, "video/mpeg")

View File

@ -18,7 +18,7 @@
# See the README file for information on usage and redistribution.
#
from PIL import Image, JpegImagePlugin
from . import Image, JpegImagePlugin
__version__ = "0.1"
@ -39,6 +39,7 @@ class MpoImageFile(JpegImagePlugin.JpegImageFile):
format = "MPO"
format_description = "MPO (CIPA DC-007)"
_close_exclusive_fp_after_loading = False
def _open(self):
self.fp.seek(0) # prep the fp in order to pass the JPEG test
@ -71,9 +72,8 @@ class MpoImageFile(JpegImagePlugin.JpegImageFile):
return self.__framecount > 1
def seek(self, frame):
if frame < 0 or frame >= self.__framecount:
raise EOFError("no more images in MPO file")
else:
if not self._seek_check(frame):
return
self.fp = self.__fp
self.offset = self.__mpoffsets[frame]
self.tile = [

View File

@ -1,6 +1,5 @@
#
# The Python Imaging Library.
# $Id$
#
# MSP file handling
#
@ -9,15 +8,25 @@
# History:
# 95-09-05 fl Created
# 97-01-03 fl Read/write MSP images
# 17-02-21 es Fixed RLE interpretation
#
# Copyright (c) Secret Labs AB 1997.
# Copyright (c) Fredrik Lundh 1995-97.
# Copyright (c) Eric Soroos 2017.
#
# See the README file for information on usage and redistribution.
#
# More info on this format: https://archive.org/details/gg243631
# Page 313:
# Figure 205. Windows Paint Version 1: "DanM" Format
# Figure 206. Windows Paint Version 2: "LinS" Format. Used in Windows V2.03
#
# See also: http://www.fileformat.info/format/mspaint/egff.htm
from PIL import Image, ImageFile, _binary
from . import Image, ImageFile
from ._binary import i16le as i16, o16le as o16, i8
import struct
import io
__version__ = "0.1"
@ -25,8 +34,6 @@ __version__ = "0.1"
#
# read MSP files
i16 = _binary.i16le
def _accept(prefix):
return prefix[:4] in [b"DanM", b"LinS"]
@ -61,13 +68,93 @@ class MspImageFile(ImageFile.ImageFile):
if s[:4] == b"DanM":
self.tile = [("raw", (0, 0)+self.size, 32, ("1", 0, 1))]
else:
self.tile = [("msp", (0, 0)+self.size, 32+2*self.size[1], None)]
self.tile = [("MSP", (0, 0)+self.size, 32, None)]
class MspDecoder(ImageFile.PyDecoder):
# The algo for the MSP decoder is from
# http://www.fileformat.info/format/mspaint/egff.htm
# cc-by-attribution -- That page references is taken from the
# Encyclopedia of Graphics File Formats and is licensed by
# O'Reilly under the Creative Common/Attribution license
#
# For RLE encoded files, the 32byte header is followed by a scan
# line map, encoded as one 16bit word of encoded byte length per
# line.
#
# NOTE: the encoded length of the line can be 0. This was not
# handled in the previous version of this encoder, and there's no
# mention of how to handle it in the documentation. From the few
# examples I've seen, I've assumed that it is a fill of the
# background color, in this case, white.
#
#
# Pseudocode of the decoder:
# Read a BYTE value as the RunType
# If the RunType value is zero
# Read next byte as the RunCount
# Read the next byte as the RunValue
# Write the RunValue byte RunCount times
# If the RunType value is non-zero
# Use this value as the RunCount
# Read and write the next RunCount bytes literally
#
# e.g.:
# 0x00 03 ff 05 00 01 02 03 04
# would yield the bytes:
# 0xff ff ff 00 01 02 03 04
#
# which are then interpreted as a bit packed mode '1' image
_pulls_fd = True
def decode(self, buffer):
img = io.BytesIO()
blank_line = bytearray((0xff,)*((self.state.xsize+7)//8))
try:
self.fd.seek(32)
rowmap = struct.unpack_from("<%dH" % (self.state.ysize),
self.fd.read(self.state.ysize*2))
except struct.error:
raise IOError("Truncated MSP file in row map")
for x, rowlen in enumerate(rowmap):
try:
if rowlen == 0:
img.write(blank_line)
continue
row = self.fd.read(rowlen)
if len(row) != rowlen:
raise IOError("Truncated MSP file, expected %d bytes on row %s",
(rowlen, x))
idx = 0
while idx < rowlen:
runtype = i8(row[idx])
idx += 1
if runtype == 0:
(runcount, runval) = struct.unpack_from("Bc", row, idx)
img.write(runval * runcount)
idx += 2
else:
runcount = runtype
img.write(row[idx:idx+runcount])
idx += runcount
except struct.error:
raise IOError("Corrupted MSP file in row %d" % x)
self.set_as_raw(img.getvalue(), ("1", 0, 1))
return 0, 0
Image.register_decoder('MSP', MspDecoder)
#
# write MSP files (uncompressed only)
o16 = _binary.o16le
def _save(im, fp, filename):
@ -95,6 +182,7 @@ def _save(im, fp, filename):
# image body
ImageFile._save(im, fp, [("raw", (0, 0)+im.size, 32, ("1", 0, 1))])
#
# registry

View File

@ -1,180 +0,0 @@
olefile (formerly OleFileIO_PL)
===============================
[olefile](http://www.decalage.info/olefile) is a Python package to parse, read and write
[Microsoft OLE2 files](http://en.wikipedia.org/wiki/Compound_File_Binary_Format)
(also called Structured Storage, Compound File Binary Format or Compound Document File Format),
such as Microsoft Office 97-2003 documents, vbaProject.bin in MS Office 2007+ files, Image Composer
and FlashPix files, Outlook messages, StickyNotes, several Microscopy file formats, McAfee antivirus quarantine files,
etc.
**Quick links:** [Home page](http://www.decalage.info/olefile) -
[Download/Install](https://bitbucket.org/decalage/olefileio_pl/wiki/Install) -
[Documentation](https://bitbucket.org/decalage/olefileio_pl/wiki) -
[Report Issues/Suggestions/Questions](https://bitbucket.org/decalage/olefileio_pl/issues?status=new&status=open) -
[Contact the author](http://decalage.info/contact) -
[Repository](https://bitbucket.org/decalage/olefileio_pl) -
[Updates on Twitter](https://twitter.com/decalage2)
News
----
Follow all updates and news on Twitter: <https://twitter.com/decalage2>
- **2015-01-25 v0.42**: improved handling of special characters in stream/storage names on Python 2.x (using UTF-8
instead of Latin-1), fixed bug in listdir with empty storages.
- 2014-11-25 v0.41: OleFileIO.open and isOleFile now support OLE files stored in byte strings, fixed installer for
python 3, added support for Jython (Niko Ehrenfeuchter)
- 2014-10-01 v0.40: renamed OleFileIO_PL to olefile, added initial write support for streams >4K, updated doc and
license, improved the setup script.
- 2014-07-27 v0.31: fixed support for large files with 4K sectors, thanks to Niko Ehrenfeuchter, Martijn Berger and
Dave Jones. Added test scripts from Pillow (by hugovk). Fixed setup for Python 3 (Martin Panter)
- 2014-02-04 v0.30: now compatible with Python 3.x, thanks to Martin Panter who did most of the hard work.
- 2013-07-24 v0.26: added methods to parse stream/storage timestamps, improved listdir to include storages, fixed
parsing of direntry timestamps
- 2013-05-27 v0.25: improved metadata extraction, properties parsing and exception handling, fixed
[issue #12](https://bitbucket.org/decalage/olefileio_pl/issue/12/error-when-converting-timestamps-in-ole)
- 2013-05-07 v0.24: new features to extract metadata (get\_metadata method and OleMetadata class), improved
getproperties to convert timestamps to Python datetime
- 2012-10-09: published [python-oletools](http://www.decalage.info/python/oletools), a package of analysis tools based
on OleFileIO_PL
- 2012-09-11 v0.23: added support for file-like objects, fixed [issue #8](https://bitbucket.org/decalage/olefileio_pl/issue/8/bug-with-file-object)
- 2012-02-17 v0.22: fixed issues #7 (bug in getproperties) and #2 (added close method)
- 2011-10-20: code hosted on bitbucket to ease contributions and bug tracking
- 2010-01-24 v0.21: fixed support for big-endian CPUs, such as PowerPC Macs.
- 2009-12-11 v0.20: small bugfix in OleFileIO.open when filename is not plain str.
- 2009-12-10 v0.19: fixed support for 64 bits platforms (thanks to Ben G. and Martijn for reporting the bug)
- see changelog in source code for more info.
Download/Install
----------------
If you have pip or setuptools installed (pip is included in Python 2.7.9+), you may simply run **pip install olefile**
or **easy_install olefile** for the first installation.
To update olefile, run **pip install -U olefile**.
Otherwise, see https://bitbucket.org/decalage/olefileio_pl/wiki/Install
Features
--------
- Parse, read and write any OLE file such as Microsoft Office 97-2003 legacy document formats (Word .doc, Excel .xls,
PowerPoint .ppt, Visio .vsd, Project .mpp), Image Composer and FlashPix files, Outlook messages, StickyNotes,
Zeiss AxioVision ZVI files, Olympus FluoView OIB files, etc
- List all the streams and storages contained in an OLE file
- Open streams as files
- Parse and read property streams, containing metadata of the file
- Portable, pure Python module, no dependency
olefile can be used as an independent package or with PIL/Pillow.
olefile is mostly meant for developers. If you are looking for tools to analyze OLE files or to extract data (especially
for security purposes such as malware analysis and forensics), then please also check my
[python-oletools](http://www.decalage.info/python/oletools), which are built upon olefile and provide a higher-level interface.
History
-------
olefile is based on the OleFileIO module from [PIL](http://www.pythonware.com/products/pil/index.htm), the excellent
Python Imaging Library, created and maintained by Fredrik Lundh. The olefile API is still compatible with PIL, but
since 2005 I have improved the internal implementation significantly, with new features, bugfixes and a more robust
design. From 2005 to 2014 the project was called OleFileIO_PL, and in 2014 I changed its name to olefile to celebrate
its 9 years and its new write features.
As far as I know, olefile is the most complete and robust Python implementation to read MS OLE2 files, portable on
several operating systems. (please tell me if you know other similar Python modules)
Since 2014 olefile/OleFileIO_PL has been integrated into [Pillow](http://python-imaging.github.io/), the friendly fork
of PIL. olefile will continue to be improved as a separate project, and new versions will be merged into Pillow
regularly.
Main improvements over the original version of OleFileIO in PIL:
----------------------------------------------------------------
- Compatible with Python 3.x and 2.6+
- Many bug fixes
- Support for files larger than 6.8MB
- Support for 64 bits platforms and big-endian CPUs
- Robust: many checks to detect malformed files
- Runtime option to choose if malformed files should be parsed or raise exceptions
- Improved API
- Metadata extraction, stream/storage timestamps (e.g. for document forensics)
- Can open file-like objects
- Added setup.py and install.bat to ease installation
- More convenient slash-based syntax for stream paths
- Write features
Documentation
-------------
Please see the [online documentation](https://bitbucket.org/decalage/olefileio_pl/wiki) for more information,
especially the [OLE overview](https://bitbucket.org/decalage/olefileio_pl/wiki/OLE_Overview) and the
[API page](https://bitbucket.org/decalage/olefileio_pl/wiki/API) which describe how to use olefile in Python applications.
A copy of the same documentation is also provided in the doc subfolder of the olefile package.
## Real-life examples ##
A real-life example: [using OleFileIO_PL for malware analysis and forensics](http://blog.gregback.net/2011/03/using-remnux-for-forensic-puzzle-6/).
See also [this paper](https://computer-forensics.sans.org/community/papers/gcfa/grow-forensic-tools-taxonomy-python-libraries-helpful-forensic-analysis_6879) about python tools for forensics, which features olefile.
License
-------
olefile (formerly OleFileIO_PL) is copyright (c) 2005-2015 Philippe Lagadec
([http://www.decalage.info](http://www.decalage.info))
All rights reserved.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
----------
olefile is based on source code from the OleFileIO module of the Python Imaging Library (PIL) published by Fredrik
Lundh under the following license:
The Python Imaging Library (PIL) is
- Copyright (c) 1997-2005 by Secret Labs AB
- Copyright (c) 1995-2005 by Fredrik Lundh
By obtaining, using, and/or copying this software and/or its associated documentation, you agree that you have read,
understood, and will comply with the following terms and conditions:
Permission to use, copy, modify, and distribute this software and its associated documentation for any purpose and
without fee is hereby granted, provided that the above copyright notice appears in all copies, and that both that
copyright notice and this permission notice appear in supporting documentation, and that the name of Secret Labs AB or
the author not be used in advertising or publicity pertaining to distribution of the software without specific, written
prior permission.
SECRET LABS AB AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES
OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL SECRET LABS AB OR THE AUTHOR BE LIABLE FOR ANY SPECIAL, INDIRECT OR
CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
SOFTWARE.

File diff suppressed because it is too large Load Diff

View File

@ -15,7 +15,8 @@
# See the README file for information on usage and redistribution.
#
from PIL import EpsImagePlugin
from . import EpsImagePlugin
from ._util import py3
import sys
##
@ -24,7 +25,7 @@ import sys
class PSDraw(object):
"""
Sets up printing to the given file. If **file** is omitted,
Sets up printing to the given file. If **fp** is omitted,
:py:attr:`sys.stdout` is assumed.
"""
@ -34,7 +35,7 @@ class PSDraw(object):
self.fp = fp
def _fp_write(self, to_write):
if bytes is str or self.fp == sys.stdout:
if not py3 or self.fp == sys.stdout:
self.fp.write(to_write)
else:
self.fp.write(bytes(to_write, 'UTF-8'))
@ -153,6 +154,7 @@ class PSDraw(object):
# Copyright (c) Fredrik Lundh 1994.
#
EDROFF_PS = """\
/S { show } bind def
/P { moveto show } bind def

View File

@ -13,7 +13,7 @@
# See the README file for information on usage and redistribution.
#
from PIL._binary import o8
from ._binary import o8
##

Some files were not shown because too many files have changed in this diff Show More