Skip to content

Commit fc94271

Browse files
Lucas-CCimon Lucas (LCM)
and
Cimon Lucas (LCM)
authored
Harmonizing typer usage (#74)
Co-authored-by: Cimon Lucas (LCM) <[email protected]>
1 parent 1d4e256 commit fc94271

File tree

3 files changed

+114
-69
lines changed

3 files changed

+114
-69
lines changed

pdfly/cli.py

+31-38
Original file line numberDiff line numberDiff line change
@@ -52,11 +52,8 @@ def extract_images(
5252
pdf: Annotated[
5353
Path,
5454
typer.Argument(
55-
exists=True,
56-
file_okay=True,
5755
dir_okay=False,
58-
writable=False,
59-
readable=True,
56+
exists=True,
6057
resolve_path=True,
6158
),
6259
]
@@ -69,11 +66,8 @@ def up2(
6966
pdf: Annotated[
7067
Path,
7168
typer.Argument(
72-
exists=True,
73-
file_okay=True,
7469
dir_okay=False,
75-
writable=False,
76-
readable=True,
70+
exists=True,
7771
resolve_path=True,
7872
),
7973
],
@@ -87,11 +81,8 @@ def cat(
8781
filename: Annotated[
8882
Path,
8983
typer.Argument(
90-
exists=True,
91-
file_okay=True,
9284
dir_okay=False,
93-
writable=False,
94-
readable=True,
85+
exists=True,
9586
resolve_path=True,
9687
),
9788
],
@@ -111,11 +102,8 @@ def rm(
111102
filename: Annotated[
112103
Path,
113104
typer.Argument(
114-
exists=True,
115-
file_okay=True,
116105
dir_okay=False,
117-
writable=False,
118-
readable=True,
106+
exists=True,
119107
resolve_path=True,
120108
),
121109
],
@@ -135,11 +123,8 @@ def metadata(
135123
pdf: Annotated[
136124
Path,
137125
typer.Argument(
138-
exists=True,
139-
file_okay=True,
140126
dir_okay=False,
141-
writable=False,
142-
readable=True,
127+
exists=True,
143128
resolve_path=True,
144129
),
145130
],
@@ -159,11 +144,8 @@ def pagemeta(
159144
pdf: Annotated[
160145
Path,
161146
typer.Argument(
162-
exists=True,
163-
file_okay=True,
164147
dir_okay=False,
165-
writable=False,
166-
readable=True,
148+
exists=True,
167149
resolve_path=True,
168150
),
169151
],
@@ -188,11 +170,8 @@ def extract_text(
188170
pdf: Annotated[
189171
Path,
190172
typer.Argument(
191-
exists=True,
192-
file_okay=True,
193173
dir_okay=False,
194-
writable=False,
195-
readable=True,
174+
exists=True,
196175
resolve_path=True,
197176
),
198177
]
@@ -202,19 +181,16 @@ def extract_text(
202181

203182
reader = PdfReader(str(pdf))
204183
for page in reader.pages:
205-
print(page.extract_text())
184+
typer.echo(page.extract_text())
206185

207186

208187
@entry_point.command(name="compress", help=pdfly.compress.__doc__) # type: ignore[misc]
209188
def compress(
210189
pdf: Annotated[
211190
Path,
212191
typer.Argument(
213-
exists=True,
214-
file_okay=True,
215192
dir_okay=False,
216-
writable=False,
217-
readable=True,
193+
exists=True,
218194
resolve_path=True,
219195
),
220196
],
@@ -231,7 +207,14 @@ def compress(
231207

232208
@entry_point.command(name="update-offsets", help=pdfly.update_offsets.__doc__) # type: ignore[misc]
233209
def update_offsets(
234-
file_in: Path,
210+
file_in: Annotated[
211+
Path,
212+
typer.Argument(
213+
dir_okay=False,
214+
exists=True,
215+
resolve_path=True,
216+
),
217+
],
235218
file_out: Path,
236219
encoding: str = typer.Option(
237220
"ISO-8859-1",
@@ -246,15 +229,25 @@ def update_offsets(
246229

247230
@entry_point.command(name="x2pdf", help=pdfly.x2pdf.__doc__) # type: ignore[misc]
248231
def x2pdf(
249-
x: List[Path],
232+
x: List[
233+
Annotated[
234+
Path,
235+
typer.Argument(
236+
dir_okay=False,
237+
exists=True,
238+
resolve_path=True,
239+
),
240+
]
241+
],
250242
output: Annotated[
251243
Path,
252244
typer.Option(
253245
"-o",
254246
"--output",
255-
exists=False,
256247
writable=True,
257248
),
258249
],
259-
) -> int:
260-
return pdfly.x2pdf.main(x, output)
250+
) -> None:
251+
exit_code = pdfly.x2pdf.main(x, output)
252+
if exit_code:
253+
raise typer.Exit(code=exit_code)

pdfly/x2pdf.py

+26-29
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
"""Convert one or more files to PDF. Each file is a page."""
22

3+
from io import BytesIO
34
from pathlib import Path
45
from typing import List
56

67
from fpdf import FPDF
78
from PIL import Image
9+
from pypdf import PdfReader, PdfWriter
810
from rich.console import Console
911

1012

@@ -16,39 +18,34 @@ def px_to_mm(px: float) -> float:
1618
return mm
1719

1820

19-
def image_to_pdf(pdf: FPDF, x: Path) -> None:
20-
cover = Image.open(x)
21-
width: float
22-
height: float
23-
width, height = cover.size
24-
cover.close()
21+
def image_to_pdf(filepath: Path) -> BytesIO:
22+
with Image.open(filepath) as cover:
23+
width, height = cover.size
2524
width, height = px_to_mm(width), px_to_mm(height)
26-
25+
pdf = FPDF(unit="mm")
2726
pdf.add_page(format=(width, height))
28-
pdf.image(x, x=0, y=0)
27+
pdf.image(filepath, x=0, y=0)
28+
return BytesIO(pdf.output())
2929

3030

31-
def main(xs: List[Path], output: Path) -> int:
31+
def main(in_filepaths: List[Path], out_filepath: Path) -> int:
3232
console = Console()
33-
for x in xs:
34-
path_str = str(x).lower()
35-
if path_str.endswith(("doc", "docx", "odt")):
36-
console.print("[red]Error: Cannot convert Word documents to PDF")
37-
return 1
38-
if not x.exists():
39-
console.print(f"[red]Error: File '{x}' does not exist.")
40-
return 2
41-
if output.exists():
42-
console.print(f"[red]Error: Output file '{output}' exist.")
43-
return 3
44-
pdf = FPDF(
45-
unit="mm",
46-
)
47-
for x in xs:
48-
path_str = str(x).lower()
33+
exit_code = 0
34+
writer = PdfWriter()
35+
for filepath in in_filepaths:
36+
if filepath.name.endswith(".pdf"):
37+
for page in PdfReader(filepath).pages:
38+
writer.insert_page(page)
39+
continue
4940
try:
50-
image_to_pdf(pdf, x)
41+
pdf_bytes = image_to_pdf(filepath)
42+
new_page = PdfReader(pdf_bytes).pages[0]
43+
writer.insert_page(new_page)
5144
except Exception:
52-
console.print(f"[red]Error: Could not convert '{x}' to a PDF.")
53-
pdf.output(str(output))
54-
return 0
45+
console.print(
46+
f"[red]Error: Could not convert '{filepath}' to a PDF."
47+
)
48+
console.print_exception(extra_lines=1, max_frames=1)
49+
exit_code += 1
50+
writer.write(out_filepath)
51+
return exit_code

tests/test_x2pdf.py

+57-2
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,9 @@
99
from .conftest import run_cli
1010

1111

12-
def test_x2pdf(capsys, tmp_path: Path) -> None:
12+
def test_x2pdf_succeed_to_convert_jpg(capsys, tmp_path: Path):
1313
# Arrange
1414
output = tmp_path / "out.pdf"
15-
assert not output.exists()
1615

1716
# Act
1817
exit_code = run_cli(
@@ -29,3 +28,59 @@ def test_x2pdf(capsys, tmp_path: Path) -> None:
2928
assert exit_code == 0, captured
3029
assert captured.out == ""
3130
assert output.exists()
31+
32+
33+
def test_x2pdf_succeed_to_embed_pdfs(capsys, tmp_path: Path):
34+
# Arrange
35+
output = tmp_path / "out.pdf"
36+
37+
# Act
38+
exit_code = run_cli(
39+
[
40+
"x2pdf",
41+
"sample-files/001-trivial/minimal-document.pdf",
42+
"sample-files/002-trivial-libre-office-writer/002-trivial-libre-office-writer.pdf",
43+
"--output",
44+
str(output),
45+
]
46+
)
47+
48+
# Assert
49+
captured = capsys.readouterr()
50+
assert exit_code == 0, captured
51+
assert captured.out == ""
52+
assert output.exists()
53+
54+
55+
def test_x2pdf_fail_to_open_file(capsys, tmp_path: Path):
56+
# Arrange & Act
57+
exit_code = run_cli(
58+
[
59+
"x2pdf",
60+
"NonExistingFile",
61+
"--output",
62+
str(tmp_path / "out.pdf"),
63+
]
64+
)
65+
66+
# Assert
67+
captured = capsys.readouterr()
68+
assert exit_code == 1, captured
69+
assert "No such file or directory" in captured.out
70+
71+
72+
def test_x2pdf_fail_to_convert(capsys, tmp_path: Path):
73+
# Arrange & Act
74+
exit_code = run_cli(
75+
[
76+
"x2pdf",
77+
"README.md",
78+
"--output",
79+
str(tmp_path / "out.pdf"),
80+
]
81+
)
82+
83+
# Assert
84+
captured = capsys.readouterr()
85+
assert exit_code == 1, captured
86+
assert "Error: Could not convert 'README.md' to a PDF" in captured.out

0 commit comments

Comments
 (0)