#!/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)