Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 6744e0525a |
@@ -1,12 +0,0 @@
|
|||||||
version = 1
|
|
||||||
|
|
||||||
exclude_patterns = [
|
|
||||||
"camelot/ext/**"
|
|
||||||
]
|
|
||||||
|
|
||||||
[[analyzers]]
|
|
||||||
name = "python"
|
|
||||||
enabled = true
|
|
||||||
|
|
||||||
[analyzers.meta]
|
|
||||||
runtime_version = "3.x.x"
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
open_collective: camelot
|
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
# .readthedocs.yml
|
|
||||||
# Read the Docs configuration file
|
|
||||||
# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details
|
|
||||||
|
|
||||||
# Required
|
|
||||||
version: 2
|
|
||||||
|
|
||||||
# Build documentation in the docs/ directory with Sphinx
|
|
||||||
sphinx:
|
|
||||||
configuration: docs/conf.py
|
|
||||||
|
|
||||||
# Build documentation with MkDocs
|
|
||||||
#mkdocs:
|
|
||||||
# configuration: mkdocs.yml
|
|
||||||
|
|
||||||
# Optionally build your docs in additional formats such as PDF
|
|
||||||
formats:
|
|
||||||
- pdf
|
|
||||||
|
|
||||||
# Optionally set the version of Python and requirements required to build your docs
|
|
||||||
python:
|
|
||||||
version: 3.8
|
|
||||||
install:
|
|
||||||
- requirements: requirements.txt
|
|
||||||
@@ -8,6 +8,14 @@ install:
|
|||||||
- make install
|
- make install
|
||||||
jobs:
|
jobs:
|
||||||
include:
|
include:
|
||||||
|
- stage: test
|
||||||
|
script:
|
||||||
|
- make test
|
||||||
|
python: '2.7'
|
||||||
|
- stage: test
|
||||||
|
script:
|
||||||
|
- make test
|
||||||
|
python: '3.5'
|
||||||
- stage: test
|
- stage: test
|
||||||
script:
|
script:
|
||||||
- make test
|
- make test
|
||||||
@@ -17,13 +25,8 @@ jobs:
|
|||||||
- make test
|
- make test
|
||||||
python: '3.7'
|
python: '3.7'
|
||||||
dist: xenial
|
dist: xenial
|
||||||
- stage: test
|
|
||||||
script:
|
|
||||||
- make test
|
|
||||||
python: '3.8'
|
|
||||||
dist: xenial
|
|
||||||
- stage: coverage
|
- stage: coverage
|
||||||
python: '3.8'
|
python: '3.6'
|
||||||
script:
|
script:
|
||||||
- make test
|
- make test
|
||||||
- codecov --verbose
|
- codecov --verbose
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ A great way to start contributing to Camelot is to pick an issue tagged with the
|
|||||||
To install the dependencies needed for development, you can use pip:
|
To install the dependencies needed for development, you can use pip:
|
||||||
|
|
||||||
<pre>
|
<pre>
|
||||||
$ pip install "camelot-py[dev]"
|
$ pip install camelot-py[dev]
|
||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
Alternatively, you can clone the project repository, and install using pip:
|
Alternatively, you can clone the project repository, and install using pip:
|
||||||
|
|||||||
@@ -4,30 +4,6 @@ Release History
|
|||||||
master
|
master
|
||||||
------
|
------
|
||||||
|
|
||||||
0.8.2 (2020-07-27)
|
|
||||||
------------------
|
|
||||||
|
|
||||||
* Revert the changes in `0.8.1`.
|
|
||||||
|
|
||||||
0.8.1 (2020-07-21)
|
|
||||||
------------------
|
|
||||||
|
|
||||||
**Bugfixes**
|
|
||||||
|
|
||||||
* [#169](https://github.com/camelot-dev/camelot/issues/169) Fix import error caused by `pdfminer.six==20200720`. [#171](https://github.com/camelot-dev/camelot/pull/171) by Vinayak Mehta.
|
|
||||||
|
|
||||||
0.8.0 (2020-05-24)
|
|
||||||
------------------
|
|
||||||
|
|
||||||
**Improvements**
|
|
||||||
|
|
||||||
* Drop Python 2 support!
|
|
||||||
* Remove Python 2.7 and 3.5 support.
|
|
||||||
* Replace all instances of `.format` with f-strings.
|
|
||||||
* Remove all `__future__` imports.
|
|
||||||
* Fix HTTP 403 forbidden exception in read_pdf(url) and remove Python 2 urllib support.
|
|
||||||
* Fix test data.
|
|
||||||
|
|
||||||
**Bugfixes**
|
**Bugfixes**
|
||||||
|
|
||||||
* Fix library discovery on Windows. [#32](https://github.com/camelot-dev/camelot/pull/32) by [KOLANICH](https://github.com/KOLANICH).
|
* Fix library discovery on Windows. [#32](https://github.com/camelot-dev/camelot/pull/32) by [KOLANICH](https://github.com/KOLANICH).
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
[](https://travis-ci.org/camelot-dev/camelot) [](https://camelot-py.readthedocs.io/en/master/)
|
[](https://travis-ci.org/camelot-dev/camelot) [](https://camelot-py.readthedocs.io/en/master/)
|
||||||
[](https://codecov.io/github/camelot-dev/camelot?branch=master)
|
[](https://codecov.io/github/camelot-dev/camelot?branch=master)
|
||||||
[](https://pypi.org/project/camelot-py/) [](https://pypi.org/project/camelot-py/) [](https://pypi.org/project/camelot-py/) [](https://gitter.im/camelot-dev/Lobby)
|
[](https://pypi.org/project/camelot-py/) [](https://pypi.org/project/camelot-py/) [](https://pypi.org/project/camelot-py/) [](https://gitter.im/camelot-dev/Lobby)
|
||||||
[](https://github.com/ambv/black) [](https://deepsource.io/gh/camelot-dev/camelot/?ref=repository-badge)
|
[](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!
|
||||||
@@ -74,7 +74,7 @@ $ conda install -c conda-forge camelot-py
|
|||||||
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:
|
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]
|
||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
### From the source code
|
### From the source code
|
||||||
@@ -113,7 +113,7 @@ $ git clone https://www.github.com/camelot-dev/camelot
|
|||||||
You can install the development dependencies easily, using pip:
|
You can install the development dependencies easily, using pip:
|
||||||
|
|
||||||
<pre>
|
<pre>
|
||||||
$ pip install "camelot-py[dev]"
|
$ pip install camelot-py[dev]
|
||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
### Testing
|
### Testing
|
||||||
@@ -124,10 +124,6 @@ After installation, you can run tests using:
|
|||||||
$ python setup.py test
|
$ python setup.py test
|
||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
## Wrappers
|
|
||||||
|
|
||||||
- [camelot-php](https://github.com/randomstate/camelot-php) provides a [PHP](https://www.php.net/) wrapper on Camelot.
|
|
||||||
|
|
||||||
## Versioning
|
## Versioning
|
||||||
|
|
||||||
Camelot uses [Semantic Versioning](https://semver.org/). For the available versions, see the tags on this repository. For the changelog, you can check out [HISTORY.md](https://github.com/camelot-dev/camelot/blob/master/HISTORY.md).
|
Camelot uses [Semantic Versioning](https://semver.org/). For the available versions, see the tags on this repository. For the changelog, you can check out [HISTORY.md](https://github.com/camelot-dev/camelot/blob/master/HISTORY.md).
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
from __future__ import absolute_import
|
||||||
|
|
||||||
|
|
||||||
__all__ = ("main",)
|
__all__ = ("main",)
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
VERSION = (0, 8, 2)
|
VERSION = (0, 7, 3)
|
||||||
PRERELEASE = None # alpha, beta or rc
|
PRERELEASE = None # alpha, beta or rc
|
||||||
REVISION = None
|
REVISION = None
|
||||||
|
|
||||||
@@ -8,9 +8,9 @@ REVISION = None
|
|||||||
def generate_version(version, prerelease=None, revision=None):
|
def generate_version(version, prerelease=None, revision=None):
|
||||||
version_parts = [".".join(map(str, version))]
|
version_parts = [".".join(map(str, version))]
|
||||||
if prerelease is not None:
|
if prerelease is not None:
|
||||||
version_parts.append(f"-{prerelease}")
|
version_parts.append("-{}".format(prerelease))
|
||||||
if revision is not None:
|
if revision is not None:
|
||||||
version_parts.append(f".{revision}")
|
version_parts.append(".{}".format(revision))
|
||||||
return "".join(version_parts)
|
return "".join(version_parts)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -204,7 +204,7 @@ def lattice(c, *args, **kwargs):
|
|||||||
tables = read_pdf(
|
tables = read_pdf(
|
||||||
filepath, pages=pages, flavor="lattice", suppress_stdout=quiet, **kwargs
|
filepath, pages=pages, flavor="lattice", suppress_stdout=quiet, **kwargs
|
||||||
)
|
)
|
||||||
click.echo(f"Found {tables.n} tables")
|
click.echo("Found {} tables".format(tables.n))
|
||||||
if plot_type is not None:
|
if plot_type is not None:
|
||||||
for table in tables:
|
for table in tables:
|
||||||
plot(table, kind=plot_type)
|
plot(table, kind=plot_type)
|
||||||
@@ -295,7 +295,7 @@ def stream(c, *args, **kwargs):
|
|||||||
tables = read_pdf(
|
tables = read_pdf(
|
||||||
filepath, pages=pages, flavor="stream", suppress_stdout=quiet, **kwargs
|
filepath, pages=pages, flavor="stream", suppress_stdout=quiet, **kwargs
|
||||||
)
|
)
|
||||||
click.echo(f"Found {tables.n} tables")
|
click.echo("Found {} tables".format(tables.n))
|
||||||
if plot_type is not None:
|
if plot_type is not None:
|
||||||
for table in tables:
|
for table in tables:
|
||||||
plot(table, kind=plot_type)
|
plot(table, kind=plot_type)
|
||||||
|
|||||||
@@ -52,10 +52,13 @@ class TextEdge(object):
|
|||||||
self.is_valid = False
|
self.is_valid = False
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
x = round(self.x, 2)
|
return "<TextEdge x={} y0={} y1={} align={} valid={}>".format(
|
||||||
y0 = round(self.y0, 2)
|
round(self.x, 2),
|
||||||
y1 = round(self.y1, 2)
|
round(self.y0, 2),
|
||||||
return f"<TextEdge x={x} y0={y0} y1={y1} align={self.align} valid={self.is_valid}>"
|
round(self.y1, 2),
|
||||||
|
self.align,
|
||||||
|
self.is_valid,
|
||||||
|
)
|
||||||
|
|
||||||
def update_coords(self, x, y0, edge_tol=50):
|
def update_coords(self, x, y0, edge_tol=50):
|
||||||
"""Updates the text edge's x and bottom y coordinates and sets
|
"""Updates the text edge's x and bottom y coordinates and sets
|
||||||
@@ -288,11 +291,9 @@ class Cell(object):
|
|||||||
self._text = ""
|
self._text = ""
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
x1 = round(self.x1, 2)
|
return "<Cell x1={} y1={} x2={} y2={}>".format(
|
||||||
y1 = round(self.y1, 2)
|
round(self.x1, 2), round(self.y1, 2), round(self.x2, 2), round(self.y2, 2)
|
||||||
x2 = round(self.x2, 2)
|
)
|
||||||
y2 = round(self.y2, 2)
|
|
||||||
return f"<Cell x1={x1} y1={y1} x2={x2} y2={y2}>"
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def text(self):
|
def text(self):
|
||||||
@@ -350,7 +351,7 @@ class Table(object):
|
|||||||
self.page = None
|
self.page = None
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return f"<{self.__class__.__name__} shape={self.shape}>"
|
return "<{} shape={}>".format(self.__class__.__name__, self.shape)
|
||||||
|
|
||||||
def __lt__(self, other):
|
def __lt__(self, other):
|
||||||
if self.page == other.page:
|
if self.page == other.page:
|
||||||
@@ -611,7 +612,7 @@ class Table(object):
|
|||||||
|
|
||||||
"""
|
"""
|
||||||
kw = {
|
kw = {
|
||||||
"sheet_name": f"page-{self.page}-table-{self.order}",
|
"sheet_name": "page-{}-table-{}".format(self.page, self.order),
|
||||||
"encoding": "utf-8",
|
"encoding": "utf-8",
|
||||||
}
|
}
|
||||||
kw.update(kwargs)
|
kw.update(kwargs)
|
||||||
@@ -648,7 +649,7 @@ class Table(object):
|
|||||||
kw = {"if_exists": "replace", "index": False}
|
kw = {"if_exists": "replace", "index": False}
|
||||||
kw.update(kwargs)
|
kw.update(kwargs)
|
||||||
conn = sqlite3.connect(path)
|
conn = sqlite3.connect(path)
|
||||||
table_name = f"page-{self.page}-table-{self.order}"
|
table_name = "page-{}-table-{}".format(self.page, self.order)
|
||||||
self.df.to_sql(table_name, conn, **kw)
|
self.df.to_sql(table_name, conn, **kw)
|
||||||
conn.commit()
|
conn.commit()
|
||||||
conn.close()
|
conn.close()
|
||||||
@@ -669,7 +670,7 @@ class TableList(object):
|
|||||||
self._tables = tables
|
self._tables = tables
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return f"<{self.__class__.__name__} n={self.n}>"
|
return "<{} n={}>".format(self.__class__.__name__, self.n)
|
||||||
|
|
||||||
def __len__(self):
|
def __len__(self):
|
||||||
return len(self._tables)
|
return len(self._tables)
|
||||||
@@ -679,7 +680,7 @@ class TableList(object):
|
|||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _format_func(table, f):
|
def _format_func(table, f):
|
||||||
return getattr(table, f"to_{f}")
|
return getattr(table, "to_{}".format(f))
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def n(self):
|
def n(self):
|
||||||
@@ -690,7 +691,9 @@ class TableList(object):
|
|||||||
root = kwargs.get("root")
|
root = kwargs.get("root")
|
||||||
ext = kwargs.get("ext")
|
ext = kwargs.get("ext")
|
||||||
for table in self._tables:
|
for table in self._tables:
|
||||||
filename = f"{root}-page-{table.page}-table-{table.order}{ext}"
|
filename = os.path.join(
|
||||||
|
"{}-page-{}-table-{}{}".format(root, table.page, table.order, ext)
|
||||||
|
)
|
||||||
filepath = os.path.join(dirname, filename)
|
filepath = os.path.join(dirname, filename)
|
||||||
to_format = self._format_func(table, f)
|
to_format = self._format_func(table, f)
|
||||||
to_format(filepath)
|
to_format(filepath)
|
||||||
@@ -703,7 +706,9 @@ class TableList(object):
|
|||||||
zipname = os.path.join(os.path.dirname(path), root) + ".zip"
|
zipname = os.path.join(os.path.dirname(path), root) + ".zip"
|
||||||
with zipfile.ZipFile(zipname, "w", allowZip64=True) as z:
|
with zipfile.ZipFile(zipname, "w", allowZip64=True) as z:
|
||||||
for table in self._tables:
|
for table in self._tables:
|
||||||
filename = f"{root}-page-{table.page}-table-{table.order}{ext}"
|
filename = os.path.join(
|
||||||
|
"{}-page-{}-table-{}{}".format(root, table.page, table.order, ext)
|
||||||
|
)
|
||||||
filepath = os.path.join(dirname, filename)
|
filepath = os.path.join(dirname, filename)
|
||||||
z.write(filepath, os.path.basename(filepath))
|
z.write(filepath, os.path.basename(filepath))
|
||||||
|
|
||||||
@@ -736,7 +741,7 @@ class TableList(object):
|
|||||||
filepath = os.path.join(dirname, basename)
|
filepath = os.path.join(dirname, basename)
|
||||||
writer = pd.ExcelWriter(filepath)
|
writer = pd.ExcelWriter(filepath)
|
||||||
for table in self._tables:
|
for table in self._tables:
|
||||||
sheet_name = f"page-{table.page}-table-{table.order}"
|
sheet_name = "page-{}-table-{}".format(table.page, table.order)
|
||||||
table.df.to_excel(writer, sheet_name=sheet_name, encoding="utf-8")
|
table.df.to_excel(writer, sheet_name=sheet_name, encoding="utf-8")
|
||||||
writer.save()
|
writer.save()
|
||||||
if compress:
|
if compress:
|
||||||
|
|||||||
@@ -81,7 +81,6 @@ def delete_instance(instance):
|
|||||||
"""
|
"""
|
||||||
return libgs.gsapi_delete_instance(instance)
|
return libgs.gsapi_delete_instance(instance)
|
||||||
|
|
||||||
|
|
||||||
if sys.platform == "win32":
|
if sys.platform == "win32":
|
||||||
c_stdstream_call_t = WINFUNCTYPE(c_int, gs_main_instance, POINTER(c_char), c_int)
|
c_stdstream_call_t = WINFUNCTYPE(c_int, gs_main_instance, POINTER(c_char), c_int)
|
||||||
else:
|
else:
|
||||||
@@ -248,10 +247,7 @@ if sys.platform == "win32":
|
|||||||
libgs = __win32_finddll()
|
libgs = __win32_finddll()
|
||||||
if not libgs:
|
if not libgs:
|
||||||
import ctypes.util
|
import ctypes.util
|
||||||
|
libgs = ctypes.util.find_library("".join(("gsdll", str(ctypes.sizeof(ctypes.c_voidp) * 8), ".dll"))) # finds in %PATH%
|
||||||
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)
|
||||||
|
|||||||
@@ -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, f"page-{page}.pdf")
|
|
||||||
|
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,10 +167,9 @@ 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, f"page-{p}.pdf") for p in self.pages
|
os.path.join(tempdir, "page-{0}.pdf".format(p)) for p in self.pages
|
||||||
]
|
]
|
||||||
parser = Lattice(**kwargs) if flavor == "lattice" else Stream(**kwargs)
|
parser = Lattice(**kwargs) if flavor == "lattice" else Stream(**kwargs)
|
||||||
for p in pages:
|
for p in pages:
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
from __future__ import division
|
||||||
|
|
||||||
import cv2
|
import cv2
|
||||||
import numpy as np
|
import numpy as np
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
from __future__ import division
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
import copy
|
import copy
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
from __future__ import division
|
||||||
import os
|
import os
|
||||||
import logging
|
import logging
|
||||||
import warnings
|
import warnings
|
||||||
@@ -357,7 +358,7 @@ class Stream(BaseParser):
|
|||||||
ncols = max(set(elements), key=elements.count)
|
ncols = max(set(elements), key=elements.count)
|
||||||
else:
|
else:
|
||||||
warnings.warn(
|
warnings.warn(
|
||||||
f"No tables found in table area {table_idx + 1}"
|
"No tables found in table area {}".format(table_idx + 1)
|
||||||
)
|
)
|
||||||
cols = [(t.x0, t.x1) for r in rows_grouped if len(r) == ncols for t in r]
|
cols = [(t.x0, t.x1) for r in rows_grouped if len(r) == ncols for t in r]
|
||||||
cols = self._merge_columns(sorted(cols), column_tol=self.column_tol)
|
cols = self._merge_columns(sorted(cols), column_tol=self.column_tol)
|
||||||
@@ -432,19 +433,19 @@ class Stream(BaseParser):
|
|||||||
|
|
||||||
def extract_tables(self, filename, suppress_stdout=False, layout_kwargs={}):
|
def extract_tables(self, filename, suppress_stdout=False, layout_kwargs={}):
|
||||||
self._generate_layout(filename, layout_kwargs)
|
self._generate_layout(filename, layout_kwargs)
|
||||||
base_filename = os.path.basename(self.rootname)
|
|
||||||
|
|
||||||
if not suppress_stdout:
|
if not suppress_stdout:
|
||||||
logger.info(f"Processing {base_filename}")
|
logger.info("Processing {}".format(os.path.basename(self.rootname)))
|
||||||
|
|
||||||
if not self.horizontal_text:
|
if not self.horizontal_text:
|
||||||
if self.images:
|
if self.images:
|
||||||
warnings.warn(
|
warnings.warn(
|
||||||
f"{base_filename} is image-based, camelot only works on"
|
"{} is image-based, camelot only works on"
|
||||||
" text-based pages."
|
" text-based pages.".format(os.path.basename(self.rootname))
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
warnings.warn(f"No tables found on {base_filename}")
|
warnings.warn(
|
||||||
|
"No tables found on {}".format(os.path.basename(self.rootname))
|
||||||
|
)
|
||||||
return []
|
return []
|
||||||
|
|
||||||
self._generate_table_bbox()
|
self._generate_table_bbox()
|
||||||
|
|||||||
@@ -35,11 +35,11 @@ class PlotMethods(object):
|
|||||||
|
|
||||||
if table.flavor == "lattice" and kind in ["textedge"]:
|
if table.flavor == "lattice" and kind in ["textedge"]:
|
||||||
raise NotImplementedError(
|
raise NotImplementedError(
|
||||||
f"Lattice flavor does not support kind='{kind}'"
|
"Lattice flavor does not support kind='{}'".format(kind)
|
||||||
)
|
)
|
||||||
elif table.flavor == "stream" and kind in ["joint", "line"]:
|
elif table.flavor == "stream" and kind in ["joint", "line"]:
|
||||||
raise NotImplementedError(
|
raise NotImplementedError(
|
||||||
f"Stream flavor does not support kind='{kind}'"
|
"Stream flavor does not support kind='{}'".format(kind)
|
||||||
)
|
)
|
||||||
|
|
||||||
plot_method = getattr(self, kind)
|
plot_method = getattr(self, kind)
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
from __future__ import division
|
||||||
|
|
||||||
import os
|
|
||||||
import re
|
import re
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
import random
|
import random
|
||||||
import shutil
|
import shutil
|
||||||
import string
|
import string
|
||||||
@@ -27,9 +29,16 @@ from pdfminer.layout import (
|
|||||||
LTImage,
|
LTImage,
|
||||||
)
|
)
|
||||||
|
|
||||||
from urllib.request import Request, urlopen
|
|
||||||
|
PY3 = sys.version_info[0] >= 3
|
||||||
|
if PY3:
|
||||||
|
from urllib.request import urlopen
|
||||||
from urllib.parse import urlparse as parse_url
|
from urllib.parse import urlparse as parse_url
|
||||||
from urllib.parse import uses_relative, uses_netloc, uses_params
|
from urllib.parse import uses_relative, uses_netloc, uses_params
|
||||||
|
else:
|
||||||
|
from urllib2 import urlopen
|
||||||
|
from urlparse import urlparse as parse_url
|
||||||
|
from urlparse import uses_relative, uses_netloc, uses_params
|
||||||
|
|
||||||
|
|
||||||
_VALID_URLS = set(uses_relative + uses_netloc + uses_params)
|
_VALID_URLS = set(uses_relative + uses_netloc + uses_params)
|
||||||
@@ -79,12 +88,13 @@ def download_url(url):
|
|||||||
Temporary filepath.
|
Temporary filepath.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
filename = f"{random_string(6)}.pdf"
|
filename = "{}.pdf".format(random_string(6))
|
||||||
with tempfile.NamedTemporaryFile("wb", delete=False) as f:
|
with tempfile.NamedTemporaryFile("wb", delete=False) as f:
|
||||||
headers = {"User-Agent": "Mozilla/5.0"}
|
obj = urlopen(url)
|
||||||
request = Request(url, None, headers)
|
if PY3:
|
||||||
obj = urlopen(request)
|
|
||||||
content_type = obj.info().get_content_type()
|
content_type = obj.info().get_content_type()
|
||||||
|
else:
|
||||||
|
content_type = obj.info().getheader("Content-Type")
|
||||||
if content_type != "application/pdf":
|
if content_type != "application/pdf":
|
||||||
raise NotImplementedError("File format not supported")
|
raise NotImplementedError("File format not supported")
|
||||||
f.write(obj.read())
|
f.write(obj.read())
|
||||||
@@ -113,7 +123,9 @@ def validate_input(kwargs, flavor="lattice"):
|
|||||||
isec = set(parser_kwargs).intersection(set(input_kwargs.keys()))
|
isec = set(parser_kwargs).intersection(set(input_kwargs.keys()))
|
||||||
if isec:
|
if isec:
|
||||||
raise ValueError(
|
raise ValueError(
|
||||||
f"{','.join(sorted(isec))} cannot be used with flavor='{flavor}'"
|
"{} cannot be used with flavor='{}'".format(
|
||||||
|
",".join(sorted(isec)), flavor
|
||||||
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
if flavor == "lattice":
|
if flavor == "lattice":
|
||||||
@@ -411,7 +423,7 @@ def text_strip(text, strip=""):
|
|||||||
return text
|
return text
|
||||||
|
|
||||||
stripped = re.sub(
|
stripped = re.sub(
|
||||||
fr"[{''.join(map(re.escape, strip))}]", "", text, re.UNICODE
|
r"[{}]".format("".join(map(re.escape, strip))), "", text, re.UNICODE
|
||||||
)
|
)
|
||||||
return stripped
|
return stripped
|
||||||
|
|
||||||
@@ -648,7 +660,9 @@ def get_table_index(
|
|||||||
text_range = (t.x0, t.x1)
|
text_range = (t.x0, t.x1)
|
||||||
col_range = (table.cols[0][0], table.cols[-1][1])
|
col_range = (table.cols[0][0], table.cols[-1][1])
|
||||||
warnings.warn(
|
warnings.warn(
|
||||||
f"{text} {text_range} does not lie in column range {col_range}"
|
"{} {} does not lie in column range {}".format(
|
||||||
|
text, text_range, col_range
|
||||||
|
)
|
||||||
)
|
)
|
||||||
r_idx = r
|
r_idx = r
|
||||||
c_idx = lt_col_overlap.index(max(lt_col_overlap))
|
c_idx = lt_col_overlap.index(max(lt_col_overlap))
|
||||||
@@ -780,7 +794,7 @@ def get_page_layout(
|
|||||||
parser = PDFParser(f)
|
parser = PDFParser(f)
|
||||||
document = PDFDocument(parser)
|
document = PDFDocument(parser)
|
||||||
if not document.is_extractable:
|
if not document.is_extractable:
|
||||||
raise PDFTextExtractionNotAllowed(f"Text extraction is not allowed: {filename}")
|
raise PDFTextExtractionNotAllowed
|
||||||
laparams = LAParams(
|
laparams = LAParams(
|
||||||
char_margin=char_margin,
|
char_margin=char_margin,
|
||||||
line_margin=line_margin,
|
line_margin=line_margin,
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ Setting up a development environment
|
|||||||
|
|
||||||
To install the dependencies needed for development, you can use pip::
|
To install the dependencies needed for development, you can use pip::
|
||||||
|
|
||||||
$ pip install "camelot-py[dev]"
|
$ pip install camelot-py[dev]
|
||||||
|
|
||||||
Alternatively, you can clone the project repository, and install using pip::
|
Alternatively, you can clone the project repository, and install using pip::
|
||||||
|
|
||||||
|
|||||||
@@ -33,9 +33,6 @@ Release v\ |version|. (:ref:`Installation <install>`)
|
|||||||
.. image:: https://img.shields.io/badge/code%20style-black-000000.svg
|
.. image:: https://img.shields.io/badge/code%20style-black-000000.svg
|
||||||
:target: https://github.com/ambv/black
|
:target: https://github.com/ambv/black
|
||||||
|
|
||||||
.. image:: https://img.shields.io/badge/continous%20quality-deepsource-lightgrey
|
|
||||||
:target: https://deepsource.io/gh/camelot-dev/camelot/?ref=repository-badge
|
|
||||||
|
|
||||||
**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!
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ Stream can be used to parse tables that have whitespaces between cells to simula
|
|||||||
|
|
||||||
3. The number of columns inside each table area are then guessed. This is done by calculating the mode of number of words in each text row. Based on this mode, words in each text row are chosen to calculate a list of column *x* ranges.
|
3. The number of columns inside each table area are then guessed. This is done by calculating the mode of number of words in each text row. Based on this mode, words in each text row are chosen to calculate a list of column *x* ranges.
|
||||||
|
|
||||||
4. Words that lie inside/outside the current column *x* ranges are then used to extend the current list of columns.
|
4. Words that lie inside/outside the current column *x* ranges are then used to extend extend the current list of columns.
|
||||||
|
|
||||||
5. Finally, a table is formed using the text rows' *y* ranges and column *x* ranges and words found on the page are assigned to the table's cells based on their *x* and *y* coordinates.
|
5. Finally, a table is formed using the text rows' *y* ranges and column *x* ranges and words found on the page are assigned to the table's cells based on their *x* and *y* coordinates.
|
||||||
|
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ Using pip
|
|||||||
|
|
||||||
After :ref:`installing the dependencies <install_deps>`, which include `Tkinter`_ and `ghostscript`_, you can simply use pip to install Camelot::
|
After :ref:`installing the dependencies <install_deps>`, which include `Tkinter`_ and `ghostscript`_, you can simply use pip to install Camelot::
|
||||||
|
|
||||||
$ pip install "camelot-py[cv]"
|
$ pip install camelot-py[cv]
|
||||||
|
|
||||||
.. _Tkinter: https://wiki.python.org/moin/TkInter
|
.. _Tkinter: https://wiki.python.org/moin/TkInter
|
||||||
.. _ghostscript: https://www.ghostscript.com
|
.. _ghostscript: https://www.ghostscript.com
|
||||||
|
|||||||
@@ -1,10 +1,8 @@
|
|||||||
chardet>=3.0.4
|
|
||||||
click>=6.7
|
click>=6.7
|
||||||
matplotlib>=2.2.3
|
matplotlib>=2.2.3
|
||||||
numpy>=1.13.3
|
numpy>=1.13.3
|
||||||
opencv-python>=3.4.2.17
|
opencv-python>=3.4.2.17
|
||||||
openpyxl>=2.5.8
|
openpyxl>=2.5.8
|
||||||
pandas>=0.23.4
|
pandas>=0.23.4
|
||||||
pdfminer.six>=20200726
|
pdfminer.six>=20170720
|
||||||
PyPDF2>=1.26.0
|
PyPDF2>=1.26.0
|
||||||
Sphinx>=3.1.2
|
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ requires = [
|
|||||||
'numpy>=1.13.3',
|
'numpy>=1.13.3',
|
||||||
'openpyxl>=2.5.8',
|
'openpyxl>=2.5.8',
|
||||||
'pandas>=0.23.4',
|
'pandas>=0.23.4',
|
||||||
'pdfminer.six>=20200726',
|
'pdfminer.six>=20170720',
|
||||||
'PyPDF2>=1.26.0'
|
'PyPDF2>=1.26.0'
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -33,11 +33,11 @@ plot_requires = [
|
|||||||
|
|
||||||
dev_requires = [
|
dev_requires = [
|
||||||
'codecov>=2.0.15',
|
'codecov>=2.0.15',
|
||||||
'pytest>=5.4.3',
|
'pytest>=3.8.0',
|
||||||
'pytest-cov>=2.10.0',
|
'pytest-cov>=2.6.0',
|
||||||
'pytest-mpl>=0.11',
|
'pytest-mpl>=0.10',
|
||||||
'pytest-runner>=5.2',
|
'pytest-runner>=4.2',
|
||||||
'Sphinx>=3.1.2'
|
'Sphinx>=1.7.9'
|
||||||
]
|
]
|
||||||
|
|
||||||
all_requires = cv_requires + plot_requires
|
all_requires = cv_requires + plot_requires
|
||||||
@@ -71,9 +71,10 @@ def setup_package():
|
|||||||
# Trove classifiers
|
# Trove classifiers
|
||||||
# Full list: https://pypi.python.org/pypi?%3Aaction=list_classifiers
|
# Full list: https://pypi.python.org/pypi?%3Aaction=list_classifiers
|
||||||
'License :: OSI Approved :: MIT License',
|
'License :: OSI Approved :: MIT License',
|
||||||
|
'Programming Language :: Python :: 2.7',
|
||||||
|
'Programming Language :: Python :: 3.5',
|
||||||
'Programming Language :: Python :: 3.6',
|
'Programming Language :: Python :: 3.6',
|
||||||
'Programming Language :: Python :: 3.7',
|
'Programming Language :: Python :: 3.7'
|
||||||
'Programming Language :: Python :: 3.8'
|
|
||||||
])
|
])
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
|||||||
@@ -1,3 +1,2 @@
|
|||||||
import matplotlib
|
import matplotlib
|
||||||
|
matplotlib.use('agg')
|
||||||
matplotlib.use("agg")
|
|
||||||
|
|||||||
@@ -1,7 +1,19 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
|
||||||
data_stream = [
|
data_stream = [
|
||||||
|
[
|
||||||
|
"",
|
||||||
|
"Table: 5 Public Health Outlay 2012-13 (Budget Estimates) (Rs. in 000)",
|
||||||
|
"",
|
||||||
|
"",
|
||||||
|
"",
|
||||||
|
"",
|
||||||
|
"",
|
||||||
|
"",
|
||||||
|
],
|
||||||
["States-A", "Revenue", "", "Capital", "", "Total", "Others(1)", "Total"],
|
["States-A", "Revenue", "", "Capital", "", "Total", "Others(1)", "Total"],
|
||||||
["", "", "", "", "", "Revenue &", "", ""],
|
["", "", "", "", "", "Revenue &", "", ""],
|
||||||
["", "Medical &", "Family", "Medical &", "Family", "", "", ""],
|
["", "Medical &", "Family", "Medical &", "Family", "", "", ""],
|
||||||
@@ -817,6 +829,18 @@ data_stream_table_rotated = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
data_stream_two_tables_1 = [
|
data_stream_two_tables_1 = [
|
||||||
|
[
|
||||||
|
"[In thousands (11,062.6 represents 11,062,600) For year ending December 31. Based on Uniform Crime Reporting (UCR)",
|
||||||
|
"",
|
||||||
|
"",
|
||||||
|
"",
|
||||||
|
"",
|
||||||
|
"",
|
||||||
|
"",
|
||||||
|
"",
|
||||||
|
"",
|
||||||
|
"",
|
||||||
|
],
|
||||||
[
|
[
|
||||||
"Program. Represents arrests reported (not charged) by 12,910 agencies with a total population of 247,526,916 as estimated",
|
"Program. Represents arrests reported (not charged) by 12,910 agencies with a total population of 247,526,916 as estimated",
|
||||||
"",
|
"",
|
||||||
@@ -1276,10 +1300,29 @@ data_stream_two_tables_1 = [
|
|||||||
"",
|
"",
|
||||||
"",
|
"",
|
||||||
],
|
],
|
||||||
|
[
|
||||||
|
"",
|
||||||
|
"Source: U.S. Department of Justice, Federal Bureau of Investigation, Uniform Crime Reports, Arrests Master Files.",
|
||||||
|
"",
|
||||||
|
"",
|
||||||
|
"",
|
||||||
|
"",
|
||||||
|
"",
|
||||||
|
"",
|
||||||
|
"",
|
||||||
|
"",
|
||||||
|
],
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
data_stream_two_tables_2 = [
|
data_stream_two_tables_2 = [
|
||||||
|
[
|
||||||
|
"",
|
||||||
|
"Source: U.S. Department of Justice, Federal Bureau of Investigation, Uniform Crime Reports, Arrests Master Files.",
|
||||||
|
"",
|
||||||
|
"",
|
||||||
|
"",
|
||||||
|
"",
|
||||||
|
],
|
||||||
["Table 325. Arrests by Race: 2009", "", "", "", "", ""],
|
["Table 325. Arrests by Race: 2009", "", "", "", "", ""],
|
||||||
[
|
[
|
||||||
"[Based on Uniform Crime Reporting (UCR) Program. Represents arrests reported (not charged) by 12,371 agencies",
|
"[Based on Uniform Crime Reporting (UCR) Program. Represents arrests reported (not charged) by 12,371 agencies",
|
||||||
@@ -1557,9 +1600,16 @@ data_stream_two_tables_2 = [
|
|||||||
"3,950",
|
"3,950",
|
||||||
],
|
],
|
||||||
["1 Except forcible rape and prostitution.", "", "", "", "", ""],
|
["1 Except forcible rape and prostitution.", "", "", "", "", ""],
|
||||||
|
[
|
||||||
|
"",
|
||||||
|
"Source: U.S. Department of Justice, Federal Bureau of Investigation, “Crime in the United States, Arrests,” September 2010,",
|
||||||
|
"",
|
||||||
|
"",
|
||||||
|
"",
|
||||||
|
"",
|
||||||
|
],
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
data_stream_table_areas = [
|
data_stream_table_areas = [
|
||||||
["", "One Withholding"],
|
["", "One Withholding"],
|
||||||
["Payroll Period", "Allowance"],
|
["Payroll Period", "Allowance"],
|
||||||
@@ -1726,7 +1776,18 @@ data_stream_columns = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
data_stream_split_text = [
|
data_stream_split_text = [
|
||||||
["FEB", "RUAR", "Y 2014 M27 (BUS)", "", "", "", "", "", "", ""],
|
[
|
||||||
|
"FEB",
|
||||||
|
"RUAR",
|
||||||
|
"Y 2014 M27 (BUS)",
|
||||||
|
"",
|
||||||
|
"ALPHABETIC LISTING BY T",
|
||||||
|
"YPE",
|
||||||
|
"",
|
||||||
|
"",
|
||||||
|
"",
|
||||||
|
"ABLPDM27",
|
||||||
|
],
|
||||||
["", "", "", "", "OF ACTIVE LICENSES", "", "", "", "", "3/19/2014"],
|
["", "", "", "", "OF ACTIVE LICENSES", "", "", "", "", "3/19/2014"],
|
||||||
["", "", "", "", "OKLAHOMA ABLE COMMIS", "SION", "", "", "", ""],
|
["", "", "", "", "OKLAHOMA ABLE COMMIS", "SION", "", "", "", ""],
|
||||||
["LICENSE", "", "", "", "PREMISE", "", "", "", "", ""],
|
["LICENSE", "", "", "", "PREMISE", "", "", "", "", ""],
|
||||||
@@ -2060,7 +2121,6 @@ data_stream_split_text = [
|
|||||||
],
|
],
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
data_stream_flag_size = [
|
data_stream_flag_size = [
|
||||||
[
|
[
|
||||||
"States",
|
"States",
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 48 KiB After Width: | Height: | Size: 48 KiB |
|
Before Width: | Height: | Size: 6.7 KiB After Width: | Height: | Size: 6.7 KiB |
|
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 13 KiB |
|
Before Width: | Height: | Size: 8.9 KiB After Width: | Height: | Size: 8.8 KiB |
|
Before Width: | Height: | Size: 19 KiB After Width: | Height: | Size: 18 KiB |
@@ -114,35 +114,31 @@ def test_cli_password():
|
|||||||
def test_cli_output_format():
|
def test_cli_output_format():
|
||||||
with TemporaryDirectory() as tempdir:
|
with TemporaryDirectory() as tempdir:
|
||||||
infile = os.path.join(testdir, "health.pdf")
|
infile = os.path.join(testdir, "health.pdf")
|
||||||
|
outfile = os.path.join(tempdir, "health.{}")
|
||||||
runner = CliRunner()
|
runner = CliRunner()
|
||||||
|
|
||||||
# json
|
# json
|
||||||
outfile = os.path.join(tempdir, "health.json")
|
|
||||||
result = runner.invoke(
|
result = runner.invoke(
|
||||||
cli,
|
cli,
|
||||||
["--format", "json", "--output", outfile, "stream", infile],
|
["--format", "json", "--output", outfile.format("json"), "stream", infile],
|
||||||
)
|
)
|
||||||
assert result.exit_code == 0
|
assert result.exit_code == 0
|
||||||
|
|
||||||
# excel
|
# excel
|
||||||
outfile = os.path.join(tempdir, "health.xlsx")
|
|
||||||
result = runner.invoke(
|
result = runner.invoke(
|
||||||
cli,
|
cli,
|
||||||
["--format", "excel", "--output", outfile, "stream", infile],
|
["--format", "excel", "--output", outfile.format("xlsx"), "stream", infile],
|
||||||
)
|
)
|
||||||
assert result.exit_code == 0
|
assert result.exit_code == 0
|
||||||
|
|
||||||
# html
|
# html
|
||||||
outfile = os.path.join(tempdir, "health.html")
|
|
||||||
result = runner.invoke(
|
result = runner.invoke(
|
||||||
cli,
|
cli,
|
||||||
["--format", "html", "--output", outfile, "stream", infile],
|
["--format", "html", "--output", outfile.format("html"), "stream", infile],
|
||||||
)
|
)
|
||||||
assert result.exit_code == 0
|
assert result.exit_code == 0
|
||||||
|
|
||||||
# zip
|
# zip
|
||||||
outfile = os.path.join(tempdir, "health.csv")
|
|
||||||
result = runner.invoke(
|
result = runner.invoke(
|
||||||
cli,
|
cli,
|
||||||
[
|
[
|
||||||
@@ -150,7 +146,7 @@ def test_cli_output_format():
|
|||||||
"--format",
|
"--format",
|
||||||
"csv",
|
"csv",
|
||||||
"--output",
|
"--output",
|
||||||
outfile,
|
outfile.format("csv"),
|
||||||
"stream",
|
"stream",
|
||||||
infile,
|
infile,
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -3,11 +3,9 @@
|
|||||||
import os
|
import os
|
||||||
|
|
||||||
import pandas as pd
|
import pandas as pd
|
||||||
from pandas.testing import assert_frame_equal
|
|
||||||
|
|
||||||
import camelot
|
import camelot
|
||||||
from camelot.core import Table, TableList
|
from camelot.core import Table, TableList
|
||||||
from camelot.__version__ import generate_version
|
|
||||||
|
|
||||||
from .data import *
|
from .data import *
|
||||||
|
|
||||||
@@ -28,10 +26,10 @@ def test_password():
|
|||||||
|
|
||||||
filename = os.path.join(testdir, "health_protected.pdf")
|
filename = os.path.join(testdir, "health_protected.pdf")
|
||||||
tables = camelot.read_pdf(filename, password="ownerpass", flavor="stream")
|
tables = camelot.read_pdf(filename, password="ownerpass", flavor="stream")
|
||||||
assert_frame_equal(df, tables[0].df)
|
assert df.equals(tables[0].df)
|
||||||
|
|
||||||
tables = camelot.read_pdf(filename, password="userpass", flavor="stream")
|
tables = camelot.read_pdf(filename, password="userpass", flavor="stream")
|
||||||
assert_frame_equal(df, tables[0].df)
|
assert df.equals(tables[0].df)
|
||||||
|
|
||||||
|
|
||||||
def test_stream():
|
def test_stream():
|
||||||
@@ -39,7 +37,7 @@ def test_stream():
|
|||||||
|
|
||||||
filename = os.path.join(testdir, "health.pdf")
|
filename = os.path.join(testdir, "health.pdf")
|
||||||
tables = camelot.read_pdf(filename, flavor="stream")
|
tables = camelot.read_pdf(filename, flavor="stream")
|
||||||
assert_frame_equal(df, tables[0].df)
|
assert df.equals(tables[0].df)
|
||||||
|
|
||||||
|
|
||||||
def test_stream_table_rotated():
|
def test_stream_table_rotated():
|
||||||
@@ -47,11 +45,11 @@ def test_stream_table_rotated():
|
|||||||
|
|
||||||
filename = os.path.join(testdir, "clockwise_table_2.pdf")
|
filename = os.path.join(testdir, "clockwise_table_2.pdf")
|
||||||
tables = camelot.read_pdf(filename, flavor="stream")
|
tables = camelot.read_pdf(filename, flavor="stream")
|
||||||
assert_frame_equal(df, tables[0].df)
|
assert df.equals(tables[0].df)
|
||||||
|
|
||||||
filename = os.path.join(testdir, "anticlockwise_table_2.pdf")
|
filename = os.path.join(testdir, "anticlockwise_table_2.pdf")
|
||||||
tables = camelot.read_pdf(filename, flavor="stream")
|
tables = camelot.read_pdf(filename, flavor="stream")
|
||||||
assert_frame_equal(df, tables[0].df)
|
assert df.equals(tables[0].df)
|
||||||
|
|
||||||
|
|
||||||
def test_stream_two_tables():
|
def test_stream_two_tables():
|
||||||
@@ -73,7 +71,7 @@ def test_stream_table_regions():
|
|||||||
tables = camelot.read_pdf(
|
tables = camelot.read_pdf(
|
||||||
filename, flavor="stream", table_regions=["320,460,573,335"]
|
filename, flavor="stream", table_regions=["320,460,573,335"]
|
||||||
)
|
)
|
||||||
assert_frame_equal(df, tables[0].df)
|
assert df.equals(tables[0].df)
|
||||||
|
|
||||||
|
|
||||||
def test_stream_table_areas():
|
def test_stream_table_areas():
|
||||||
@@ -83,7 +81,7 @@ def test_stream_table_areas():
|
|||||||
tables = camelot.read_pdf(
|
tables = camelot.read_pdf(
|
||||||
filename, flavor="stream", table_areas=["320,500,573,335"]
|
filename, flavor="stream", table_areas=["320,500,573,335"]
|
||||||
)
|
)
|
||||||
assert_frame_equal(df, tables[0].df)
|
assert df.equals(tables[0].df)
|
||||||
|
|
||||||
|
|
||||||
def test_stream_columns():
|
def test_stream_columns():
|
||||||
@@ -93,7 +91,7 @@ def test_stream_columns():
|
|||||||
tables = camelot.read_pdf(
|
tables = camelot.read_pdf(
|
||||||
filename, flavor="stream", columns=["67,180,230,425,475"], row_tol=10
|
filename, flavor="stream", columns=["67,180,230,425,475"], row_tol=10
|
||||||
)
|
)
|
||||||
assert_frame_equal(df, tables[0].df)
|
assert df.equals(tables[0].df)
|
||||||
|
|
||||||
|
|
||||||
def test_stream_split_text():
|
def test_stream_split_text():
|
||||||
@@ -106,7 +104,7 @@ def test_stream_split_text():
|
|||||||
columns=["72,95,209,327,442,529,566,606,683"],
|
columns=["72,95,209,327,442,529,566,606,683"],
|
||||||
split_text=True,
|
split_text=True,
|
||||||
)
|
)
|
||||||
assert_frame_equal(df, tables[0].df)
|
assert df.equals(tables[0].df)
|
||||||
|
|
||||||
|
|
||||||
def test_stream_flag_size():
|
def test_stream_flag_size():
|
||||||
@@ -114,7 +112,7 @@ def test_stream_flag_size():
|
|||||||
|
|
||||||
filename = os.path.join(testdir, "superscript.pdf")
|
filename = os.path.join(testdir, "superscript.pdf")
|
||||||
tables = camelot.read_pdf(filename, flavor="stream", flag_size=True)
|
tables = camelot.read_pdf(filename, flavor="stream", flag_size=True)
|
||||||
assert_frame_equal(df, tables[0].df)
|
assert df.equals(tables[0].df)
|
||||||
|
|
||||||
|
|
||||||
def test_stream_strip_text():
|
def test_stream_strip_text():
|
||||||
@@ -122,7 +120,7 @@ def test_stream_strip_text():
|
|||||||
|
|
||||||
filename = os.path.join(testdir, "detect_vertical_false.pdf")
|
filename = os.path.join(testdir, "detect_vertical_false.pdf")
|
||||||
tables = camelot.read_pdf(filename, flavor="stream", strip_text=" ,\n")
|
tables = camelot.read_pdf(filename, flavor="stream", strip_text=" ,\n")
|
||||||
assert_frame_equal(df, tables[0].df)
|
assert df.equals(tables[0].df)
|
||||||
|
|
||||||
|
|
||||||
def test_stream_edge_tol():
|
def test_stream_edge_tol():
|
||||||
@@ -130,7 +128,7 @@ def test_stream_edge_tol():
|
|||||||
|
|
||||||
filename = os.path.join(testdir, "edge_tol.pdf")
|
filename = os.path.join(testdir, "edge_tol.pdf")
|
||||||
tables = camelot.read_pdf(filename, flavor="stream", edge_tol=500)
|
tables = camelot.read_pdf(filename, flavor="stream", edge_tol=500)
|
||||||
assert_frame_equal(df, tables[0].df)
|
assert df.equals(tables[0].df)
|
||||||
|
|
||||||
|
|
||||||
def test_stream_layout_kwargs():
|
def test_stream_layout_kwargs():
|
||||||
@@ -140,7 +138,7 @@ def test_stream_layout_kwargs():
|
|||||||
tables = camelot.read_pdf(
|
tables = camelot.read_pdf(
|
||||||
filename, flavor="stream", layout_kwargs={"detect_vertical": False}
|
filename, flavor="stream", layout_kwargs={"detect_vertical": False}
|
||||||
)
|
)
|
||||||
assert_frame_equal(df, tables[0].df)
|
assert df.equals(tables[0].df)
|
||||||
|
|
||||||
|
|
||||||
def test_lattice():
|
def test_lattice():
|
||||||
@@ -150,7 +148,7 @@ def test_lattice():
|
|||||||
testdir, "tabula/icdar2013-dataset/competition-dataset-us/us-030.pdf"
|
testdir, "tabula/icdar2013-dataset/competition-dataset-us/us-030.pdf"
|
||||||
)
|
)
|
||||||
tables = camelot.read_pdf(filename, pages="2")
|
tables = camelot.read_pdf(filename, pages="2")
|
||||||
assert_frame_equal(df, tables[0].df)
|
assert df.equals(tables[0].df)
|
||||||
|
|
||||||
|
|
||||||
def test_lattice_table_rotated():
|
def test_lattice_table_rotated():
|
||||||
@@ -158,11 +156,11 @@ def test_lattice_table_rotated():
|
|||||||
|
|
||||||
filename = os.path.join(testdir, "clockwise_table_1.pdf")
|
filename = os.path.join(testdir, "clockwise_table_1.pdf")
|
||||||
tables = camelot.read_pdf(filename)
|
tables = camelot.read_pdf(filename)
|
||||||
assert_frame_equal(df, tables[0].df)
|
assert df.equals(tables[0].df)
|
||||||
|
|
||||||
filename = os.path.join(testdir, "anticlockwise_table_1.pdf")
|
filename = os.path.join(testdir, "anticlockwise_table_1.pdf")
|
||||||
tables = camelot.read_pdf(filename)
|
tables = camelot.read_pdf(filename)
|
||||||
assert_frame_equal(df, tables[0].df)
|
assert df.equals(tables[0].df)
|
||||||
|
|
||||||
|
|
||||||
def test_lattice_two_tables():
|
def test_lattice_two_tables():
|
||||||
@@ -181,7 +179,7 @@ def test_lattice_table_regions():
|
|||||||
|
|
||||||
filename = os.path.join(testdir, "table_region.pdf")
|
filename = os.path.join(testdir, "table_region.pdf")
|
||||||
tables = camelot.read_pdf(filename, table_regions=["170,370,560,270"])
|
tables = camelot.read_pdf(filename, table_regions=["170,370,560,270"])
|
||||||
assert_frame_equal(df, tables[0].df)
|
assert df.equals(tables[0].df)
|
||||||
|
|
||||||
|
|
||||||
def test_lattice_table_areas():
|
def test_lattice_table_areas():
|
||||||
@@ -189,7 +187,7 @@ def test_lattice_table_areas():
|
|||||||
|
|
||||||
filename = os.path.join(testdir, "twotables_2.pdf")
|
filename = os.path.join(testdir, "twotables_2.pdf")
|
||||||
tables = camelot.read_pdf(filename, table_areas=["80,693,535,448"])
|
tables = camelot.read_pdf(filename, table_areas=["80,693,535,448"])
|
||||||
assert_frame_equal(df, tables[0].df)
|
assert df.equals(tables[0].df)
|
||||||
|
|
||||||
|
|
||||||
def test_lattice_process_background():
|
def test_lattice_process_background():
|
||||||
@@ -197,7 +195,7 @@ def test_lattice_process_background():
|
|||||||
|
|
||||||
filename = os.path.join(testdir, "background_lines_1.pdf")
|
filename = os.path.join(testdir, "background_lines_1.pdf")
|
||||||
tables = camelot.read_pdf(filename, process_background=True)
|
tables = camelot.read_pdf(filename, process_background=True)
|
||||||
assert_frame_equal(df, tables[1].df)
|
assert df.equals(tables[1].df)
|
||||||
|
|
||||||
|
|
||||||
def test_lattice_copy_text():
|
def test_lattice_copy_text():
|
||||||
@@ -205,7 +203,7 @@ def test_lattice_copy_text():
|
|||||||
|
|
||||||
filename = os.path.join(testdir, "row_span_1.pdf")
|
filename = os.path.join(testdir, "row_span_1.pdf")
|
||||||
tables = camelot.read_pdf(filename, line_scale=60, copy_text="v")
|
tables = camelot.read_pdf(filename, line_scale=60, copy_text="v")
|
||||||
assert_frame_equal(df, tables[0].df)
|
assert df.equals(tables[0].df)
|
||||||
|
|
||||||
|
|
||||||
def test_lattice_shift_text():
|
def test_lattice_shift_text():
|
||||||
@@ -273,7 +271,7 @@ def test_arabic():
|
|||||||
|
|
||||||
filename = os.path.join(testdir, "tabula/arabic.pdf")
|
filename = os.path.join(testdir, "tabula/arabic.pdf")
|
||||||
tables = camelot.read_pdf(filename)
|
tables = camelot.read_pdf(filename)
|
||||||
assert_frame_equal(df, tables[0].df)
|
assert df.equals(tables[0].df)
|
||||||
|
|
||||||
|
|
||||||
def test_table_order():
|
def test_table_order():
|
||||||
@@ -299,18 +297,3 @@ def test_table_order():
|
|||||||
(1, 2),
|
(1, 2),
|
||||||
(1, 1),
|
(1, 1),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
def test_version_generation():
|
|
||||||
version = (0, 7, 3)
|
|
||||||
assert generate_version(version, prerelease=None, revision=None) == "0.7.3"
|
|
||||||
|
|
||||||
|
|
||||||
def test_version_generation_with_prerelease_revision():
|
|
||||||
version = (0, 7, 3)
|
|
||||||
prerelease = "alpha"
|
|
||||||
revision = 2
|
|
||||||
assert (
|
|
||||||
generate_version(version, prerelease=prerelease, revision=revision)
|
|
||||||
== "0.7.3-alpha.2"
|
|
||||||
)
|
|
||||||
|
|||||||
@@ -10,93 +10,88 @@ import camelot
|
|||||||
|
|
||||||
testdir = os.path.dirname(os.path.abspath(__file__))
|
testdir = os.path.dirname(os.path.abspath(__file__))
|
||||||
testdir = os.path.join(testdir, "files")
|
testdir = os.path.join(testdir, "files")
|
||||||
filename = os.path.join(testdir, "foo.pdf")
|
filename = os.path.join(testdir, 'foo.pdf')
|
||||||
|
|
||||||
|
|
||||||
def test_unknown_flavor():
|
def test_unknown_flavor():
|
||||||
message = "Unknown flavor specified." " Use either 'lattice' or 'stream'"
|
message = ("Unknown flavor specified."
|
||||||
|
" Use either 'lattice' or 'stream'")
|
||||||
with pytest.raises(NotImplementedError, match=message):
|
with pytest.raises(NotImplementedError, match=message):
|
||||||
tables = camelot.read_pdf(filename, flavor="chocolate")
|
tables = camelot.read_pdf(filename, flavor='chocolate')
|
||||||
|
|
||||||
|
|
||||||
def test_input_kwargs():
|
def test_input_kwargs():
|
||||||
message = "columns cannot be used with flavor='lattice'"
|
message = "columns cannot be used with flavor='lattice'"
|
||||||
with pytest.raises(ValueError, match=message):
|
with pytest.raises(ValueError, match=message):
|
||||||
tables = camelot.read_pdf(filename, columns=["10,20,30,40"])
|
tables = camelot.read_pdf(filename, columns=['10,20,30,40'])
|
||||||
|
|
||||||
|
|
||||||
def test_unsupported_format():
|
def test_unsupported_format():
|
||||||
message = "File format not supported"
|
message = 'File format not supported'
|
||||||
filename = os.path.join(testdir, "foo.csv")
|
filename = os.path.join(testdir, 'foo.csv')
|
||||||
with pytest.raises(NotImplementedError, match=message):
|
with pytest.raises(NotImplementedError, match=message):
|
||||||
tables = camelot.read_pdf(filename)
|
tables = camelot.read_pdf(filename)
|
||||||
|
|
||||||
|
|
||||||
def test_stream_equal_length():
|
def test_stream_equal_length():
|
||||||
message = "Length of table_areas and columns" " should be equal"
|
message = ("Length of table_areas and columns"
|
||||||
|
" should be equal")
|
||||||
with pytest.raises(ValueError, match=message):
|
with pytest.raises(ValueError, match=message):
|
||||||
tables = camelot.read_pdf(
|
tables = camelot.read_pdf(filename, flavor='stream',
|
||||||
filename,
|
table_areas=['10,20,30,40'], columns=['10,20,30,40', '10,20,30,40'])
|
||||||
flavor="stream",
|
|
||||||
table_areas=["10,20,30,40"],
|
|
||||||
columns=["10,20,30,40", "10,20,30,40"],
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def test_image_warning():
|
def test_image_warning():
|
||||||
filename = os.path.join(testdir, "image.pdf")
|
filename = os.path.join(testdir, 'image.pdf')
|
||||||
with warnings.catch_warnings():
|
with warnings.catch_warnings():
|
||||||
warnings.simplefilter("error")
|
warnings.simplefilter('error')
|
||||||
with pytest.raises(UserWarning) as e:
|
with pytest.raises(UserWarning) as e:
|
||||||
tables = camelot.read_pdf(filename)
|
tables = camelot.read_pdf(filename)
|
||||||
assert (
|
assert str(e.value) == 'page-1 is image-based, camelot only works on text-based pages.'
|
||||||
str(e.value)
|
|
||||||
== "page-1 is image-based, camelot only works on text-based pages."
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def test_no_tables_found():
|
def test_no_tables_found():
|
||||||
filename = os.path.join(testdir, "blank.pdf")
|
filename = os.path.join(testdir, 'blank.pdf')
|
||||||
with warnings.catch_warnings():
|
with warnings.catch_warnings():
|
||||||
warnings.simplefilter("error")
|
warnings.simplefilter('error')
|
||||||
with pytest.raises(UserWarning) as e:
|
with pytest.raises(UserWarning) as e:
|
||||||
tables = camelot.read_pdf(filename)
|
tables = camelot.read_pdf(filename)
|
||||||
assert str(e.value) == "No tables found on page-1"
|
assert str(e.value) == 'No tables found on page-1'
|
||||||
|
|
||||||
|
|
||||||
def test_no_tables_found_logs_suppressed():
|
def test_no_tables_found_logs_suppressed():
|
||||||
filename = os.path.join(testdir, "foo.pdf")
|
filename = os.path.join(testdir, 'foo.pdf')
|
||||||
with warnings.catch_warnings():
|
with warnings.catch_warnings():
|
||||||
# the test should fail if any warning is thrown
|
# the test should fail if any warning is thrown
|
||||||
warnings.simplefilter("error")
|
warnings.simplefilter('error')
|
||||||
try:
|
try:
|
||||||
tables = camelot.read_pdf(filename, suppress_stdout=True)
|
tables = camelot.read_pdf(filename, suppress_stdout=True)
|
||||||
except Warning as e:
|
except Warning as e:
|
||||||
warning_text = str(e)
|
warning_text = str(e)
|
||||||
pytest.fail(f"Unexpected warning: {warning_text}")
|
pytest.fail('Unexpected warning: {}'.format(warning_text))
|
||||||
|
|
||||||
|
|
||||||
def test_no_tables_found_warnings_suppressed():
|
def test_no_tables_found_warnings_suppressed():
|
||||||
filename = os.path.join(testdir, "blank.pdf")
|
filename = os.path.join(testdir, 'blank.pdf')
|
||||||
with warnings.catch_warnings():
|
with warnings.catch_warnings():
|
||||||
# the test should fail if any warning is thrown
|
# the test should fail if any warning is thrown
|
||||||
warnings.simplefilter("error")
|
warnings.simplefilter('error')
|
||||||
try:
|
try:
|
||||||
tables = camelot.read_pdf(filename, suppress_stdout=True)
|
tables = camelot.read_pdf(filename, suppress_stdout=True)
|
||||||
except Warning as e:
|
except Warning as e:
|
||||||
warning_text = str(e)
|
warning_text = str(e)
|
||||||
pytest.fail(f"Unexpected warning: {warning_text}")
|
pytest.fail('Unexpected warning: {}'.format(warning_text))
|
||||||
|
|
||||||
|
|
||||||
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'
|
||||||
with pytest.raises(Exception, match=message):
|
with pytest.raises(Exception, match=message):
|
||||||
tables = camelot.read_pdf(filename)
|
tables = camelot.read_pdf(filename)
|
||||||
|
|
||||||
|
|
||||||
def test_bad_password():
|
def test_bad_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'
|
||||||
with pytest.raises(Exception, match=message):
|
with pytest.raises(Exception, match=message):
|
||||||
tables = camelot.read_pdf(filename, password="wrongpass")
|
tables = camelot.read_pdf(filename, password='wrongpass')
|
||||||
|
|||||||
@@ -11,50 +11,57 @@ testdir = os.path.dirname(os.path.abspath(__file__))
|
|||||||
testdir = os.path.join(testdir, "files")
|
testdir = os.path.join(testdir, "files")
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.mpl_image_compare(baseline_dir="files/baseline_plots", remove_text=True)
|
@pytest.mark.mpl_image_compare(
|
||||||
|
baseline_dir="files/baseline_plots", remove_text=True)
|
||||||
def test_text_plot():
|
def test_text_plot():
|
||||||
filename = os.path.join(testdir, "foo.pdf")
|
filename = os.path.join(testdir, "foo.pdf")
|
||||||
tables = camelot.read_pdf(filename)
|
tables = camelot.read_pdf(filename)
|
||||||
return camelot.plot(tables[0], kind="text")
|
return camelot.plot(tables[0], kind='text')
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.mpl_image_compare(baseline_dir="files/baseline_plots", remove_text=True)
|
@pytest.mark.mpl_image_compare(
|
||||||
|
baseline_dir="files/baseline_plots", remove_text=True)
|
||||||
def test_grid_plot():
|
def test_grid_plot():
|
||||||
filename = os.path.join(testdir, "foo.pdf")
|
filename = os.path.join(testdir, "foo.pdf")
|
||||||
tables = camelot.read_pdf(filename)
|
tables = camelot.read_pdf(filename)
|
||||||
return camelot.plot(tables[0], kind="grid")
|
return camelot.plot(tables[0], kind='grid')
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.mpl_image_compare(baseline_dir="files/baseline_plots", remove_text=True)
|
@pytest.mark.mpl_image_compare(
|
||||||
|
baseline_dir="files/baseline_plots", remove_text=True)
|
||||||
def test_lattice_contour_plot():
|
def test_lattice_contour_plot():
|
||||||
filename = os.path.join(testdir, "foo.pdf")
|
filename = os.path.join(testdir, "foo.pdf")
|
||||||
tables = camelot.read_pdf(filename)
|
tables = camelot.read_pdf(filename)
|
||||||
return camelot.plot(tables[0], kind="contour")
|
return camelot.plot(tables[0], kind='contour')
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.mpl_image_compare(baseline_dir="files/baseline_plots", remove_text=True)
|
@pytest.mark.mpl_image_compare(
|
||||||
|
baseline_dir="files/baseline_plots", remove_text=True)
|
||||||
def test_stream_contour_plot():
|
def test_stream_contour_plot():
|
||||||
filename = os.path.join(testdir, "tabula/12s0324.pdf")
|
filename = os.path.join(testdir, "tabula/12s0324.pdf")
|
||||||
tables = camelot.read_pdf(filename, flavor="stream")
|
tables = camelot.read_pdf(filename, flavor='stream')
|
||||||
return camelot.plot(tables[0], kind="contour")
|
return camelot.plot(tables[0], kind='contour')
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.mpl_image_compare(baseline_dir="files/baseline_plots", remove_text=True)
|
@pytest.mark.mpl_image_compare(
|
||||||
|
baseline_dir="files/baseline_plots", remove_text=True)
|
||||||
def test_line_plot():
|
def test_line_plot():
|
||||||
filename = os.path.join(testdir, "foo.pdf")
|
filename = os.path.join(testdir, "foo.pdf")
|
||||||
tables = camelot.read_pdf(filename)
|
tables = camelot.read_pdf(filename)
|
||||||
return camelot.plot(tables[0], kind="line")
|
return camelot.plot(tables[0], kind='line')
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.mpl_image_compare(baseline_dir="files/baseline_plots", remove_text=True)
|
@pytest.mark.mpl_image_compare(
|
||||||
|
baseline_dir="files/baseline_plots", remove_text=True)
|
||||||
def test_joint_plot():
|
def test_joint_plot():
|
||||||
filename = os.path.join(testdir, "foo.pdf")
|
filename = os.path.join(testdir, "foo.pdf")
|
||||||
tables = camelot.read_pdf(filename)
|
tables = camelot.read_pdf(filename)
|
||||||
return camelot.plot(tables[0], kind="joint")
|
return camelot.plot(tables[0], kind='joint')
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.mpl_image_compare(baseline_dir="files/baseline_plots", remove_text=True)
|
@pytest.mark.mpl_image_compare(
|
||||||
|
baseline_dir="files/baseline_plots", remove_text=True)
|
||||||
def test_textedge_plot():
|
def test_textedge_plot():
|
||||||
filename = os.path.join(testdir, "tabula/12s0324.pdf")
|
filename = os.path.join(testdir, "tabula/12s0324.pdf")
|
||||||
tables = camelot.read_pdf(filename, flavor="stream")
|
tables = camelot.read_pdf(filename, flavor='stream')
|
||||||
return camelot.plot(tables[0], kind="textedge")
|
return camelot.plot(tables[0], kind='textedge')
|
||||||
|
|||||||