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 ADDED
File without changes
cli/docome/__init__.py ADDED
File without changes
@@ -0,0 +1,3 @@
1
+ from .pdf import pdf_app
2
+
3
+ __all__ = ["pdf_app"]
@@ -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()