Compare commits
21 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 6744e0525a | |||
| f1879726d9 | |||
| 56f3b54f62 | |||
| 11fadb16fd | |||
| 2340833bb8 | |||
| 7ce4cb5050 | |||
| 78e5dd1f4e | |||
| 857f68ef6b | |||
| 14c3cb49b9 | |||
| a6d32ecddb | |||
| e5e02401da | |||
| ea3eac3c40 | |||
| 28a8112c6d | |||
| 45384106c8 | |||
| 81729f57cc | |||
| 7ecfcad239 | |||
| b2929a9e92 | |||
| 6d33c7ff1e | |||
| 5687fbc8b2 | |||
| 9e356b1b0a | |||
| f7b94b3e57 |
@@ -4,6 +4,11 @@ Release History
|
|||||||
master
|
master
|
||||||
------
|
------
|
||||||
|
|
||||||
|
**Bugfixes**
|
||||||
|
|
||||||
|
* Fix library discovery on Windows. [#32](https://github.com/camelot-dev/camelot/pull/32) by [KOLANICH](https://github.com/KOLANICH).
|
||||||
|
* Fix calling convention of callback functions. [#34](https://github.com/camelot-dev/camelot/pull/34) by [KOLANICH](https://github.com/KOLANICH).
|
||||||
|
|
||||||
0.7.3 (2019-07-07)
|
0.7.3 (2019-07-07)
|
||||||
------------------
|
------------------
|
||||||
|
|
||||||
|
|||||||
@@ -71,7 +71,7 @@ $ conda install -c conda-forge camelot-py
|
|||||||
|
|
||||||
### Using pip
|
### Using pip
|
||||||
|
|
||||||
After [installing the dependencies](https://camelot-py.readthedocs.io/en/master/user/install-deps.html) ([tk](https://packages.ubuntu.com/trusty/python-tk) and [ghostscript](https://www.ghostscript.com/)), you can simply use pip to install Camelot:
|
After [installing the dependencies](https://camelot-py.readthedocs.io/en/master/user/install-deps.html) ([tk](https://packages.ubuntu.com/bionic/python/python-tk) and [ghostscript](https://www.ghostscript.com/)), you can simply use pip to install Camelot:
|
||||||
|
|
||||||
<pre>
|
<pre>
|
||||||
$ pip install camelot-py[cv]
|
$ pip install camelot-py[cv]
|
||||||
@@ -132,4 +132,8 @@ Camelot uses [Semantic Versioning](https://semver.org/). For the available versi
|
|||||||
|
|
||||||
This project is licensed under the MIT License, see the [LICENSE](https://github.com/camelot-dev/camelot/blob/master/LICENSE) file for details.
|
This project is licensed under the MIT License, see the [LICENSE](https://github.com/camelot-dev/camelot/blob/master/LICENSE) file for details.
|
||||||
|
|
||||||
<img src="http://i65.tinypic.com/9h4ajs.png" align="centre" />
|
## Support the development
|
||||||
|
|
||||||
|
You can support our work on Camelot with a one-time or monthly donation [on OpenCollective](https://opencollective.com/camelot). Organizations who use camelot can also sponsor the project for an acknowledgement on [our documentation site](https://camelot-py.readthedocs.io/en/master/) and this README.
|
||||||
|
|
||||||
|
Special thanks to all the users, organizations and contributors that support Camelot!
|
||||||
|
|||||||
@@ -81,7 +81,9 @@ def delete_instance(instance):
|
|||||||
"""
|
"""
|
||||||
return libgs.gsapi_delete_instance(instance)
|
return libgs.gsapi_delete_instance(instance)
|
||||||
|
|
||||||
|
if sys.platform == "win32":
|
||||||
|
c_stdstream_call_t = WINFUNCTYPE(c_int, gs_main_instance, POINTER(c_char), c_int)
|
||||||
|
else:
|
||||||
c_stdstream_call_t = CFUNCTYPE(c_int, gs_main_instance, POINTER(c_char), c_int)
|
c_stdstream_call_t = CFUNCTYPE(c_int, gs_main_instance, POINTER(c_char), c_int)
|
||||||
|
|
||||||
|
|
||||||
@@ -243,6 +245,9 @@ def __win32_finddll():
|
|||||||
|
|
||||||
if sys.platform == "win32":
|
if sys.platform == "win32":
|
||||||
libgs = __win32_finddll()
|
libgs = __win32_finddll()
|
||||||
|
if not libgs:
|
||||||
|
import ctypes.util
|
||||||
|
libgs = ctypes.util.find_library("".join(("gsdll", str(ctypes.sizeof(ctypes.c_voidp) * 8), ".dll"))) # finds in %PATH%
|
||||||
if not libgs:
|
if not libgs:
|
||||||
raise RuntimeError("Please make sure that Ghostscript is installed")
|
raise RuntimeError("Please make sure that Ghostscript is installed")
|
||||||
libgs = windll.LoadLibrary(libgs)
|
libgs = windll.LoadLibrary(libgs)
|
||||||
|
|||||||
+22
-17
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
|
import copy
|
||||||
|
|
||||||
from PyPDF2 import PdfFileReader, PdfFileWriter
|
from PyPDF2 import PdfFileReader, PdfFileWriter
|
||||||
|
|
||||||
@@ -89,50 +90,55 @@ class PDFHandler(object):
|
|||||||
P.extend(range(p["start"], p["end"] + 1))
|
P.extend(range(p["start"], p["end"] + 1))
|
||||||
return sorted(set(P))
|
return sorted(set(P))
|
||||||
|
|
||||||
def _save_page(self, filepath, page, temp):
|
def _save_pages(self, filepath, pages, temp):
|
||||||
"""Saves specified page from PDF into a temporary directory.
|
"""Saves specified page from PDF into a temporary directory.
|
||||||
|
|
||||||
Parameters
|
Parameters
|
||||||
----------
|
----------
|
||||||
filepath : str
|
filepath : str
|
||||||
Filepath or URL of the PDF file.
|
Filepath or URL of the PDF file.
|
||||||
page : int
|
pages : int
|
||||||
Page number.
|
Page numbers.
|
||||||
temp : str
|
temp : str
|
||||||
Tmp directory.
|
Tmp directory.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
with open(filepath, "rb") as fileobj:
|
with open(filepath, "rb") as fileobj:
|
||||||
infile = PdfFileReader(fileobj, strict=False)
|
infile_original = PdfFileReader(fileobj, strict=False)
|
||||||
if infile.isEncrypted:
|
if infile_original.isEncrypted:
|
||||||
infile.decrypt(self.password)
|
infile_original.decrypt(self.password)
|
||||||
fpath = os.path.join(temp, "page-{0}.pdf".format(page))
|
|
||||||
|
for page in pages:
|
||||||
|
# Ensure PdfFileReader object is unmodified
|
||||||
|
infile = copy.copy(infile_original)
|
||||||
|
fpath = os.path.join(temp, 'page-{0}.pdf'.format(page))
|
||||||
froot, fext = os.path.splitext(fpath)
|
froot, fext = os.path.splitext(fpath)
|
||||||
p = infile.getPage(page - 1)
|
p = infile.getPage(page - 1)
|
||||||
outfile = PdfFileWriter()
|
outfile = PdfFileWriter()
|
||||||
outfile.addPage(p)
|
outfile.addPage(p)
|
||||||
with open(fpath, "wb") as f:
|
with open(fpath, 'wb') as f:
|
||||||
outfile.write(f)
|
outfile.write(f)
|
||||||
|
|
||||||
|
# Orient rotated pages correctly
|
||||||
layout, dim = get_page_layout(fpath)
|
layout, dim = get_page_layout(fpath)
|
||||||
# fix rotated PDF
|
|
||||||
chars = get_text_objects(layout, ltype="char")
|
chars = get_text_objects(layout, ltype="char")
|
||||||
horizontal_text = get_text_objects(layout, ltype="horizontal_text")
|
horizontal_text = get_text_objects(layout, ltype="horizontal_text")
|
||||||
vertical_text = get_text_objects(layout, ltype="vertical_text")
|
vertical_text = get_text_objects(layout, ltype="vertical_text")
|
||||||
rotation = get_rotation(chars, horizontal_text, vertical_text)
|
rotation = get_rotation(chars, horizontal_text, vertical_text)
|
||||||
if rotation != "":
|
if rotation != '':
|
||||||
fpath_new = "".join([froot.replace("page", "p"), "_rotated", fext])
|
fpath_new = ''.join([froot.replace('page', 'p'), '_rotated', fext])
|
||||||
os.rename(fpath, fpath_new)
|
os.rename(fpath, fpath_new)
|
||||||
infile = PdfFileReader(open(fpath_new, "rb"), strict=False)
|
infile = PdfFileReader(open(fpath_new, 'rb'), strict=False)
|
||||||
if infile.isEncrypted:
|
if infile.isEncrypted:
|
||||||
infile.decrypt(self.password)
|
infile.decrypt(self.password)
|
||||||
outfile = PdfFileWriter()
|
outfile = PdfFileWriter()
|
||||||
p = infile.getPage(0)
|
p = infile.getPage(0)
|
||||||
if rotation == "anticlockwise":
|
if rotation == 'anticlockwise':
|
||||||
p.rotateClockwise(90)
|
p.rotateClockwise(90)
|
||||||
elif rotation == "clockwise":
|
elif rotation == 'clockwise':
|
||||||
p.rotateCounterClockwise(90)
|
p.rotateCounterClockwise(90)
|
||||||
outfile.addPage(p)
|
outfile.addPage(p)
|
||||||
with open(fpath, "wb") as f:
|
with open(fpath, 'wb') as f:
|
||||||
outfile.write(f)
|
outfile.write(f)
|
||||||
|
|
||||||
def parse(
|
def parse(
|
||||||
@@ -161,8 +167,7 @@ class PDFHandler(object):
|
|||||||
"""
|
"""
|
||||||
tables = []
|
tables = []
|
||||||
with TemporaryDirectory() as tempdir:
|
with TemporaryDirectory() as tempdir:
|
||||||
for p in self.pages:
|
self._save_pages(self.filepath, self.pages, tempdir)
|
||||||
self._save_page(self.filepath, p, tempdir)
|
|
||||||
pages = [
|
pages = [
|
||||||
os.path.join(tempdir, "page-{0}.pdf".format(p)) for p in self.pages
|
os.path.join(tempdir, "page-{0}.pdf".format(p)) for p in self.pages
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -30,6 +30,9 @@ Release v\ |version|. (:ref:`Installation <install>`)
|
|||||||
.. image:: https://badges.gitter.im/camelot-dev/Lobby.png
|
.. image:: https://badges.gitter.im/camelot-dev/Lobby.png
|
||||||
:target: https://gitter.im/camelot-dev/Lobby
|
:target: https://gitter.im/camelot-dev/Lobby
|
||||||
|
|
||||||
|
.. image:: https://img.shields.io/badge/code%20style-black-000000.svg
|
||||||
|
:target: https://github.com/ambv/black
|
||||||
|
|
||||||
**Camelot** is a Python library that makes it easy for *anyone* to extract tables from PDF files!
|
**Camelot** is a Python library that makes it easy for *anyone* to extract tables from PDF files!
|
||||||
|
|
||||||
.. note:: You can also check out `Excalibur`_, which is a web interface for Camelot!
|
.. note:: You can also check out `Excalibur`_, which is a web interface for Camelot!
|
||||||
@@ -83,6 +86,13 @@ See `comparison with other PDF table extraction libraries and tools`_.
|
|||||||
.. _ETL and data analysis workflows: https://gist.github.com/vinayak-mehta/e5949f7c2410a0e12f25d3682dc9e873
|
.. _ETL and data analysis workflows: https://gist.github.com/vinayak-mehta/e5949f7c2410a0e12f25d3682dc9e873
|
||||||
.. _comparison with other PDF table extraction libraries and tools: https://github.com/camelot-dev/camelot/wiki/Comparison-with-other-PDF-Table-Extraction-libraries-and-tools
|
.. _comparison with other PDF table extraction libraries and tools: https://github.com/camelot-dev/camelot/wiki/Comparison-with-other-PDF-Table-Extraction-libraries-and-tools
|
||||||
|
|
||||||
|
Support us on OpenCollective
|
||||||
|
----------------------------
|
||||||
|
|
||||||
|
If Camelot helped you extract tables from PDFs, please consider supporting its development by `becoming a backer or a sponsor on OpenCollective`_!
|
||||||
|
|
||||||
|
.. _becoming a backer or a sponsor on OpenCollective: https://opencollective.com/camelot
|
||||||
|
|
||||||
The User Guide
|
The User Guide
|
||||||
--------------
|
--------------
|
||||||
|
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ The easiest way to install Camelot is to install it with `conda`_, which is a pa
|
|||||||
|
|
||||||
$ conda install -c conda-forge camelot-py
|
$ conda install -c conda-forge camelot-py
|
||||||
|
|
||||||
.. note:: Camelot is available for Python 2.7, 3.5 and 3.6 on Linux, macOS and Windows. For Windows, you will need to install ghostscript which you can get from their `downloads page`_.
|
.. note:: Camelot is available for Python 2.7, 3.5, 3.6 and 3.7 on Linux, macOS and Windows. For Windows, you will need to install ghostscript which you can get from their `downloads page`_.
|
||||||
|
|
||||||
.. _conda: https://conda.io/docs/
|
.. _conda: https://conda.io/docs/
|
||||||
.. _Anaconda: http://docs.continuum.io/anaconda/
|
.. _Anaconda: http://docs.continuum.io/anaconda/
|
||||||
|
|||||||
Reference in New Issue
Block a user