docome 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- cli/__init__.py +0 -0
- cli/docome/__init__.py +0 -0
- cli/docome/commands/__init__.py +3 -0
- cli/docome/commands/convert.py +335 -0
- cli/docome/commands/edit.py +348 -0
- cli/docome/commands/ocr.py +65 -0
- cli/docome/commands/pdf.py +232 -0
- cli/docome/commands/signature.py +142 -0
- cli/docome/commands/thumbnail.py +116 -0
- cli/docome/gui.py +289 -0
- cli/docome/main.py +311 -0
- docome-0.1.0.dist-info/METADATA +28 -0
- docome-0.1.0.dist-info/RECORD +17 -0
- docome-0.1.0.dist-info/WHEEL +5 -0
- docome-0.1.0.dist-info/entry_points.txt +2 -0
- docome-0.1.0.dist-info/licenses/LICENSE +21 -0
- docome-0.1.0.dist-info/top_level.txt +1 -0
cli/__init__.py
ADDED
|
File without changes
|
cli/docome/__init__.py
ADDED
|
File without changes
|
|
@@ -0,0 +1,335 @@
|
|
|
1
|
+
import json
|
|
2
|
+
import sys
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
from typing import Optional, List, Tuple
|
|
5
|
+
|
|
6
|
+
import typer
|
|
7
|
+
from rich.console import Console
|
|
8
|
+
from rich.table import Table
|
|
9
|
+
|
|
10
|
+
sys.path.insert(0, str(Path(__file__).parent.parent.parent.parent))
|
|
11
|
+
|
|
12
|
+
from packages.conversion_engine import ConversionEngine, evaluate, pdf_to_docx_hq
|
|
13
|
+
from packages.image_engine import ImageEngine
|
|
14
|
+
|
|
15
|
+
# Main CLI app - this is added as a subcommand to the main app
|
|
16
|
+
convert_app = typer.Typer(help="Document and image conversion operations")
|
|
17
|
+
console = Console()
|
|
18
|
+
|
|
19
|
+
# Image engine instance for image conversions
|
|
20
|
+
image_engine = ImageEngine()
|
|
21
|
+
|
|
22
|
+
# Extension to conversion type mapper
|
|
23
|
+
CONVERSION_MAP = {
|
|
24
|
+
# Document conversions (PDF-based)
|
|
25
|
+
(".pdf", ".docx"): "pdf2docx",
|
|
26
|
+
(".pdf", ".docx-hq"): "pdf2docx-hq",
|
|
27
|
+
(".pdf", ".xlsx"): "pdf2xlsx",
|
|
28
|
+
(".pdf", ".pptx"): "pdf2pptx",
|
|
29
|
+
(".pdf", ".txt"): "pdf2txt",
|
|
30
|
+
(".pdf", ".md"): "pdf2md",
|
|
31
|
+
# Reverse document conversions
|
|
32
|
+
(".docx", ".pdf"): "docx2pdf",
|
|
33
|
+
# Image conversions (format changes)
|
|
34
|
+
(".png", ".jpg"): "convert-img",
|
|
35
|
+
(".png", ".jpeg"): "convert-img",
|
|
36
|
+
(".png", ".webp"): "convert-img",
|
|
37
|
+
(".png", ".bmp"): "convert-img",
|
|
38
|
+
(".png", ".tiff"): "convert-img",
|
|
39
|
+
(".jpg", ".png"): "convert-img",
|
|
40
|
+
(".jpg", ".webp"): "convert-img",
|
|
41
|
+
(".jpg", ".bmp"): "convert-img",
|
|
42
|
+
(".jpg", ".tiff"): "convert-img",
|
|
43
|
+
(".webp", ".png"): "convert-img",
|
|
44
|
+
(".webp", ".jpg"): "convert-img",
|
|
45
|
+
(".bmp", ".png"): "convert-img",
|
|
46
|
+
(".bmp", ".jpg"): "convert-img",
|
|
47
|
+
# Image to PDF
|
|
48
|
+
(".png", ".pdf"): "img2pdf",
|
|
49
|
+
(".jpg", ".pdf"): "img2pdf",
|
|
50
|
+
(".jpeg", ".pdf"): "img2pdf",
|
|
51
|
+
(".bmp", ".pdf"): "img2pdf",
|
|
52
|
+
# Compression
|
|
53
|
+
(".jpg", "compress-img"): "compress-img",
|
|
54
|
+
(".png", "compress-img"): "compress-img",
|
|
55
|
+
# Resize
|
|
56
|
+
(".jpg", "resize-img"): "resize-img",
|
|
57
|
+
(".png", "resize-img"): "resize-img",
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def parse_source_dest(source_str: str, dest_str: str) -> Tuple[List[str], List[str]]:
|
|
62
|
+
"""Parse source and destination strings, handling comma-separated lists."""
|
|
63
|
+
sources = [s.strip() for s in source_str.split(",")]
|
|
64
|
+
dests = [d.strip() for d in dest_str.split(",")]
|
|
65
|
+
return sources, dests
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def get_extension(filepath: str) -> str:
|
|
69
|
+
"""Get file extension from path."""
|
|
70
|
+
return Path(filepath).suffix.lower()
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def detect_conversion_type(source: str, dest: str) -> Optional[str]:
|
|
74
|
+
"""Auto-detect conversion type from file extensions."""
|
|
75
|
+
src_ext = get_extension(source)
|
|
76
|
+
dst_ext = get_extension(dest)
|
|
77
|
+
|
|
78
|
+
# Direct extension mapping from CONVERSION_MAP
|
|
79
|
+
key = (src_ext, dst_ext)
|
|
80
|
+
if key in CONVERSION_MAP:
|
|
81
|
+
return CONVERSION_MAP[key]
|
|
82
|
+
|
|
83
|
+
# PDF to document formats (explicit checks after map)
|
|
84
|
+
if src_ext == ".pdf":
|
|
85
|
+
if dst_ext == ".docx":
|
|
86
|
+
return "pdf2docx"
|
|
87
|
+
if dst_ext == ".docx-hq":
|
|
88
|
+
return "pdf2docx-hq"
|
|
89
|
+
if dst_ext == ".xlsx":
|
|
90
|
+
return "pdf2xlsx"
|
|
91
|
+
if dst_ext == ".pptx":
|
|
92
|
+
return "pdf2pptx"
|
|
93
|
+
if dst_ext == ".txt":
|
|
94
|
+
return "pdf2txt"
|
|
95
|
+
if dst_ext == ".md":
|
|
96
|
+
return "pdf2md"
|
|
97
|
+
|
|
98
|
+
# Image to PDF
|
|
99
|
+
if dst_ext == ".pdf" and src_ext in (".png", ".jpg", ".jpeg", ".bmp", ".webp"):
|
|
100
|
+
return "img2pdf"
|
|
101
|
+
|
|
102
|
+
# Image format conversions (between image formats)
|
|
103
|
+
if src_ext in (".png", ".jpg", ".jpeg", ".webp", ".bmp") and dst_ext in (".png", ".jpg", ".jpeg", ".webp", ".bmp"):
|
|
104
|
+
return "convert-img"
|
|
105
|
+
|
|
106
|
+
# Fallback: anything to PDF for images already caught above
|
|
107
|
+
# Fallback: unsupported
|
|
108
|
+
return None
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
@convert_app.command("convert-files")
|
|
112
|
+
def convert_files(
|
|
113
|
+
source_and_dest: str = typer.Argument(..., help="Input and output file paths, e.g. 'test.png into output.pdf'"),
|
|
114
|
+
):
|
|
115
|
+
"""Convert files using natural language format.
|
|
116
|
+
|
|
117
|
+
Examples:
|
|
118
|
+
docome convert-files test.png into output.pdf
|
|
119
|
+
docome convert-files test.png,test2.png into output1.pdf,output2.pdf
|
|
120
|
+
"""
|
|
121
|
+
console.print(f"[blue]Processing conversion...[/blue]")
|
|
122
|
+
|
|
123
|
+
# Parse "source into dest" format
|
|
124
|
+
if "into" in source_and_dest.lower():
|
|
125
|
+
parts = source_and_dest.lower().split("into", 1)
|
|
126
|
+
if len(parts) == 2:
|
|
127
|
+
source_str = parts[0].strip()
|
|
128
|
+
dest_str = parts[1].strip()
|
|
129
|
+
else:
|
|
130
|
+
source_str = source_and_dest.strip()
|
|
131
|
+
dest_str = ""
|
|
132
|
+
else:
|
|
133
|
+
# No "into" keyword - assume single file conversion or use existing logic
|
|
134
|
+
source_str = source_and_dest.strip()
|
|
135
|
+
dest_str = ""
|
|
136
|
+
|
|
137
|
+
sources, dests = parse_source_dest(source_str, dest_str)
|
|
138
|
+
|
|
139
|
+
if len(sources) != len(dests):
|
|
140
|
+
console.print(
|
|
141
|
+
f"[red]Error: Number of source files ({len(sources)}) must match number of destination files ({len(dests)}).[/red]"
|
|
142
|
+
)
|
|
143
|
+
raise typer.Exit(code=1)
|
|
144
|
+
|
|
145
|
+
# Validate that source files exist
|
|
146
|
+
for src in sources:
|
|
147
|
+
if not Path(src).exists():
|
|
148
|
+
console.print(f"[red]Error: Source file not found: {src}[/red]")
|
|
149
|
+
raise typer.Exit(code=1)
|
|
150
|
+
|
|
151
|
+
engine = ConversionEngine()
|
|
152
|
+
image_engine_local = ImageEngine()
|
|
153
|
+
|
|
154
|
+
for src, dst in zip(sources, dests):
|
|
155
|
+
src = src.strip()
|
|
156
|
+
dst = dst.strip()
|
|
157
|
+
|
|
158
|
+
console.print(f"[blue]Converting {src} ...[/blue]")
|
|
159
|
+
|
|
160
|
+
conv_type = detect_conversion_type(src, dst)
|
|
161
|
+
|
|
162
|
+
if conv_type is None:
|
|
163
|
+
console.print(
|
|
164
|
+
f"[red]Unsupported conversion from {get_extension(src)} to {get_extension(dst)}[/red]"
|
|
165
|
+
)
|
|
166
|
+
raise typer.Exit(code=1)
|
|
167
|
+
|
|
168
|
+
# Handle image to PDF (img2pdf) - uses ImageEngine
|
|
169
|
+
if conv_type == "img2pdf":
|
|
170
|
+
if src.endswith((".png", ".jpg", ".jpeg", ".bmp", ".webp")):
|
|
171
|
+
image_engine_local.images_to_pdf([src], dst)
|
|
172
|
+
console.print(f"[green]PDF saved to: {dst}[/green]")
|
|
173
|
+
else:
|
|
174
|
+
console.print(
|
|
175
|
+
f"[red]Unsupported image format: {get_extension(src)}[/red]"
|
|
176
|
+
)
|
|
177
|
+
raise typer.Exit(code=1)
|
|
178
|
+
|
|
179
|
+
# Handle convert-img (format conversion)
|
|
180
|
+
elif conv_type == "convert-img":
|
|
181
|
+
if src.endswith((".png", ".jpg", ".jpeg", ".webp", ".bmp")):
|
|
182
|
+
dst_ext = Path(dst).suffix.lower()
|
|
183
|
+
output_format = dst_ext.lstrip(".") or "PNG"
|
|
184
|
+
image_engine_local.convert_image_format(src, dst, output_format)
|
|
185
|
+
console.print(f"[green]Image saved to: {dst}[/green]")
|
|
186
|
+
else:
|
|
187
|
+
console.print(f"[red]Unsupported source format: {get_extension(src)}[/red]")
|
|
188
|
+
raise typer.Exit(code=1)
|
|
189
|
+
|
|
190
|
+
# Handle document conversions using ConversionEngine
|
|
191
|
+
else:
|
|
192
|
+
# Map command name to engine method
|
|
193
|
+
method_map = {
|
|
194
|
+
"pdf2docx": lambda: engine.pdf_to_docx(src, dst),
|
|
195
|
+
"pdf2docx-hq": lambda: pdf_to_docx_hq(src, dst),
|
|
196
|
+
"pdf2xlsx": lambda: engine.pdf_to_xlsx(src, dst),
|
|
197
|
+
"pdf2pptx": lambda: engine.pdf_to_pptx(src, dst),
|
|
198
|
+
"pdf2txt": lambda: engine.pdf_to_text(src, dst),
|
|
199
|
+
"pdf2md": lambda: engine.pdf_to_markdown(src, dst),
|
|
200
|
+
"docx2pdf": lambda: engine.docx_to_pdf(src, dst),
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
if conv_type in method_map:
|
|
204
|
+
method_map[conv_type]()
|
|
205
|
+
console.print(f"[green]{dst} saved successfully[/green]")
|
|
206
|
+
else:
|
|
207
|
+
console.print(f"[red]Conversion type {conv_type} not implemented[/red]")
|
|
208
|
+
raise typer.Exit(code=1)
|
|
209
|
+
|
|
210
|
+
console.print(f"[green]All conversions completed![/green]")
|
|
211
|
+
|
|
212
|
+
|
|
213
|
+
@convert_app.command("docx2pdf")
|
|
214
|
+
def docx2pdf_cmd(
|
|
215
|
+
input: str = typer.Argument(..., help="Input DOCX file"),
|
|
216
|
+
output: str = typer.Option(..., "-o", "--output", help="Output PDF file"),
|
|
217
|
+
):
|
|
218
|
+
"""Convert DOCX to PDF."""
|
|
219
|
+
console.print(f"[blue]Converting {input} to PDF...[/blue]")
|
|
220
|
+
engine = ConversionEngine()
|
|
221
|
+
engine.docx_to_pdf(input, output)
|
|
222
|
+
console.print(f"[green]PDF saved to: {output}[/green]")
|
|
223
|
+
|
|
224
|
+
|
|
225
|
+
@convert_app.command("pdf2docx")
|
|
226
|
+
def pdf2docx_cmd(
|
|
227
|
+
input: str = typer.Argument(..., help="Input PDF file"),
|
|
228
|
+
output: str = typer.Option(..., "-o", "--output", help="Output DOCX file"),
|
|
229
|
+
):
|
|
230
|
+
"""Convert PDF to DOCX."""
|
|
231
|
+
console.print(f"[blue]Converting {input} to DOCX...[/blue]")
|
|
232
|
+
engine = ConversionEngine()
|
|
233
|
+
engine.pdf_to_docx(input, output)
|
|
234
|
+
console.print(f"[green]DOCX saved to: {output}[/green]")
|
|
235
|
+
|
|
236
|
+
|
|
237
|
+
@convert_app.command("pdf2docx-hq")
|
|
238
|
+
def pdf2docx_hq_cmd(
|
|
239
|
+
input: str = typer.Argument(..., help="Input PDF file"),
|
|
240
|
+
output: str = typer.Option(..., "-o", "--output", help="Output DOCX file"),
|
|
241
|
+
):
|
|
242
|
+
"""Convert PDF to DOCX with high-fidelity reconstruction."""
|
|
243
|
+
console.print(f"[blue]Converting {input} to DOCX (high fidelity)...[/blue]")
|
|
244
|
+
from packages.conversion_engine import pdf_to_docx_hq
|
|
245
|
+
pdf_to_docx_hq(input, output)
|
|
246
|
+
console.print(f"[green]High-fidelity DOCX saved to: {output}[/green]")
|
|
247
|
+
|
|
248
|
+
|
|
249
|
+
@convert_app.command("pdf2txt")
|
|
250
|
+
def pdf2txt_cmd(
|
|
251
|
+
input: str = typer.Argument(..., help="Input PDF file"),
|
|
252
|
+
output: Optional[str] = typer.Option(None, "-o", "--output", help="Output text file (optional)"),
|
|
253
|
+
):
|
|
254
|
+
"""Convert PDF to plain text."""
|
|
255
|
+
console.print(f"[blue]Converting {input} to text...[/blue]")
|
|
256
|
+
engine = ConversionEngine()
|
|
257
|
+
text = engine.pdf_to_text(input, output)
|
|
258
|
+
if output:
|
|
259
|
+
console.print(f"[green]Text saved to: {output}[/green]")
|
|
260
|
+
else:
|
|
261
|
+
console.print(text[:500] + "..." if len(text) > 500 else text)
|
|
262
|
+
|
|
263
|
+
|
|
264
|
+
@convert_app.command("pdf2md")
|
|
265
|
+
def pdf2md_cmd(
|
|
266
|
+
input: str = typer.Argument(..., help="Input PDF file"),
|
|
267
|
+
output: Optional[str] = typer.Option(None, "-o", "--output", help="Output markdown file (optional)"),
|
|
268
|
+
):
|
|
269
|
+
"""Convert PDF to Markdown."""
|
|
270
|
+
console.print(f"[blue]Converting {input} to Markdown...[/blue]")
|
|
271
|
+
engine = ConversionEngine()
|
|
272
|
+
md = engine.pdf_to_markdown(input, output)
|
|
273
|
+
if output:
|
|
274
|
+
console.print(f"[green]Markdown saved to: {output}[/green]")
|
|
275
|
+
else:
|
|
276
|
+
console.print(md[:500] + "..." if len(md) > 500 else md)
|
|
277
|
+
|
|
278
|
+
|
|
279
|
+
@convert_app.command("img2pdf")
|
|
280
|
+
def img2pdf_cmd(
|
|
281
|
+
input: str = typer.Argument(..., help="Input image file or list of images separated by comma"),
|
|
282
|
+
output: str = typer.Option(..., "-o", "--output", help="Output PDF file"),
|
|
283
|
+
):
|
|
284
|
+
"""Convert image(s) to PDF."""
|
|
285
|
+
console.print(f"[blue]Converting {input} to PDF...[/blue]")
|
|
286
|
+
if "," in input:
|
|
287
|
+
image_paths = [p.strip() for p in input.split(",")]
|
|
288
|
+
image_engine.images_to_pdf(image_paths, output)
|
|
289
|
+
else:
|
|
290
|
+
image_engine.images_to_pdf([input], output)
|
|
291
|
+
console.print(f"[green]PDF saved to: {output}[/green]")
|
|
292
|
+
|
|
293
|
+
|
|
294
|
+
@convert_app.command("convert-img")
|
|
295
|
+
def convert_img_cmd(
|
|
296
|
+
input: str = typer.Argument(..., help="Input image file"),
|
|
297
|
+
output: str = typer.Option(..., "-o", "--output", help="Output image file"),
|
|
298
|
+
format: str = typer.Option("PNG", "--format", help="Output format (PNG, JPEG, JPG, BMP, GIF, TIFF, WEBP)"),
|
|
299
|
+
):
|
|
300
|
+
"""Convert image from one format to another."""
|
|
301
|
+
console.print(f"[blue]Converting {input} to {format}...[/blue]")
|
|
302
|
+
image_engine.convert_image_format(input, output, format)
|
|
303
|
+
console.print(f"[green]Image saved to: {output}[/green]")
|
|
304
|
+
|
|
305
|
+
|
|
306
|
+
@convert_app.command("compress-img")
|
|
307
|
+
def compress_img_cmd(
|
|
308
|
+
input: str = typer.Argument(..., help="Input image file"),
|
|
309
|
+
output: str = typer.Option(..., "-o", "--output", help="Output image file"),
|
|
310
|
+
quality: int = typer.Option(85, "--quality", help="Compression quality (1-100)"),
|
|
311
|
+
format: str = typer.Option("JPEG", "--format", help="Output format"),
|
|
312
|
+
):
|
|
313
|
+
"""Compress an image with specified quality."""
|
|
314
|
+
console.print(f"[blue]Compressing {input}...[/blue]")
|
|
315
|
+
image_engine.compress_image(input, output, quality=quality, output_format=format)
|
|
316
|
+
console.print(f"[green]Image saved to: {output}[/green]")
|
|
317
|
+
|
|
318
|
+
|
|
319
|
+
@convert_app.command("resize-img")
|
|
320
|
+
def resize_img_cmd(
|
|
321
|
+
input: str = typer.Argument(..., help="Input image file"),
|
|
322
|
+
output: str = typer.Option(..., "-o", "--output", help="Output image file"),
|
|
323
|
+
width: Optional[int] = typer.Option(None, "--width", help="Output width"),
|
|
324
|
+
height: Optional[int] = typer.Option(None, "--height", help="Output height"),
|
|
325
|
+
maintain_aspect: bool = typer.Option(True, "--no-maintain-aspect/--maintain-aspect", help="Maintain aspect ratio"),
|
|
326
|
+
):
|
|
327
|
+
"""Resize an image and return transformation info."""
|
|
328
|
+
console.print(f"[blue]Resizing {input}...[/blue]")
|
|
329
|
+
info = image_engine.resize_image(input, output, width=width, height=height, maintain_aspect=maintain_aspect)
|
|
330
|
+
console.print(f"[green]Image saved to: {output}[/green]")
|
|
331
|
+
console.print(f"[dim]Transformation: {info['original_size']} -> {info['new_size']} (maintain_aspect={info['maintain_aspect']})[/dim]")
|
|
332
|
+
|
|
333
|
+
|
|
334
|
+
if __name__ == "__main__":
|
|
335
|
+
convert_app()
|