Skip to content

Commit ebd6858

Browse files
committed
add CLI test and reformat
1 parent 2421153 commit ebd6858

File tree

12 files changed

+365
-132
lines changed

12 files changed

+365
-132
lines changed

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ license = "Apache-2.0"
77
readme = "README.rst"
88

99
[tool.poetry.scripts]
10-
scan-pdf = 'scan_pdf.console:run'
10+
scan-pdf = 'scan_pdf.cli:run'
1111

1212
[tool.poetry.dependencies]
1313
python = "^3.9"

scan_pdf/cli.py

Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
#!/usr/bin/env python
2+
3+
from __future__ import print_function
4+
5+
import argparse
6+
import logging
7+
import os
8+
import shutil
9+
import stat
10+
11+
from scan_pdf import Scanner, Converter, Combiner, TempDir
12+
13+
14+
def parse_args() -> argparse.Namespace:
15+
parser = argparse.ArgumentParser(
16+
description="Produce PDF from Scanner with document-feeder"
17+
)
18+
parser.add_argument(
19+
"output_file_name", type=str, nargs=1, help="name of the produced output file"
20+
)
21+
parser.add_argument(
22+
"--device",
23+
dest="device",
24+
type=str,
25+
default=None,
26+
help="optional device locator",
27+
)
28+
parser.add_argument(
29+
"--resolution",
30+
dest="resolution",
31+
type=int,
32+
default=300,
33+
help="scan resolution DPI (default: 300)",
34+
)
35+
parser.add_argument(
36+
"--flatbed",
37+
dest="flatbed",
38+
action="store_true",
39+
help="scan only one page from flatbed glass",
40+
)
41+
parser.add_argument(
42+
"-d", dest="debug", action="store_true", help="enable debug output"
43+
)
44+
parser.add_argument(
45+
"--duplex",
46+
dest="duplex",
47+
action="store_true",
48+
help="scan both sides of document",
49+
)
50+
parser.add_argument(
51+
"--color-mode",
52+
dest="color_mode",
53+
action="store",
54+
choices=["bw", "gray", "color"],
55+
default="gray",
56+
help="default: gray",
57+
)
58+
parser.add_argument(
59+
"--threshold",
60+
dest="threshold",
61+
default=None,
62+
action="store",
63+
help="disabled by default",
64+
)
65+
parser.add_argument("--color-depth", dest="color_depth", default=4)
66+
parser.add_argument(
67+
"--paper-format",
68+
dest="paper_format",
69+
action="store",
70+
choices=Scanner.paper_formats.keys(),
71+
default="A4",
72+
help="default: A4",
73+
)
74+
parser.add_argument(
75+
"--paper-left", dest="paper_left", default=0, help="override left offset"
76+
)
77+
parser.add_argument(
78+
"--paper-top", dest="paper_top", default=0, help="override top offset"
79+
)
80+
parser.add_argument(
81+
"--paper-height", dest="paper_height", default=0, help="override paper height"
82+
)
83+
parser.add_argument(
84+
"--paper-width", dest="paper_width", default=0, help="override paper width"
85+
)
86+
options = parser.parse_args()
87+
return options
88+
89+
90+
def run() -> None:
91+
options = parse_args()
92+
93+
logging.basicConfig(level=logging.DEBUG if options.debug else logging.INFO)
94+
logger = logging.getLogger(__name__)
95+
96+
origin_dir = os.getcwd()
97+
98+
with TempDir() as temp_dir:
99+
os.chmod(temp_dir, stat.S_IRWXU)
100+
os.chdir(temp_dir)
101+
102+
output_file_name = options.output_file_name[0]
103+
104+
scanner = Scanner(options)
105+
106+
output_basenames = scanner.get_page_file_basenames()
107+
108+
if len(output_basenames) > 0:
109+
converter = Converter(options)
110+
for page_number, output_basename in enumerate(output_basenames, start=1):
111+
converter.convert(
112+
output_basename,
113+
scanner.page_file_suffix,
114+
options.duplex and page_number % 2 == 0,
115+
)
116+
117+
combiner = Combiner(options)
118+
combiner.combine(
119+
[
120+
output_basename + converter.page_file_suffix
121+
for output_basename in output_basenames
122+
]
123+
)
124+
125+
os.chdir(origin_dir)
126+
shutil.move(
127+
os.path.join(temp_dir, os.path.basename(output_file_name)),
128+
output_file_name,
129+
)
130+
else:
131+
logger.warn("no pages found")

scan_pdf/console.py

Lines changed: 0 additions & 71 deletions
This file was deleted.

scan_pdf/convert.py

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -8,21 +8,23 @@
88

99

1010
class Converter:
11-
page_file_suffix = '.pdf'
11+
page_file_suffix = ".pdf"
1212

1313
def __init__(self, options: argparse.Namespace):
1414
self.options = options
1515

16-
def convert(self, source_basename: str, source_suffix: str, flip: bool = False) -> int:
17-
logger.info('convert %s', source_basename)
18-
args = ['convert']
16+
def convert(
17+
self, source_basename: str, source_suffix: str, flip: bool = False
18+
) -> int:
19+
logger.info("convert %s", source_basename)
20+
args = ["convert"]
1921

20-
color_depth = self.options.color_depth if self.options.color_mode != 'bw' else 1
21-
args += ['-depth', str(color_depth)]
22-
args += ['-density', str(self.options.resolution)]
23-
args += ['-compress', 'zip']
22+
color_depth = self.options.color_depth if self.options.color_mode != "bw" else 1
23+
args += ["-depth", str(color_depth)]
24+
args += ["-density", str(self.options.resolution)]
25+
args += ["-compress", "zip"]
2426
if flip:
25-
args += ['-rotate', '180']
27+
args += ["-rotate", "180"]
2628
target_file = source_basename + self.page_file_suffix
2729
args += [source_basename + source_suffix, target_file]
2830

scan_pdf/scan.py

Lines changed: 46 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -19,50 +19,71 @@ def __init__(self, left: int, width: int, top: int, height: int):
1919

2020
class Scanner:
2121
paper_formats = {
22-
'A3': PaperFormat(0, 297, 0, 420),
23-
'A4': PaperFormat(0, 210, 0, 297),
24-
'A5': PaperFormat(0, 149, 0, 218),
22+
"A3": PaperFormat(0, 297, 0, 420),
23+
"A4": PaperFormat(0, 210, 0, 297),
24+
"A5": PaperFormat(0, 149, 0, 218),
2525
}
26-
page_file_suffix = '.pnm'
26+
page_file_suffix = ".pnm"
2727

2828
def __init__(self, options: argparse.Namespace):
29-
30-
args = ['scanimage', '-b', '--format=pnm']
29+
args = ["scanimage", "-b", "--format=pnm"]
3130

3231
if options.device:
3332
args += ["-d", options.device]
3433

35-
color_mode = 'Gray'
36-
if options.color_mode == 'bw':
37-
color_mode = 'Lineart'
38-
elif options.color_mode == 'color':
39-
color_mode = 'Color'
40-
args += ['--mode', color_mode]
34+
color_mode = "Gray"
35+
if options.color_mode == "bw":
36+
color_mode = "Lineart"
37+
elif options.color_mode == "color":
38+
color_mode = "Color"
39+
args += ["--mode", color_mode]
4140

4241
if options.flatbed:
43-
args += ['--source', 'Flatbed', '--batch-count', '1']
42+
args += ["--source", "Flatbed", "--batch-count", "1"]
4443
else:
45-
args += ['--source', 'Automatic Document Feeder']
46-
args += ['--adf-mode', 'Duplex' if options.duplex else 'Simplex']
44+
args += ["--source", "Automatic Document Feeder"]
45+
args += ["--adf-mode", "Duplex" if options.duplex else "Simplex"]
4746

48-
args += ['--resolution', str(options.resolution)]
47+
args += ["--resolution", str(options.resolution)]
4948

5049
if options.threshold:
51-
args += ['--halftoning', 'None', '--threshold', str(options.threshold)]
52-
53-
paper_format = self.paper_formats.get(options.paper_format, self.paper_formats['A4'])
54-
55-
args += ['-l', str(int(options.paper_left if options.paper_left else paper_format.left))]
56-
args += ['-x', str(int(options.paper_width if options.paper_width else paper_format.width))]
57-
args += ['-t', str(int(options.paper_top if options.paper_top else paper_format.top))]
58-
args += ['-y', str(int(options.paper_height if options.paper_height else paper_format.height))]
50+
args += ["--halftoning", "None", "--threshold", str(options.threshold)]
51+
52+
paper_format = self.paper_formats.get(
53+
options.paper_format, self.paper_formats["A4"]
54+
)
55+
56+
args += [
57+
"-l",
58+
str(int(options.paper_left if options.paper_left else paper_format.left)),
59+
]
60+
args += [
61+
"-x",
62+
str(
63+
int(options.paper_width if options.paper_width else paper_format.width)
64+
),
65+
]
66+
args += [
67+
"-t",
68+
str(int(options.paper_top if options.paper_top else paper_format.top)),
69+
]
70+
args += [
71+
"-y",
72+
str(
73+
int(
74+
options.paper_height
75+
if options.paper_height
76+
else paper_format.height
77+
)
78+
),
79+
]
5980

6081
logger.debug("call %s", " ".join(args))
6182
retval = subprocess.call(args)
6283
logger.debug("call retured %d", retval)
6384

6485
def get_page_file_basenames(self) -> List[str]:
65-
output_files = glob.glob('out*' + self.page_file_suffix)
86+
output_files = glob.glob("out*" + self.page_file_suffix)
6687
output_files = sorted(output_files, key=cmp_to_key(self.compare_output_names))
6788

6889
return [os.path.splitext(output_file)[0] for output_file in output_files]

scan_pdf/util.py

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,18 @@
77

88
class TempDir:
99
def __init__(self) -> None:
10-
self.temp_dir :Optional[str] = None
10+
self.temp_dir: Optional[str] = None
1111

1212
def __enter__(self) -> str:
13-
self.temp_dir = tempfile.mkdtemp(prefix='scanpdf_')
13+
self.temp_dir = tempfile.mkdtemp(prefix="scanpdf_")
1414
return self.temp_dir
1515

1616
# noinspection PyUnusedLocal
17-
def __exit__(self, type_value: type[BaseException], value: BaseException, traceback: TracebackType) -> None:
17+
def __exit__(
18+
self,
19+
type_value: type[BaseException],
20+
value: BaseException,
21+
traceback: TracebackType,
22+
) -> None:
1823
if self.temp_dir is not None:
1924
shutil.rmtree(self.temp_dir, ignore_errors=True)

tests/__init__.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
import os
2+
from argparse import Namespace
23

34

4-
class Options(object):
5+
class Options(Namespace):
56
pass
67

78

89
def touch(file_name: str):
9-
with open(file_name, 'a'):
10+
with open(file_name, "a"):
1011
os.utime(file_name, None)

0 commit comments

Comments
 (0)