Remove _errors and ghostscript test

pull/2/head
Vinayak Mehta 2018-12-18 07:43:52 +05:30
parent 9879a87c6f
commit 4938c48853
5 changed files with 48 additions and 257 deletions

View File

@ -4,6 +4,7 @@
ghostscript - A Python interface for the Ghostscript interpreter C-API ghostscript - A Python interface for the Ghostscript interpreter C-API
""" """
# #
# Modifications 2018 by Vinayak Mehta <vmehta94@gmail.com>
# Copyright 2010-2018 by Hartmut Goebel <h.goebel@crazy-compilers.com> # Copyright 2010-2018 by Hartmut Goebel <h.goebel@crazy-compilers.com>
# #
# This program is free software: you can redistribute it and/or modify # This program is free software: you can redistribute it and/or modify
@ -20,60 +21,22 @@ ghostscript - A Python interface for the Ghostscript interpreter C-API
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
# #
from __future__ import absolute_import
__author__ = "Hartmut Goebel <h.goebel@crazy-compilers.com>"
__copyright__ = "Copyright 2010-2018 by Hartmut Goebel <h.goebel@crazy-compilers.com>"
__licence__ = "GNU General Public License version 3 (GPL v3)"
__version__ = '0.6'
__all__ = ['Ghostscript', 'revision',
'GhostscriptError', 'PleaseDisplayUsage']
import atexit
from . import _gsprint as gs from . import _gsprint as gs
GhostscriptError = gs.GhostscriptError __author__ = 'Hartmut Goebel <h.goebel@crazy-compilers.com>'
__copyright__ = 'Copyright 2010-2018 by Hartmut Goebel <h.goebel@crazy-compilers.com>'
__license__ = 'GNU General Public License version 3 (GPL v3)'
__version__ = '0.6'
def PleaseDisplayUsage(Warning): class __Ghostscript(object):
"""
This exception is raised when Ghostscript asks the application to
display the usage. The application should catch the exception an
print the usage message.
"""
pass
def revision():
"""
This function returns the revision numbers and strings of the
Ghostscript interpreter library as a dict. You should call it
before any other interpreter library functions to make sure that
the correct version of the Ghostscript interpreter has been
loaded.
"""
rev = gs.revision()
return dict((f, getattr(rev, f)) for f, _ in rev._fields_)
MAX_STRING_LENGTH = gs.MAX_STRING_LENGTH
class Ghostscript(object):
@staticmethod
def revision():
return revision()
def __init__(self, instance, args, stdin=None, stdout=None, stderr=None): def __init__(self, instance, args, stdin=None, stdout=None, stderr=None):
self._initialized = False self._initialized = False
self._callbacks = None self._callbacks = None
if stdin or stdout or stderr: if stdin or stdout or stderr:
self.set_stdio(stdin, stdout, stderr) self.set_stdio(stdin, stdout, stderr)
rc = gs.init_with_args(instance, args) rc = gs.init_with_args(instance, args)
if rc == gs.e_Info:
raise PleaseDisplayUsage
self._initialized = True self._initialized = True
if rc == gs.e_Quit: if rc == gs.e_Quit:
self.exit() self.exit()
@ -117,15 +80,10 @@ class Ghostscript(object):
self._initialized = False self._initialized = False
__Ghostscript = Ghostscript
__instance__ = None
def Ghostscript(*args, **kwargs): def Ghostscript(*args, **kwargs):
"""Factory function for setting up a Ghostscript instance
""" """
Factory function for setting up a Ghostscript instance global __instance__
"""
global __instance__, __object_count__
# Ghostscript only supports a single instance # Ghostscript only supports a single instance
if __instance__ is None: if __instance__ is None:
__instance__ = gs.new_instance() __instance__ = gs.new_instance()
@ -133,3 +91,6 @@ def Ghostscript(*args, **kwargs):
stdin=kwargs.get('stdin', None), stdin=kwargs.get('stdin', None),
stdout=kwargs.get('stdout', None), stdout=kwargs.get('stdout', None),
stderr=kwargs.get('stderr', None)) stderr=kwargs.get('stderr', None))
__instance__ = None

View File

@ -1,123 +0,0 @@
"""
Definition of Ghostscript error codes
"""
#
# Copyright (C) 2010-2018 by Hartmut Goebel
#
# Based on iapi.h which is
# Copyright (C) 1989, 1995, 1998, 1999 Aladdin Enterprises. All rights reserved.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
__author__ = "Hartmut Goebel <h.goebel@crazy-compilers.com>"
__copyright__ = "Copyright 2010-2018 by Hartmut Goebel <h.goebel@crazy-compilers.com>"
__licence__ = "GNU General Public License version 3 (GPL v3)"
__version__ = "0.6"
LEVEL1_ERROR_NAMES = ["unknownerror", "dictfull", "dictstackoverflow",
"dictstackunderflow", "execstackoverflow",
"interrupt", "invalidaccess", "invalidexit",
"invalidfileaccess", "invalidfont",
"invalidrestore", "ioerror", "limitcheck",
"nocurrentpoint", "rangecheck", "stackoverflow",
"stackunderflow", "syntaxerror", "timeout",
"typecheck", "undefined", "undefinedfilename",
"undefinedresult", "unmatchedmark", "VMerror"]
LEVEL2_ERROR_NAMES = ["configurationerror", "invalidcontext",
"undefinedresource", "unregistered",
"invalidid"]
ERROR_NAMES = LEVEL1_ERROR_NAMES + LEVEL2_ERROR_NAMES
PSEUDO_ERROR_NAMES = ['Fatal', 'Quit', 'InterpreterExit', 'RemapColor',
'ExecStackUnderflow', 'VMreclaim', 'NeedInput',
'NeedStdin', 'NeedStdout', 'NeedStderr', 'Info']
def error2name(ecode):
if ecode <= e_Fatal:
return PSEUDO_ERROR_NAMES[-ecode-100]
else:
return ERROR_NAMES[-ecode-1]
#
# Internal code for a fatal error.
# gs_interpret also returns this for a .quit with a positive exit code.
#
e_Fatal = -100
#
# Internal code for the .quit operator.
# The real quit code is an integer on the operand stack.
# gs_interpret returns this only for a .quit with a zero exit code.
#
e_Quit = -101
#
# Internal code for a normal exit from the interpreter.
# Do not use outside of interp.c.
#
e_InterpreterExit = -102
#
# Internal code that indicates that a procedure has been stored in the
# remap_proc of the graphics state, and should be called before retrying
# the current token. This is used for color remapping involving a call
# back into the interpreter -- inelegant, but effective.
#
e_RemapColor = -103
#
# Internal code to indicate we have underflowed the top block
# of the e-stack.
#
e_ExecStackUnderflow = -104
#
# Internal code for the vmreclaim operator with a positive operand.
# We need to handle this as an error because otherwise the interpreter
# won't reload enough of its state when the operator returns.
#
e_VMreclaim = -105
#
# Internal code for requesting more input from run_string.
#
e_NeedInput = -106
#
# Internal code for stdin callout.
#
e_NeedStdin = -107
#
# Internal code for stdout callout.
#
e_NeedStdout = -108
#
# Internal code for stderr callout.
#
e_NeedStderr = -109
#
# Internal code for a normal exit when usage info is displayed.
# This allows Window versions of Ghostscript to pause until
# the message can be read.
#
e_Info = -110

View File

@ -4,6 +4,7 @@
ghostscript._gsprint - A low-level interface to the Ghostscript C-API using ctypes ghostscript._gsprint - A low-level interface to the Ghostscript C-API using ctypes
""" """
# #
# Modifications 2018 by Vinayak Mehta <vmehta94@gmail.com>
# Copyright 2010-2018 by Hartmut Goebel <h.goebel@crazy-compilers.com> # Copyright 2010-2018 by Hartmut Goebel <h.goebel@crazy-compilers.com>
# #
# Display_callback Structure by Lasse Fister <commander@graphicore.de> in 2013 # Display_callback Structure by Lasse Fister <commander@graphicore.de> in 2013
@ -22,62 +23,38 @@ ghostscript._gsprint - A low-level interface to the Ghostscript C-API using ctyp
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
# #
from __future__ import absolute_import
__author__ = "Hartmut Goebel <h.goebel@crazy-compilers.com>"
__copyright__ = "Copyright 2010-2018 by Hartmut Goebel <h.goebel@crazy-compilers.com>"
__licence__ = "GNU General Public License version 3 (GPL v3)"
__version__ = "0.6"
import sys import sys
from ctypes import * from ctypes import *
from ._errors import e_Quit, e_Info
# base/gserrors.h
#
# Internal code for a normal exit when usage info is displayed.
# This allows Window versions of Ghostscript to pause until
# the message can be read.
#
e_Info = -110
MAX_STRING_LENGTH = 65535 #
# Internal code for the .quit operator.
# The real quit code is an integer on the operand stack.
# gs_interpret returns this only for a .quit with a zero exit code.
#
e_Quit = -101
__author__ = 'Hartmut Goebel <h.goebel@crazy-compilers.com>'
__copyright__ = 'Copyright 2010-2018 by Hartmut Goebel <h.goebel@crazy-compilers.com>'
__license__ = 'GNU General Public License version 3 (GPL v3)'
__version__ = '0.6'
gs_main_instance = c_void_p gs_main_instance = c_void_p
display_callback = c_void_p display_callback = c_void_p
# https://www.ghostscript.com/doc/current/API.htm
class Revision(Structure):
_fields_ = [
("product", c_char_p),
("copyright", c_char_p),
("revision", c_long),
("revisiondate", c_long)
]
class GhostscriptError(Exception): def new_instance():
def __init__(self, ecode): """Create a new instance of Ghostscript
# :todo:
Exception.__init__(self, error2name(ecode))
self.code = ecode
def revision():
"""
Get version numbers and strings.
This is safe to call at any time.
You should call this first to make sure that the correct version
of the Ghostscript is being used.
Returns a Revision instance
"""
revision = Revision()
rc = libgs.gsapi_revision(pointer(revision), sizeof(revision))
if rc:
raise ArgumentError("Revision structure size is incorrect, "
"requires %s bytes" % rc)
return revision
def new_instance(): # display_callback=None):
"""
Create a new instance of Ghostscript
This instance is passed to most other API functions. This instance is passed to most other API functions.
""" """
@ -91,8 +68,7 @@ def new_instance(): # display_callback=None):
def delete_instance(instance): def delete_instance(instance):
""" """Destroy an instance of Ghostscript
Destroy an instance of Ghostscript
Before you call this, Ghostscript must have finished. Before you call this, Ghostscript must have finished.
If Ghostscript has been initialised, you must call exit() If Ghostscript has been initialised, you must call exit()
@ -105,11 +81,9 @@ c_stdstream_call_t = CFUNCTYPE(c_int, gs_main_instance, POINTER(c_char), c_int)
def _wrap_stdin(infp): def _wrap_stdin(infp):
""" """Wrap a filehandle into a C function to be used as `stdin` callback
Wrap a filehandle into a C function to be used as `stdin` callback
for ``set_stdio``. The filehandle has to support the readline() method. for ``set_stdio``. The filehandle has to support the readline() method.
""" """
def _wrap(instance, dest, count): def _wrap(instance, dest, count):
try: try:
data = infp.readline(count) data = infp.readline(count)
@ -127,12 +101,10 @@ def _wrap_stdin(infp):
def _wrap_stdout(outfp): def _wrap_stdout(outfp):
""" """Wrap a filehandle into a C function to be used as `stdout` or
Wrap a filehandle into a C function to be used as `stdout` or
`stderr` callback for ``set_stdio``. The filehandle has to support the `stderr` callback for ``set_stdio``. The filehandle has to support the
write() and flush() methods. write() and flush() methods.
""" """
def _wrap(instance, str, count): def _wrap(instance, str, count):
outfp.write(str[:count]) outfp.write(str[:count])
outfp.flush() outfp.flush()
@ -145,8 +117,7 @@ _wrap_stderr = _wrap_stdout
def set_stdio(instance, stdin, stdout, stderr): def set_stdio(instance, stdin, stdout, stderr):
""" """Set the callback functions for stdio.
Set the callback functions for stdio.
``stdin``, ``stdout`` and ``stderr`` have to be ``ctypes`` ``stdin``, ``stdout`` and ``stderr`` have to be ``ctypes``
callback functions matching the ``_gsprint.c_stdstream_call_t`` callback functions matching the ``_gsprint.c_stdstream_call_t``
@ -173,13 +144,8 @@ def set_stdio(instance, stdin, stdout, stderr):
return rc return rc
# :todo: set_poll (instance, int(*poll_fn)(void *caller_handle));
# :todo: set_display_callback(instance, callback):
def init_with_args(instance, argv): def init_with_args(instance, argv):
""" """Initialise the interpreter
Initialise the interpreter.
1. If quit or EOF occur during init_with_args(), the return value 1. If quit or EOF occur during init_with_args(), the return value
will be e_Quit. This is not an error. You must call exit() and will be e_Quit. This is not an error. You must call exit() and
@ -200,8 +166,7 @@ def init_with_args(instance, argv):
def exit(instance): def exit(instance):
""" """Exit the interpreter
Exit the interpreter
This must be called on shutdown if init_with_args() has been This must be called on shutdown if init_with_args() has been
called, and just before delete_instance() called, and just before delete_instance()
@ -230,7 +195,7 @@ def __win32_finddll():
# Look up different variants of Ghostscript and take the highest # Look up different variants of Ghostscript and take the highest
# version for which the DLL is to be found in the filesystem. # version for which the DLL is to be found in the filesystem.
for key_name in ('AFPL Ghostscript', 'Aladdin Ghostscript', for key_name in ('AFPL Ghostscript', 'Aladdin Ghostscript',
'GPL Ghostscript', 'GNU Ghostscript'): 'GNU Ghostscript', 'GPL Ghostscript'):
try: try:
k1 = OpenKey(HKEY_LOCAL_MACHINE, "Software\\%s" % key_name) k1 = OpenKey(HKEY_LOCAL_MACHINE, "Software\\%s" % key_name)
for num in range(0, QueryInfoKey(k1)[0]): for num in range(0, QueryInfoKey(k1)[0]):
@ -256,17 +221,18 @@ def __win32_finddll():
if sys.platform == 'win32': if sys.platform == 'win32':
libgs = __win32_finddll() libgs = __win32_finddll()
if not libgs: if not libgs:
raise RuntimeError('Can not find Ghostscript DLL in registry') raise RuntimeError('Please make sure that Ghostscript is installed')
libgs = windll.LoadLibrary(libgs) libgs = windll.LoadLibrary(libgs)
else: else:
try: try:
libgs = cdll.LoadLibrary("libgs.so") libgs = cdll.LoadLibrary('libgs.so')
except OSError: except OSError:
# shared object file not found # shared object file not found
import ctypes.util import ctypes.util
libgs = ctypes.util.find_library('gs') libgs = ctypes.util.find_library('gs')
if not libgs: if not libgs:
raise RuntimeError('Can not find Ghostscript library (libgs)') raise RuntimeError('Please make sure that Ghostscript is installed')
libgs = cdll.LoadLibrary(libgs) libgs = cdll.LoadLibrary(libgs)
del __win32_finddll del __win32_finddll

View File

@ -181,8 +181,10 @@ class Lattice(BaseParser):
gs_call = '-q -sDEVICE=png16m -o {} -r600 {}'.format( gs_call = '-q -sDEVICE=png16m -o {} -r600 {}'.format(
self.imagename, self.filename) self.imagename, self.filename)
gs_call = gs_call.encode().split() gs_call = gs_call.encode().split()
with Ghostscript(*gs_call) as gs: null = open(os.devnull, 'wb')
with Ghostscript(*gs_call, stdout=null) as gs:
pass pass
null.close()
def _generate_table_bbox(self): def _generate_table_bbox(self):
self.image, self.threshold = adaptive_threshold( self.image, self.threshold = adaptive_threshold(

View File

@ -74,21 +74,6 @@ def test_no_tables_found_warnings_suppressed():
pytest.fail('Unexpected warning: {}'.format(warning_text)) pytest.fail('Unexpected warning: {}'.format(warning_text))
def test_ghostscript_not_found(monkeypatch):
import distutils
def _find_executable_patch(arg):
return ''
monkeypatch.setattr(distutils.spawn, 'find_executable', _find_executable_patch)
message = ('Please make sure that Ghostscript is installed and available'
' on the PATH environment variable')
filename = os.path.join(testdir, 'foo.pdf')
with pytest.raises(Exception, message=message):
tables = camelot.read_pdf(filename)
def test_no_password(): def test_no_password():
filename = os.path.join(testdir, 'health_protected.pdf') filename = os.path.join(testdir, 'health_protected.pdf')
message = 'file has not been decrypted' message = 'file has not been decrypted'