summaryrefslogtreecommitdiff
path: root/demos/python
diff options
context:
space:
mode:
authorJulian Smith <jules@op59.net>2020-07-24 14:03:51 +0100
committerJulian Smith <jules@op59.net>2020-07-24 17:53:28 +0100
commitf77f99702c9c8418b9516056d5f4280105beafc4 (patch)
tree8de7d49c98dac3a525c3e81920cbe513fb3a9fbf /demos/python
parent269f880585dd2c6b3f716668b05f4c911214356b (diff)
downloadghostpdl-f77f99702c9c8418b9516056d5f4280105beafc4.tar.gz
demos/python/gsapi.py: improved encoding/decoding of strings.
Set global _encoding to the encoding passed to gsapi_set_arg_encoding(), and use it where expected by the underlying C code. gsapi_set_stdio(): Pass bytes object to stdout and stderr callbacks for convenience, and document what expectations are. gsapi_run_string*(): Accept str or bytes; encode the former into bytes using utf-8 encoding.
Diffstat (limited to 'demos/python')
-rwxr-xr-xdemos/python/gsapi.py147
1 files changed, 117 insertions, 30 deletions
diff --git a/demos/python/gsapi.py b/demos/python/gsapi.py
index ad9fadebb..0888ee608 100755
--- a/demos/python/gsapi.py
+++ b/demos/python/gsapi.py
@@ -59,13 +59,15 @@ def gsapi_revision():
'''
Returns (e, r) where <r> is a gsapi_revision_t.
'''
+ # [unicode: we assume that underlying gsapi_revision() returns utf-8
+ # strings.]
_r = _gsapi_revision_t()
e = _libgs.gsapi_revision(ctypes.byref(_r), ctypes.sizeof(_r))
if e:
return e, None
r = gsapi_revision_t(
- _r.product.decode('latin-1'),
- _r.copyright.decode('latin-1'),
+ _r.product.decode('utf-8'),
+ _r.copyright.decode('utf-8'),
_r.revision,
_r.revisiondate,
)
@@ -87,9 +89,42 @@ def gsapi_delete_instance(instance):
def gsapi_set_stdio(instance, stdin_fn, stdout_fn, stderr_fn):
- stdin_fn2 = _stdio_fn(stdin_fn) if stdin_fn else None
- stdout_fn2 = _stdio_fn(stdout_fn) if stdout_fn else None
- stderr_fn2 = _stdio_fn(stderr_fn) if stderr_fn else None
+ '''
+ stdin_fn:
+ If not None, will be called with (caller_handle, text, len_)
+ where <text> is a ctypes.LP_c_char of length <len_>.
+
+ [todo: wrap this to be easier to use from Python?]
+
+ stdout_fn and stderr_fn:
+ If not None, called with (caller_handle, text):
+ caller_handle:
+ As passed originally to gsapi_new_instance().
+ text:
+ A Python bytes object.
+ Should return the number of bytes of <text> that they handled; for
+ convenience None is converted to len(text).
+ '''
+ # [unicode: we do not do any encoding or decoding; stdin_fn should encode
+ # and stdout_fn and stderr_fn should decode. ]
+ def make_out(fn):
+ if not fn:
+ return None
+ def out(caller_handle, text, len_):
+ text2 = text[:len_] # converts from ctypes.LP_c_char to bytes.
+ ret = fn(caller_handle, text2)
+ if ret is None:
+ return len_
+ return ret
+ return _stdio_fn(out)
+ def make_in(fn):
+ if not fn:
+ return None
+ return _stdio_fn(fn)
+
+ stdout_fn2 = make_out(stdout_fn)
+ stderr_fn2 = make_out(stderr_fn)
+ stdin_fn2 = make_in(stdin_fn)
e = _libgs.gsapi_set_stdio(instance, stdout_fn2, stdout_fn2, stdout_fn2)
if not e:
# Need to keep references to call-back functions.
@@ -161,12 +196,11 @@ def gsapi_set_display_callback(instance, callback):
def gsapi_set_default_device_list(instance, list_):
+ # [unicode: we assume that underlying gsapi_set_default_device_list() is
+ # expecting list_ to be in utf-8 encoding.]
assert isinstance(list_, str)
- e = _libgs.gsapi_set_default_device_list(
- instance,
- list_.encode('latin-1'),
- len(list_),
- )
+ list_2 = list_.encode('utf-8')
+ e = _libgs.gsapi_set_default_device_list(instance, list_2, len(list_))
return e
@@ -174,6 +208,8 @@ def gsapi_get_default_device_list(instance):
'''
Returns (e, list) where <list> is a string.
'''
+ # [unicode: we assume underlying gsapi_get_default_device_list() returns
+ # strings encoded as latin-1.]
list_ = ctypes.POINTER(ctypes.c_char)()
len_ = ctypes.c_int()
e = _libgs.gsapi_get_default_device_list(
@@ -183,7 +219,7 @@ def gsapi_get_default_device_list(instance):
)
if e:
return e, ''
- return e, list_[:len_.value]
+ return e, list_[:len_.value].decode('latin-1')
GS_ARG_ENCODING_LOCAL = 0
@@ -192,16 +228,33 @@ GS_ARG_ENCODING_UTF16LE = 2
def gsapi_set_arg_encoding(instance, encoding):
+ assert encoding in (
+ GS_ARG_ENCODING_LOCAL,
+ GS_ARG_ENCODING_UTF8,
+ GS_ARG_ENCODING_UTF16LE,
+ )
e = _libgs.gsapi_set_arg_encoding(instance, encoding)
+ if not e:
+ if encoding == GS_ARG_ENCODING_LOCAL:
+ # This is probably wrong on Windows.
+ _encoding = 'utf-8'
+ elif encoding == GS_ARG_ENCODING_UTF8:
+ _encoding = 'utf-8'
+ elif encoding == GS_ARG_ENCODING_UTF16LE:
+ _encoding = 'utf-16-le'
return e
def gsapi_init_with_args(instance, args):
+ # [unicode: we assume that underlying gsapi_init_with_args()
+ # expects strings in args[] to be encoded in encoding set by
+ # gsapi_set_arg_encoding().]
+
# Create copy of args in format expected by C.
argc = len(args)
argv = (_pchar * (argc + 1))()
for i, arg in enumerate(args):
- enc_arg = arg.encode('utf-8')
+ enc_arg = arg.encode(_encoding)
argv[i] = ctypes.create_string_buffer(enc_arg)
argv[argc] = None
@@ -220,8 +273,14 @@ def gsapi_run_string_begin(instance, user_errors):
def gsapi_run_string_continue(instance, str_, user_errors):
'''
+ <str_> should be either a python string or a bytes object. If the former,
+ it is converted into a bytes object using utf-8 encoding.
+
Returns (e, exit_code).
'''
+ if isinstance(str_, str):
+ str_ = str_.encode('utf-8')
+ assert isinstance(str_, bytes)
pexit_code = ctypes.c_int()
e = _libgs.gsapi_run_string_continue(
instance,
@@ -238,31 +297,43 @@ def gsapi_run_string_end(instance, user_errors):
Returns (e, exit_code).
'''
pexit_code = ctypes.c_int()
- e = _libgs.gsapi_run_string_end(instance, user_errors, ctypes.byref(pexit_code))
+ e = _libgs.gsapi_run_string_end(
+ instance,
+ user_errors,
+ ctypes.byref(pexit_code),
+ )
return e, pexit_code.value
def gsapi_run_string_with_length(instance, str_, length, user_errors):
'''
+ <str_> should be either a python string or a bytes object. If the former,
+ it is converted into a bytes object using utf-8 encoding.
+
Returns (e, exit_code).
'''
- pexit_code = ctypes.c_int()
- e = _libgs.gsapi_run_string_with_length(
- instance,
- str_,
- length,
- user_errors,
- ctypes.byref(pexit_code),
- )
- return e, pexit_code.value
+ return gsapi_run_string(instance, str_[:length], user_errors)
def gsapi_run_string(instance, str_, user_errors):
'''
+ <str_> should be either a python string or a bytes object. If the former,
+ it is converted into a bytes object using utf-8 encoding.
+
Returns (e, exit_code).
'''
+ if isinstance(str_, str):
+ str_ = str_.encode('utf-8')
+ assert isinstance(str_, bytes)
pexit_code = ctypes.c_int()
- e = _libgs.gsapi_run_string(instance, str_, user_errors, ctypes.byref(pexit_code))
+ # We use gsapi_run_string_with_length() because str_ might contain zeros.
+ e = _libgs.gsapi_run_string_with_length(
+ instance,
+ str_,
+ len(str_),
+ user_errors,
+ ctypes.byref(pexit_code),
+ )
return e, pexit_code.value
@@ -270,8 +341,11 @@ def gsapi_run_file(instance, filename, user_errors):
'''
Returns (e, exit_code).
'''
+ # [unicode: we assume that underlying gsapi_run_file() expects <filename>
+ # to be encoded in encoding set by gsapi_set_arg_encoding().]
pexit_code = ctypes.c_int()
- e = _libgs.gsapi_run_file(instance, filename, user_errors, ctypes.byref(pexit_code))
+ filename2 = filename.encode(_encoding)
+ e = _libgs.gsapi_run_file(instance, filename2, user_errors, ctypes.byref(pexit_code))
return e, pexit_code.value
@@ -293,6 +367,8 @@ gs_spt_size_t = 8 # void * is a size_t *.
def gsapi_set_param(instance, param, value):
+ # [unicode: we assume that underlying gsapi_set_param() expects <param> and
+ # string <value> to be encoded as latin-1.]
param2 = param.encode('latin-1')
if 0: pass
elif isinstance(value, bool):
@@ -321,12 +397,18 @@ GS_PERMIT_FILE_CONTROL = 2
def gsapi_add_control_path(instance, type_, path):
- e = _libgs.gsapi_add_control_path(instance, type_, path)
+ # [unicode: we assume that underlying gsapi_add_control_path() expects
+ # <path> to be encoded in encoding set by gsapi_set_arg_encoding().]
+ path2 = path.encode(_encoding)
+ e = _libgs.gsapi_add_control_path(instance, type_, path2)
return e
def gsapi_remove_control_path(instance, type_, path):
- e = _libgs.gsapi_remove_control_path(instance, type_, path)
+ # [unicode: we assume that underlying gsapi_remove_control_path() expects
+ # <path> to be encoded in encoding set by gsapi_set_arg_encoding().]
+ path2 = path.encode(_encoding)
+ e = _libgs.gsapi_remove_control_path(instance, type_, path2)
return e
@@ -352,6 +434,12 @@ def gsapi_is_path_control_active(instance):
_libgs = ctypes.CDLL('libgs.so')
+# The encoding that we use when passing strings to the underlying gsapi_*() C
+# functions. Changed by gsapi_set_arg_encoding().
+#
+# This default is probably incorrect on Windows.
+#
+_encoding = 'utf-8'
class _gsapi_revision_t(ctypes.Structure):
_fields_ = [
@@ -529,7 +617,7 @@ if __name__ == '__main__':
assert not e
- e, instance = gsapi_new_instance(0)
+ e, instance = gsapi_new_instance(1)
print('gsapi_new_instance => e=%s: %s' % (e, instance))
assert not e
@@ -537,9 +625,8 @@ if __name__ == '__main__':
print('gsapi_set_arg_encoding => e=%s' % e)
assert not e
- def stdout_fn(caller_handle, str_, len_):
- sys.stdout.write(str_[:len_].decode('latin-1').replace('\n', '\n*** '))
- return len_
+ def stdout_fn(caller_handle, bytes_):
+ sys.stdout.write(bytes_.decode('latin-1'))
e = gsapi_set_stdio(instance, None, stdout_fn, None)
print('gsapi_set_stdio => e=%s' % e)
assert not e