mirror of
https://github.com/jsvine/pdfplumber.git
synced 2026-08-29 08:34:23 +08:00
319 lines
9.6 KiB
Python
319 lines
9.6 KiB
Python
#!/usr/bin/env python
|
|
import json
|
|
import logging
|
|
import os
|
|
import sys
|
|
import unittest
|
|
from io import StringIO
|
|
from subprocess import PIPE, Popen
|
|
|
|
import pytest
|
|
|
|
import pdfplumber
|
|
|
|
logging.disable(logging.ERROR)
|
|
|
|
HERE = os.path.abspath(os.path.dirname(__file__))
|
|
|
|
|
|
SCOTUS_TEXT = [
|
|
{
|
|
"type": "Div",
|
|
"children": [
|
|
{
|
|
"type": "P",
|
|
"page_number": 1,
|
|
"attributes": {
|
|
"LineHeight": 25.75,
|
|
"TextIndent": 21.625,
|
|
"O": "Layout",
|
|
},
|
|
"mcids": [1],
|
|
"text": [
|
|
"IN THE SUPREME COURT OF THE UNITED STATES - - - - - - - - - - - - "
|
|
"- - - - - x MICHAEL A. KNOWLES, : WARDEN, :"
|
|
],
|
|
},
|
|
{
|
|
"type": "P",
|
|
"page_number": 1,
|
|
"attributes": {
|
|
"LineHeight": 25.75,
|
|
"StartIndent": 86.375,
|
|
"O": "Layout",
|
|
},
|
|
"mcids": [2],
|
|
"text": [" Petitioner :"],
|
|
},
|
|
{
|
|
"type": "P",
|
|
"page_number": 1,
|
|
"attributes": {
|
|
"LineHeight": 25.75,
|
|
"TextIndent": 50.375,
|
|
"O": "Layout",
|
|
},
|
|
"mcids": [3, 4],
|
|
"text": [
|
|
" v. ",
|
|
": No. 07-1315 ALEXANDRE MIRZAYANCE. : - - - - - - - - - - - - - -"
|
|
" - - - x",
|
|
],
|
|
},
|
|
{
|
|
"type": "P",
|
|
"page_number": 1,
|
|
"attributes": {
|
|
"O": "Layout",
|
|
"SpaceAfter": 24.5,
|
|
"LineHeight": 25.75,
|
|
"StartIndent": 165.625,
|
|
"EndIndent": 57.625,
|
|
},
|
|
"mcids": [5],
|
|
"text": [" Washington, D.C. Tuesday, January 13, 2009"],
|
|
},
|
|
{
|
|
"type": "P",
|
|
"page_number": 1,
|
|
"attributes": {
|
|
"LineHeight": 25.75,
|
|
"TextIndent": 100.75,
|
|
"O": "Layout",
|
|
},
|
|
"mcids": [6],
|
|
"text": [
|
|
" The above-entitled matter came on for oral argument before the "
|
|
"Supreme Court of the United States at 1:01 p.m. APPEARANCES: "
|
|
"STEVEN E. MERCER, ESQ., Deputy Attorney General, Los"
|
|
],
|
|
},
|
|
{
|
|
"type": "P",
|
|
"page_number": 1,
|
|
"attributes": {
|
|
"O": "Layout",
|
|
"SpaceAfter": 179.125,
|
|
"LineHeight": 25.75,
|
|
"TextIndent": 21.625,
|
|
"EndIndent": 50.375,
|
|
"TextAlign": "None",
|
|
},
|
|
"mcids": [7],
|
|
"text": [
|
|
" Angeles, Cal.; on behalf of the Petitioner. CHARLES M. SEVILLA, "
|
|
"ESQ., San Diego, Cal.; on behalf of the Respondent. "
|
|
],
|
|
},
|
|
{
|
|
"type": "P",
|
|
"page_number": 1,
|
|
"attributes": {"O": "Layout", "TextAlign": "Center", "SpaceAfter": 8.5},
|
|
"mcids": [8],
|
|
"text": ["1\n"],
|
|
},
|
|
{
|
|
"type": "P",
|
|
"page_number": 1,
|
|
"attributes": {"O": "Layout", "TextAlign": "Center"},
|
|
"mcids": [9],
|
|
"text": ["Alderson Reporting Company "],
|
|
},
|
|
],
|
|
}
|
|
]
|
|
|
|
|
|
def run(cmd):
|
|
return Popen(cmd, stdout=PIPE).communicate()[0]
|
|
|
|
|
|
class Test(unittest.TestCase):
|
|
@classmethod
|
|
def setup_class(self):
|
|
self.path = os.path.join(HERE, "pdfs/pdffill-demo.pdf")
|
|
self.pdf = pdfplumber.open(self.path, pages=[1, 2, 5])
|
|
|
|
@classmethod
|
|
def teardown_class(self):
|
|
self.pdf.close()
|
|
|
|
def test_json(self):
|
|
c = json.loads(self.pdf.to_json())
|
|
assert (
|
|
c["pages"][0]["rects"][0]["bottom"] == self.pdf.pages[0].rects[0]["bottom"]
|
|
)
|
|
|
|
def test_json_attr_filter(self):
|
|
c = json.loads(self.pdf.to_json(include_attrs=["page_number"]))
|
|
assert list(c["pages"][0]["rects"][0].keys()) == ["object_type", "page_number"]
|
|
|
|
with pytest.raises(ValueError):
|
|
self.pdf.to_json(include_attrs=["page_number"], exclude_attrs=["bottom"])
|
|
|
|
with pytest.raises(ValueError):
|
|
self.pdf.to_json(exclude_attrs=["object_type"])
|
|
|
|
def test_json_all_types(self):
|
|
c = json.loads(self.pdf.to_json(object_types=None))
|
|
found_types = c["pages"][0].keys()
|
|
assert "chars" in found_types
|
|
assert "lines" in found_types
|
|
assert "rects" in found_types
|
|
assert "images" in found_types
|
|
assert "curves" in c["pages"][2].keys()
|
|
|
|
def test_single_pages(self):
|
|
c = json.loads(self.pdf.pages[0].to_json())
|
|
assert c["rects"][0]["bottom"] == self.pdf.pages[0].rects[0]["bottom"]
|
|
|
|
def test_additional_attr_types(self):
|
|
path = os.path.join(HERE, "pdfs/issue-67-example.pdf")
|
|
with pdfplumber.open(path, pages=[1]) as pdf:
|
|
c = json.loads(pdf.to_json())
|
|
assert len(c["pages"][0]["images"])
|
|
|
|
def test_csv(self):
|
|
c = self.pdf.to_csv(precision=3)
|
|
assert c.split("\r\n")[9] == (
|
|
"char,1,45.83,58.826,656.82,674.82,117.18,117.18,135.18,12.996,"
|
|
"18.0,12.996,,,,,,,TimesNewRomanPSMT,,,"
|
|
'"(1.0, 0.0, 0.0, 1.0, 45.83, 660.69)"'
|
|
',,,DeviceRGB,"(0.0, 0.0, 0.0)",,,18.0,,,,"(0,)",,Y,,1,'
|
|
)
|
|
|
|
io = StringIO()
|
|
self.pdf.to_csv(io, precision=3)
|
|
io.seek(0)
|
|
c_from_io = io.read()
|
|
assert c == c_from_io
|
|
|
|
def test_csv_all_types(self):
|
|
c = self.pdf.to_csv(object_types=None)
|
|
assert c.split("\r\n")[1].split(",")[0] == "line"
|
|
|
|
def test_cli_help(self):
|
|
res = run([sys.executable, "-m", "pdfplumber.cli"])
|
|
assert b"usage:" in res
|
|
|
|
def test_cli_structure(self):
|
|
res = run([sys.executable, "-m", "pdfplumber.cli", self.path, "--structure"])
|
|
c = json.loads(res)
|
|
# lol no structure
|
|
assert c == []
|
|
|
|
def test_cli_structure_text(self):
|
|
path = os.path.join(HERE, "pdfs/scotus-transcript-p1.pdf")
|
|
res = run([sys.executable, "-m", "pdfplumber.cli", path, "--structure-text"])
|
|
c = json.loads(res)
|
|
assert c == SCOTUS_TEXT
|
|
|
|
def test_cli_json(self):
|
|
res = run(
|
|
[
|
|
sys.executable,
|
|
"-m",
|
|
"pdfplumber.cli",
|
|
self.path,
|
|
"--format",
|
|
"json",
|
|
"--pages",
|
|
"1-2",
|
|
"5",
|
|
"--indent",
|
|
"2",
|
|
]
|
|
)
|
|
|
|
c = json.loads(res)
|
|
assert c["pages"][0]["page_number"] == 1
|
|
assert c["pages"][1]["page_number"] == 2
|
|
assert c["pages"][2]["page_number"] == 5
|
|
assert c["pages"][0]["rects"][0]["bottom"] == float(
|
|
self.pdf.pages[0].rects[0]["bottom"]
|
|
)
|
|
|
|
def test_cli_csv(self):
|
|
res = run(
|
|
[
|
|
sys.executable,
|
|
"-m",
|
|
"pdfplumber.cli",
|
|
self.path,
|
|
"--format",
|
|
"csv",
|
|
"--precision",
|
|
"3",
|
|
]
|
|
)
|
|
|
|
assert res.decode("utf-8").split("\r\n")[9] == (
|
|
"char,1,45.83,58.826,656.82,674.82,117.18,117.18,135.18,12.996,"
|
|
"18.0,12.996,,,,,,,TimesNewRomanPSMT,,,"
|
|
'"(1.0, 0.0, 0.0, 1.0, 45.83, 660.69)"'
|
|
',,,DeviceRGB,"(0.0, 0.0, 0.0)",,,18.0,,,,"(0,)",,Y,,1,'
|
|
)
|
|
|
|
def test_cli_csv_exclude(self):
|
|
res = run(
|
|
[
|
|
sys.executable,
|
|
"-m",
|
|
"pdfplumber.cli",
|
|
self.path,
|
|
"--format",
|
|
"csv",
|
|
"--precision",
|
|
"3",
|
|
"--exclude-attrs",
|
|
"matrix",
|
|
"mcid",
|
|
"ncs",
|
|
]
|
|
)
|
|
|
|
assert res.decode("utf-8").split("\r\n")[9] == (
|
|
"char,1,45.83,58.826,656.82,674.82,117.18,117.18,135.18,12.996,"
|
|
"18.0,12.996,,,,,,,TimesNewRomanPSMT,"
|
|
',,,"(0.0, 0.0, 0.0)",,,18.0,,,,"(0,)",,Y,,1,'
|
|
)
|
|
|
|
def test_cli_csv_include(self):
|
|
res = run(
|
|
[
|
|
sys.executable,
|
|
"-m",
|
|
"pdfplumber.cli",
|
|
self.path,
|
|
"--format",
|
|
"csv",
|
|
"--precision",
|
|
"3",
|
|
"--include-attrs",
|
|
"page_number",
|
|
]
|
|
)
|
|
|
|
assert res.decode("utf-8").split("\r\n")[9] == ("char,1")
|
|
|
|
def test_cli_text(self):
|
|
path = os.path.join(HERE, "pdfs/scotus-transcript-p1.pdf")
|
|
res = run(
|
|
[
|
|
sys.executable,
|
|
"-m",
|
|
"pdfplumber.cli",
|
|
path,
|
|
"--format",
|
|
"text",
|
|
]
|
|
)
|
|
|
|
target_path = os.path.join(HERE, "comparisons/scotus-transcript-p1.txt")
|
|
target = open(target_path).read()
|
|
assert res.decode("utf-8") == target
|
|
|
|
def test_page_to_dict(self):
|
|
x = self.pdf.pages[0].to_dict(object_types=["char"])
|
|
assert len(x["chars"]) == len(self.pdf.pages[0].chars)
|