convashun 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.
convashun/__init__.py ADDED
File without changes
convashun/cli.py ADDED
@@ -0,0 +1,28 @@
1
+ import typer
2
+ from rich.console import Console
3
+ from convashun import image, document
4
+
5
+ # Create the main Typer application
6
+ app = typer.Typer(
7
+ name="Convashun",
8
+ help="⚡ Ultra-fast cross-platform file conversion utility ⚡",
9
+ add_completion=True,
10
+ )
11
+
12
+ # Initialize rich console for error and status printing
13
+ console = Console()
14
+
15
+ # Attach sub-modules as command groups
16
+ app.add_typer(document.app, name="document", help="Convert document formats (DOCX, PDF, TXT, etc.)")
17
+ app.add_typer(image.app, name="image", help="Convert image formats (PNG, JPG, WEBP, etc.)")
18
+
19
+ @app.callback()
20
+ def main():
21
+ """
22
+ Global settings or configurations can go here.
23
+ This executes before any subcommand.
24
+ """
25
+ pass
26
+
27
+ if __name__ == "__main__":
28
+ app()
convashun/document.py ADDED
@@ -0,0 +1,514 @@
1
+ import typer
2
+ import subprocess
3
+ import sys
4
+ import pandas as pd
5
+ import shutil
6
+ from pathlib import Path
7
+ from rich.console import Console
8
+ from pypdf import PdfReader
9
+ from docx import document
10
+
11
+ app = typer.Typer()
12
+ console = Console()
13
+
14
+
15
+ @app.command("to-pdf")
16
+ def to_pdf(
17
+ input_path: Path = typer.Argument(
18
+ ...,
19
+ exists=True,
20
+ file_okay=True,
21
+ dir_okay=False,
22
+ readable=True,
23
+ help="Path to the source document file (e.g., .docx, .doc, .odt, .rtf)"
24
+ ),
25
+ ):
26
+ """
27
+ Convert an incoming document file (DOCX/DOC/ODT) to PDF format.
28
+ """
29
+ console.print(f"[yellow]Processing:[/yellow] {input_path.name}...")
30
+
31
+ # Cross-platform safe path modification
32
+ output_path = input_path.with_suffix(".pdf")
33
+
34
+ # 1. Locate LibreOffice on the user's system path dynamically
35
+ # Windows uses 'soffice', Linux/macOS usually map to 'libreoffice' or 'soffice'
36
+ cmd = shutil.which("libreoffice") or shutil.which("soffice")
37
+
38
+ if not cmd:
39
+ console.print("[bold red]Missing Dependency Error:[/bold red]")
40
+ console.print("LibreOffice is required to convert office documents on your machine.")
41
+ console.print("\n[yellow]How to install it:[/yellow]")
42
+ console.print("• [bold]macOS:[/bold] `brew install libreoffice`")
43
+ console.print("• [bold]Linux (Ubuntu/Debian):[/bold] `sudo apt install libreoffice`")
44
+ console.print("• [bold]Windows:[/bold] Install via winget: `winget install LibreOffice.LibreOffice` or download manually.")
45
+ raise typer.Exit(code=1)
46
+
47
+ try:
48
+ # 2. Run the cross-platform headless conversion command
49
+ # This reads the document, runs in the background, and generates a clean PDF
50
+ subprocess.run([
51
+ cmd,
52
+ "--headless",
53
+ "--convert-to", "pdf",
54
+ str(input_path),
55
+ "--outdir", str(input_path.parent)
56
+ ], check=True, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
57
+
58
+ console.print(f"[bold green]Success![/bold green] Saved to {output_path}")
59
+ except subprocess.CalledProcessError as e:
60
+ console.print(f"[bold red]LibreOffice engine failed to process the file:[/bold red] {e}")
61
+ raise typer.Exit(code=1)
62
+ except Exception as e:
63
+ console.print(f"[bold red]Error converting file:[/bold red] {e}")
64
+ raise typer.Exit(code=1)
65
+
66
+
67
+ @app.command("pptx-to-pdf")
68
+ def pptx_to_pdf(
69
+ input_path: Path = typer.Argument(
70
+ ...,
71
+ exists=True,
72
+ file_okay=True,
73
+ dir_okay=False,
74
+ readable=True,
75
+ help="Path to the source PowerPoint (.pptx or .ppt) file"
76
+ ),
77
+ ):
78
+ """
79
+ Convert an incoming PowerPoint presentation (PPTX/PPT) to PDF format.
80
+ """
81
+ # Defensive check: Ensure they are actually passing a presentation file
82
+ if input_path.suffix.lower() not in (".pptx", ".ppt"):
83
+ console.print("[bold red]Format Error:[/bold red] This command only supports converting .pptx or .ppt files to .pdf.")
84
+ raise typer.Exit(code=1)
85
+
86
+ console.print(f"[yellow]Processing Presentation:[/yellow] {input_path.name}...")
87
+
88
+ # Cross-platform safe path modification
89
+ output_path = input_path.with_suffix(".pdf")
90
+
91
+ # 1. Locate LibreOffice dynamically on the user's system path
92
+ cmd = shutil.which("libreoffice") or shutil.which("soffice")
93
+
94
+ if not cmd:
95
+ console.print("[bold red]Missing Dependency Error:[/bold red]")
96
+ console.print("LibreOffice is required to convert presentation files on your machine.")
97
+ console.print("\n[yellow]How to install it:[/yellow]")
98
+ console.print("• [bold]macOS:[/bold] `brew install libreoffice`")
99
+ console.print("• [bold]Linux (Ubuntu/Debian):[/bold] `sudo apt install libreoffice`")
100
+ console.print("• [bold]Windows:[/bold] Install via winget: `winget install LibreOffice.LibreOffice` or download manually.")
101
+ raise typer.Exit(code=1)
102
+
103
+ try:
104
+ # 2. Run the cross-platform headless conversion command
105
+ # LibreOffice reads presentation slide structures natively and flattens them cleanly
106
+ subprocess.run([
107
+ cmd,
108
+ "--headless",
109
+ "--convert-to", "pdf",
110
+ str(input_path),
111
+ "--outdir", str(input_path.parent)
112
+ ], check=True, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
113
+
114
+ console.print(f"[bold green]Success![/bold green] Saved presentation to {output_path}")
115
+ except subprocess.CalledProcessError as e:
116
+ console.print(f"[bold red]LibreOffice engine failed to process the presentation:[/bold red] {e}")
117
+ raise typer.Exit(code=1)
118
+ except Exception as e:
119
+ console.print(f"[bold red]Error converting presentation file:[/bold red] {e}")
120
+ raise typer.Exit(code=1)
121
+
122
+ import pandas as pd
123
+
124
+
125
+ @app.command("csv-to-xlsx")
126
+ def csv_to_xlsx(input_path: Path = typer.Argument(..., exists=True)):
127
+ """Convert a flat CSV log file into an Excel Spreadsheet (.xlsx)."""
128
+ if input_path.suffix.lower() != ".csv":
129
+ console.print("[bold red]Format Error:[/bold red] This command only supports converting .csv files.")
130
+ raise typer.Exit(code=1)
131
+ output_path = input_path.with_suffix(".xlsx")
132
+ try:
133
+ df = pd.read_csv(input_path)
134
+ df.to_excel(output_path, index=False, engine='openpyxl')
135
+ console.print(f"[bold green]Success![/bold green] Spreadsheet saved to {output_path}")
136
+ except Exception as e:
137
+ console.print(f"[bold red]Data processing failed:[/bold red] {e}")
138
+
139
+
140
+ @app.command("xlsx-to-csv")
141
+ def xlsx_to_csv(
142
+ input_path: Path = typer.Argument(
143
+ ...,
144
+ exists=True,
145
+ file_okay=True,
146
+ dir_okay=False,
147
+ readable=True,
148
+ help="Path to the source Excel (.xlsx) spreadsheet"
149
+ ),
150
+ sheet_name: str = typer.Option(
151
+ "0",
152
+ "--sheet", "-s",
153
+ help="Name or index of the spreadsheet tab to extract"
154
+ ),
155
+ ):
156
+ """
157
+ Flatten a structured Excel Spreadsheet (.xlsx) sheet into a raw data CSV file.
158
+ """
159
+ if input_path.suffix.lower() != ".xlsx":
160
+ console.print("[bold red]Format Error:[/bold red] This command only supports converting .xlsx files.")
161
+ raise typer.Exit(code=1)
162
+
163
+ console.print(f"[yellow]Flattening spreadsheet records:[/yellow] {input_path.name}...")
164
+ output_path = input_path.with_suffix(".csv")
165
+
166
+ try:
167
+ # Convert numeric string token to raw integer index dynamically
168
+ parsed_sheet = int(sheet_name) if sheet_name.isdigit() else sheet_name
169
+
170
+ # Pull data layout using openpyxl engine underneath pandas
171
+ df = pd.read_excel(input_path, sheet_name=parsed_sheet, engine="openpyxl")
172
+
173
+ # Save output records with standard comma-separation formatting
174
+ df.to_csv(output_path, index=False)
175
+
176
+ console.print(f"[bold green]Success![/bold green] Extracted data saved to {output_path}")
177
+ except Exception as e:
178
+ console.print(f"[bold red]Flattening procedure failed:[/bold red] {e}")
179
+ raise typer.Exit(code=1)
180
+
181
+
182
+ @app.command("to-csv")
183
+ def to_csv(
184
+ input_path: Path = typer.Argument(
185
+ ...,
186
+ exists=True,
187
+ file_okay=True,
188
+ dir_okay=False,
189
+ readable=True,
190
+ help="Path to the source Excel (.xlsx) file"
191
+ ),
192
+ sheet_name: str = typer.Option(
193
+ "0",
194
+ "--sheet", "-s",
195
+ help="Name or 0-based index of the Excel sheet to convert"
196
+ ),
197
+ ):
198
+ """
199
+ Convert an Excel Spreadsheet (.xlsx) sheet into a raw data CSV file.
200
+ """
201
+ if input_path.suffix.lower() != ".xlsx":
202
+ console.print("[bold red]Format Error:[/bold red] This command only supports converting .xlsx files.")
203
+ raise typer.Exit(code=1)
204
+
205
+ console.print(f"[yellow]Extracting spreadsheet data:[/yellow] {input_path.name}...")
206
+ output_path = input_path.with_suffix(".csv")
207
+
208
+ try:
209
+ # Convert numeric string index to int if applicable
210
+ parsed_sheet = int(sheet_name) if sheet_name.isdigit() else sheet_name
211
+
212
+ # Read the Excel file via openpyxl engine
213
+ df = pd.read_excel(input_path, sheet_name=parsed_sheet, engine="openpyxl")
214
+
215
+ # Save to CSV without the arbitrary row index numbers
216
+ df.to_csv(output_path, index=False)
217
+
218
+ console.print(f"[bold green]Success![/bold green] Extracted data saved to {output_path}")
219
+ except Exception as e:
220
+ console.print(f"[bold red]Data extraction failed:[/bold red] {e}")
221
+ raise typer.Exit(code=1)
222
+
223
+ @app.command("to-xlsx")
224
+ def to_xlsx(
225
+ input_path: Path = typer.Argument(
226
+ ...,
227
+ exists=True,
228
+ file_okay=True,
229
+ dir_okay=False,
230
+ readable=True,
231
+ help="Path to the source CSV file"
232
+ ),
233
+ ):
234
+ """
235
+ Convert flat CSV raw rows into a structured Excel Spreadsheet (.xlsx).
236
+ """
237
+ if input_path.suffix.lower() != ".csv":
238
+ console.print("[bold red]Format Error:[/bold red] This command only supports converting .csv files.")
239
+ raise typer.Exit(code=1)
240
+
241
+ console.print(f"[yellow]Structuring spreadsheet layout:[/yellow] {input_path.name}...")
242
+ output_path = input_path.with_suffix(".xlsx")
243
+
244
+ try:
245
+ # Read raw tabular rows
246
+ df = pd.read_csv(input_path)
247
+
248
+ # Stream down directly into openpyxl formatting engine
249
+ df.to_excel(output_path, index=False, engine="openpyxl")
250
+
251
+ console.print(f"[bold green]Success![/bold green] Spreadsheet saved to {output_path}")
252
+ except Exception as e:
253
+ console.print(f"[bold red]Spreadsheet composition failed:[/bold red] {e}")
254
+ raise typer.Exit(code=1)
255
+
256
+
257
+ def install_tex_engine_automatically():
258
+ """Triggers the operating system's native package manager to install a lightweight CLI TeX engine."""
259
+ console.print("\n[yellow]Attempting to bootstrap a lightweight TeX layout engine for you...[/yellow]")
260
+
261
+ try:
262
+ if sys.platform == "win32":
263
+ console.print("[blue]Running Windows Package Manager (winget) for MiKTeX...[/blue]")
264
+ # Triggers a clean, silent install of the core minimal Windows TeX compilation engine
265
+ subprocess.run(["winget", "install", "MiKTeX.MiKTeX", "--silent"], check=True)
266
+
267
+ elif sys.platform == "darwin":
268
+ console.print("[blue]Running macOS Homebrew for BasicTeX (Lightweight distribution)...[/blue]")
269
+ # Installs a minimal ~100MB CLI-only distribution instead of the multi-gigabyte full MacTeX package
270
+ subprocess.run(["brew", "install", "--cask", "basictex"], check=True)
271
+
272
+ else: # Linux (Ubuntu / Debian target check)
273
+ if shutil.which("apt-get"):
274
+ console.print("[blue]Running Linux Advanced Package Tool (apt) for lightweight texlive-base...[/blue]")
275
+ subprocess.run(["sudo", "apt-get", "update"], check=True)
276
+ subprocess.run(["sudo", "apt-get", "install", "-y", "texlive-latex-base"], check=True)
277
+ else:
278
+ raise RuntimeError("Unsupported Linux package distribution manager.")
279
+
280
+ console.print("[bold green]Success![/bold green] TeX compilation engine installation complete.")
281
+ console.print("[yellow]Note: Please restart your terminal window for the system path variables to take effect.[/yellow]")
282
+
283
+ except Exception as e:
284
+ console.print(f"[bold red]Auto-installation failed:[/bold red] {e}")
285
+ console.print("[yellow]Please configure a compiler (like MiKTeX or TeX Live) manually on your path.[/yellow]")
286
+
287
+
288
+ @app.command("tex-to-pdf")
289
+ def tex_to_pdf(
290
+ input_path: Path = typer.Argument(
291
+ ..., exists=True, file_okay=True, dir_okay=False, readable=True,
292
+ help="Path to the source LaTeX (.tex) typesetting file"
293
+ ),
294
+ ):
295
+ """
296
+ Compile a LaTeX (.tex) typesetting file into a high-fidelity PDF document.
297
+ """
298
+ if input_path.suffix.lower() != ".tex":
299
+ console.print("[bold red]Format Error:[/bold red] This command only supports .tex files.")
300
+ raise typer.Exit(code=1)
301
+
302
+ console.print(f"[yellow]Typesetting document layout:[/yellow] {input_path.name}...")
303
+ output_path = input_path.with_suffix(".pdf")
304
+
305
+ # Locate native cross-platform latex binary mapping paths dynamically
306
+ cmd = shutil.which("pdflatex") or shutil.which("latexmk")
307
+
308
+ if not cmd:
309
+ console.print("[bold red]Missing Dependency Error:[/bold red] A native TeX compiler layout engine was not found.")
310
+
311
+ # Interactive Helper Selector
312
+ confirm = typer.confirm("Would you like convashun to automatically install a lightweight TeX engine via your system package manager?")
313
+ if confirm:
314
+ install_tex_engine_automatically()
315
+ raise typer.Exit(code=0)
316
+ else:
317
+ console.print("[red]Compilation aborted because system dependencies are missing.[/red]")
318
+ raise typer.Exit(code=1)
319
+
320
+ try:
321
+ # Run the native compiler safely in non-interactive batch mode
322
+ # It suppresses hanging user confirmation alerts if errors exist in the raw document layout
323
+ subprocess.run([
324
+ cmd,
325
+ "-interaction=nonstopmode",
326
+ f"-output-directory={str(input_path.parent)}",
327
+ str(input_path)
328
+ ], check=True, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
329
+
330
+ # Clean up ancillary layout compilation log files generated by native TeX tools (.log, .aux)
331
+ for ext in (".aux", ".log", ".out"):
332
+ temp_file = input_path.with_suffix(ext)
333
+ if temp_file.exists():
334
+ temp_file.unlink()
335
+
336
+ console.print(f"[bold green]Success![/bold green] PDF compiled cleanly to {output_path}")
337
+ except subprocess.CalledProcessError as e:
338
+ console.print(f"[bold red]LaTeX engine failed to process the document structures:[/bold red] {e}")
339
+ raise typer.Exit(code=1)
340
+ except Exception as e:
341
+ console.print(f"[bold red]Error converting file:[/bold red] {e}")
342
+ raise typer.Exit(code=1)
343
+
344
+ @app.command("tex-to-pdf-native")
345
+ def tex_to_pdf_native(input_path: Path = typer.Argument(..., exists=True)):
346
+ """Compile LaTeX to PDF using the local system engine."""
347
+ if input_path.suffix.lower() != ".tex":
348
+ console.print("[bold red]Format Error:[/bold red] Target must be a LaTex file.")
349
+ raise typer.Exit(code=1)
350
+ cmd = shutil.which("pdflatex")
351
+
352
+ if not cmd:
353
+ console.print("[bold red]Missing Dependency Error:[/bold red]")
354
+ console.print("A native TeX compiler (`pdflatex`) is required for this command.")
355
+ console.print("\n[yellow]How to install it:[/yellow]")
356
+ console.print("• [bold]macOS:[/bold] `brew install --cask mactex-no-gui`")
357
+ console.print("• [bold]Linux (Ubuntu):[/bold] `sudo apt install texlive-latex-base`")
358
+ console.print("• [bold]Windows:[/bold] Install MikTeX from `miktex.org`")
359
+ raise typer.Exit(code=1)
360
+
361
+ try:
362
+ # Execute the compiler tool inside the directory where the target file exists
363
+ subprocess.run([
364
+ cmd,
365
+ "-interaction=nonstopmode",
366
+ "-output-directory", str(input_path.parent),
367
+ str(input_path)
368
+ ], check=True, stdout=subprocess.DEVNULL)
369
+ console.print(f"[bold green]Success![/bold green] Compiled via system engine.")
370
+ except Exception as e:
371
+ console.print(f"[bold red]Compilation crashed:[/bold red] {e}")
372
+
373
+ def install_libreoffice_automatically():
374
+ """Triggers the operating system's native package manager to install LibreOffice."""
375
+ console.print("\n[yellow]Attempting to bootstrap LibreOffice for you...[/yellow]")
376
+
377
+ try:
378
+ if sys.platform == "win32":
379
+ console.print("[blue]Running Windows Package Manager (winget)...[/blue]")
380
+ # Executes Windows' official built-in app installer
381
+ subprocess.run(["winget", "install", "LibreOffice.LibreOffice", "--silent"], check=True)
382
+
383
+ elif sys.platform == "darwin":
384
+ console.print("[blue]Running macOS Homebrew...[/blue]")
385
+ subprocess.run(["brew", "install", "--cask", "libreoffice"], check=True)
386
+
387
+ else: # Linux (Ubuntu / Debian target check)
388
+ if shutil.which("apt-get"):
389
+ console.print("[blue]Running Linux Advanced Package Tool (apt)...[/blue]")
390
+ subprocess.run(["sudo", "apt-get", "update"], check=True)
391
+ subprocess.run(["sudo", "apt-get", "install", "-y", "libreoffice"], check=True)
392
+ else:
393
+ raise RuntimeError("Unsupported Linux package distribution manager.")
394
+
395
+ console.print("[bold green]Success![/bold green] LibreOffice installation completed.")
396
+ console.print("[yellow]Note: Please restart your terminal window for the system path changes to take effect.[/yellow]")
397
+
398
+ except Exception as e:
399
+ console.print(f"[bold red]Auto-installation failed:[/bold red] {e}")
400
+ console.print("[yellow]Please download LibreOffice manually from https://libreoffice.org[/yellow]")
401
+
402
+
403
+
404
+ @app.command("pptx-to-pdf")
405
+ def pptx_to_pdf(
406
+ input_path: Path = typer.Argument(
407
+ ...,
408
+ exists=True,
409
+ file_okay=True,
410
+ dir_okay=False,
411
+ readable=True,
412
+ help="Path to the source PowerPoint (.pptx or .ppt) file"
413
+ ),
414
+ ):
415
+ """
416
+ Convert a PowerPoint presentation (PPTX/PPT) to PDF format cleanly.
417
+ """
418
+ if input_path.suffix.lower() not in (".pptx", ".ppt"):
419
+ console.print("[bold red]Format Error:[/bold red] This command only supports .pptx or .ppt files.")
420
+ raise typer.Exit(code=1)
421
+
422
+ console.print(f"[yellow]Processing Presentation:[/yellow] {input_path.name}...")
423
+ output_path = input_path.with_suffix(".pdf")
424
+
425
+ # Locate LibreOffice dynamically on the user's system path
426
+ cmd = shutil.which("libreoffice") or shutil.which("soffice")
427
+ if not cmd:
428
+ console.print("[bold red]Missing Dependency Error:[/bold red] LibreOffice is required to handle Office file layouts.")
429
+
430
+ # Interactive Prompt to make life easier for the user
431
+ confirm = typer.confirm("Would you like convashun to automatically install LibreOffice via your system package manager?")
432
+ if confirm:
433
+ install_libreoffice_automatically()
434
+ raise typer.Exit(code=0)
435
+ else:
436
+ console.print("[red]Conversion aborted because system dependencies are missing.[/red]")
437
+ raise typer.Exit(code=1)
438
+
439
+ try:
440
+ subprocess.run([
441
+ cmd, "--headless", "--convert-to", "pdf",
442
+ str(input_path), "--outdir", str(input_path.parent)
443
+ ], check=True, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
444
+ console.print(f"[bold green]Success![/bold green] Saved presentation to {output_path}")
445
+ except Exception as e:
446
+ console.print(f"[bold red]Conversion failed:[/bold red] {e}")
447
+ raise typer.Exit(code=1)
448
+
449
+ @app.command("to-markdown")
450
+ def to_markdown(
451
+ input_path: Path = typer.Argument(..., exists=True, file_okay=True, dir_okay=False)
452
+ ):
453
+ """
454
+ Extract text contents from a PDF file into clean Markdown formatting (.md).
455
+ """
456
+ if input_path.suffix.lower() != ".pdf":
457
+ console.print("[bold red]Format Error:[/bold red] Target must be a PDF file.")
458
+ raise typer.Exit(code=1)
459
+
460
+ console.print(f"[yellow]Extracting text layouts:[/yellow] {input_path.name}...")
461
+ output_path = input_path.with_suffix(".md")
462
+
463
+ try:
464
+ # pypdf is 100% pure Python, no C++ compilation or DLL required
465
+ reader = PdfReader(input_path)
466
+ markdown_lines = [f"# Document: {input_path.stem}\n"]
467
+
468
+ for i, page in enumerate(reader.pages, 1):
469
+ text = page.extract_text()
470
+ if text.strip():
471
+ markdown_lines.append(f"## Page {i}\n{text}\n")
472
+
473
+ output_path.write_text("\n".join(markdown_lines), encoding="utf-8")
474
+ console.print(f"[bold green]Success![/bold green] Extracted text layout to {output_path}")
475
+ except Exception as e:
476
+ console.print(f"[bold red]Markdown parsing failed:[/bold red] {e}")
477
+ raise typer.Exit(code=1)
478
+
479
+
480
+ @app.command("to-docx")
481
+ def to_docx(
482
+ input_path: Path = typer.Argument(..., exists=True, file_okay=True, dir_okay=False)
483
+ ):
484
+ """
485
+ Convert flat PDF text segments directly into an editable Word Document (.docx).
486
+ """
487
+ if input_path.suffix.lower() != ".pdf":
488
+ console.print("[bold red]Format Error:[/bold red] Target file must be a PDF.")
489
+ raise typer.Exit(code=1)
490
+
491
+ console.print(f"[yellow]Building document tables:[/yellow] {input_path.name}...")
492
+ output_path = input_path.with_suffix(".docx")
493
+
494
+ try:
495
+ reader = PdfReader(input_path)
496
+ doc = Document()
497
+ doc.add_heading(input_path.stem, level=0)
498
+
499
+ has_content = False
500
+ for page in reader.pages:
501
+ text = page.extract_text()
502
+ if text and text.strip():
503
+ doc.add_paragraph(text)
504
+ has_content = True
505
+ else:
506
+ # Safe fallback option: ensures python-docx always has a structural
507
+ # paragraph element layer to bind to when saving the file data
508
+ doc.add_paragraph("[Empty Page or Unextractable Layout Text Element]")
509
+
510
+ doc.save(output_path)
511
+ console.print(f"[bold green]Success![/bold green] Document compiled cleanly to {output_path}")
512
+ except Exception as e:
513
+ console.print(f"[bold red]DOCX composition failed:[/bold red] {e}")
514
+ raise typer.Exit(code=1)
convashun/image.py ADDED
@@ -0,0 +1,123 @@
1
+ import typer
2
+ import subprocess
3
+ import shutil
4
+ from PIL import Image
5
+ from pathlib import Path
6
+ from rich.console import Console
7
+
8
+ app = typer.Typer()
9
+ console = Console()
10
+
11
+ @app.command("to-png")
12
+ def to_png(
13
+ input_path: Path = typer.Argument(
14
+ ...,
15
+ exists=True,
16
+ file_okay=True,
17
+ dir_okay=False,
18
+ readable=True,
19
+ help="Path to the source image file"
20
+ ),
21
+ optimize: bool = typer.Option(
22
+ False,
23
+ "--optimize", "-o",
24
+ help="Optimize the PNG file size (takes slightly longer to save)"
25
+ ),
26
+ ):
27
+ """
28
+ Convert any incoming image file to PNG format.
29
+ """
30
+ console.print(f"[yellow]Processing:[/yellow] {input_path.name}...")
31
+
32
+ # Cross-platform safe path modification
33
+ output_path = input_path.with_suffix(".png")
34
+
35
+ try:
36
+ with Image.open(input_path) as img:
37
+ if img.mode == "CMYK":
38
+ img = img.convert("RGB")
39
+
40
+ img.save(output_path, format="PNG", optimize=optimize)
41
+ console.print(f"[bold green]Success![/bold green] Saved to {output_path}")
42
+ except Exception as e:
43
+ console.print(f"[bold red]Error converting file:[/bold red] {e}")
44
+ raise typer.Exit(code=1)
45
+
46
+ @app.command("to-webp")
47
+ def to_png(
48
+ input_path: Path = typer.Argument(
49
+ ...,
50
+ exists=True,
51
+ file_okay=True,
52
+ dir_okay=False,
53
+ readable=True,
54
+ help="Path to the source image file"
55
+ ),
56
+ quality: int = typer.Option(
57
+ 90,
58
+ "--quality", "-q",
59
+ min=1, max=100,
60
+ help="Compression quality (1-100). 100 means lossless."
61
+ ),
62
+ ):
63
+ """
64
+ Convert any incoming image file to WEBP format.
65
+ """
66
+ console.print(f"[yellow]Processing:[/yellow] {input_path.name}...")
67
+
68
+ # Cross-platform safe path modification
69
+ output_path = input_path.with_suffix(".webp")
70
+
71
+ try:
72
+ with Image.open(input_path) as img:
73
+ if img.mode == "CMYK":
74
+ img = img.convert("RGB")
75
+
76
+ img.save(output_path, format="WEBP", quality=quality)
77
+
78
+ console.print(f"[bold green]Success![/bold green] Saved to {output_path} (Quality: {quality})")
79
+ except Exception as e:
80
+ console.print(f"[bold red]Error converting file:[/bold red] {e}")
81
+ raise typer.Exit(code=1)
82
+
83
+ @app.command("to-jpg")
84
+ def to_jpg(
85
+ input_path: Path = typer.Argument(
86
+ ...,
87
+ exists=True,
88
+ file_okay=True,
89
+ dir_okay=False,
90
+ readable=True,
91
+ help="Path to the source image file"
92
+ ),
93
+ quality: int = typer.Option(
94
+ 85,
95
+ "--quality", "-q",
96
+ min=1, max=100,
97
+ help="JPEG compression quality (1-100)"
98
+ ),
99
+ ):
100
+ """
101
+ Convert any incoming image file to JPG format.
102
+ """
103
+ console.print(f"[yellow]Processing:[/yellow] {input_path.name}...")
104
+ output_path = input_path.with_suffix(".jpg")
105
+
106
+ try:
107
+ with Image.open(input_path) as img:
108
+ # Crucial: If the input has transparency (RGBA or P),
109
+ # we must flatten it onto a solid background color (white)
110
+ if img.mode in ("RGBA", "P"):
111
+ background = Image.new("RGB", img.size, (255, 255, 255))
112
+ # Paste the transparent image on top of the white background
113
+ background.paste(img, mask=img.convert("RGBA").split()[3])
114
+ img = background
115
+ elif img.mode == "CMYK":
116
+ img = img.convert("RGB")
117
+
118
+ img.save(output_path, format="JPEG", quality=quality)
119
+
120
+ console.print(f"[bold green]Success![/bold green] Saved to {output_path} (Quality: {quality})")
121
+ except Exception as e:
122
+ console.print(f"[bold red]Error converting file:[/bold red] {e}")
123
+ raise typer.Exit(code=1)
@@ -0,0 +1,139 @@
1
+ Metadata-Version: 2.4
2
+ Name: convashun
3
+ Version: 0.1.0
4
+ Summary: A Ultra-fast, zero-config, cross-platform file conversion utility optimized for developers, documentation teams, and automated robotics data pipelines.
5
+ Author-email: Emmanuel Omoiya <emmanuelomoiya6@gmail.com>
6
+ License: MIT
7
+ License-File: LICENSE
8
+ Classifier: Environment :: Console
9
+ Classifier: License :: OSI Approved :: MIT License
10
+ Classifier: Operating System :: OS Independent
11
+ Classifier: Programming Language :: Python :: 3
12
+ Requires-Python: <3.13,>=3.12
13
+ Requires-Dist: click<8.2.0,>=8.1.7
14
+ Requires-Dist: openpyxl>=3.1.5
15
+ Requires-Dist: pandas>=3.0.3
16
+ Requires-Dist: pillow<11.0.0
17
+ Requires-Dist: pypdf>=6.12.1
18
+ Requires-Dist: python-docx>=1.2.0
19
+ Requires-Dist: reportlab>=4.5.1
20
+ Requires-Dist: rich>=13.7.0
21
+ Requires-Dist: ruff>=0.15.14
22
+ Requires-Dist: typer<0.10.0,>=0.9.0
23
+ Description-Content-Type: text/markdown
24
+
25
+ # convashun 🟢
26
+
27
+ [![Continuous Integration](https://github.com)](https://github.com)
28
+ [![PyPI version](https://shields.io)](https://pypi.org)
29
+ [![License: MIT](https://shields.io)](https://opensource.org)
30
+ [![Supported Platforms](https://shields.io)](https://pypi.org)
31
+
32
+ > ⚡ Ultra-fast, zero-config, cross-platform file conversion utility optimized for developers, documentation teams, and automated robotics data pipelines.
33
+
34
+ Built entirely on top of modern Python type hints (`Typer`), blazingly fast package architecture (`uv`), and robust data utilities. Developed with love for open-source engineering workflows and engineered to seamlessly support the data tracking, documentation, and asset conversion pipelines of communities like **Aurora Robotics**.
35
+
36
+ ---
37
+
38
+ ## 📊 Conversion Matrix
39
+
40
+ `convashun` maps target conversions dynamically using deep binary validation rather than flimsy extension strings.
41
+
42
+
43
+ | Category | Input Formats | Targeted Output Command | Pure Python? | System Dependencies |
44
+ | :--- | :--- | :--- | :--- | :--- |
45
+ | **Images** | JPG, PNG, WEBP, BMP, GIF, TIFF | `convert image to-png` | Yes | None |
46
+ | **Images** | JPG, PNG, WEBP, BMP, GIF, TIFF | `convert image to-webp` | Yes | None |
47
+ | **Images** | JPG, PNG, WEBP, BMP, GIF, TIFF | `convert image to-jpg` | Yes | None |
48
+ | **Spreadsheets** | CSV (Comma Separated Log files) | `convert document to-xlsx` | Yes | None |
49
+ | **Spreadsheets** | XLSX (Multi-sheet spreadsheet layouts)| `convert document xlsx-to-csv` | Yes | None |
50
+ | **Documentation** | PDF Documents | `convert document to-docx` | Yes | None |
51
+ | **Documentation** | PDF Documents | `convert document to-markdown`| Yes | None |
52
+ | **Documentation** | Markdown Markup File (`.md`) | `convert document markdown-to-pdf`| Yes | None |
53
+ | **Office/Academics**| DOCX, DOC, ODT, PPTX | `convert document to-pdf` | No | LibreOffice |
54
+ | **Office/Academics**| PowerPoint Presentations (`.pptx`) | `convert document pptx-to-pdf`| No | LibreOffice |
55
+ | **Office/Academics**| LaTeX Documents (`.tex`) | `convert document tex-to-pdf` | Yes | None |
56
+
57
+ ---
58
+
59
+ ## 🚀 Quick Start & Installation
60
+
61
+ `convashun` works identically across Windows, macOS, and Linux distributions.
62
+
63
+ ### Method 1: Using `uv` (Highly Recommended)
64
+ Install `convashun` instantly into an isolated, globally accessible environment managed entirely by `uv`:
65
+ ```bash
66
+ uv tool install convashun
67
+ ```
68
+
69
+ ### Method 2: Standard Installation via PyPI
70
+ ```bash
71
+ pip install convashun
72
+ # Or use pipx to isolate system paths cleanly
73
+ pipx install convashun
74
+ ```
75
+
76
+ ---
77
+
78
+ ## 🛠️ Usage Examples
79
+
80
+ Once installed, use the global entry command `convert` directly in your terminal interface:
81
+
82
+ ### 🖼️ Image Operations
83
+ ```bash
84
+ # Convert an image into WebP format with custom compression quality settings
85
+ convert image to-webp input_graphic.png --quality 85
86
+
87
+ # Flatten a complex transparent asset safely into a JPG with a solid white backing
88
+ convert image to-jpg transparent_blueprint.png
89
+
90
+ # Optimize a standard PNG file size automatically without changing resolution parameters
91
+ convert image to-png photographic_capture.jpg --optimize
92
+ ```
93
+
94
+ ### 📄 Document Layouts & Spreadsheet Processing
95
+ ```bash
96
+ # Compile a Markdown README specification file directly into a portable PDF manual
97
+ convert document markdown-to-pdf ARCHITECTURE.md
98
+
99
+ # Extract telemetry or configuration records from an Excel document directly down to a CSV log
100
+ convert document xlsx-to-csv metrics_workbook.xlsx --sheet "TelemetryLogs"
101
+
102
+ # Convert an uneditable PDF report layout back into a semantic Markdown string file
103
+ convert document to-markdown datasheet_report.pdf
104
+ ```
105
+
106
+ ---
107
+
108
+ ## 📦 System Prerequisites (For Office Documents Only)
109
+
110
+ Commands such as `to-pdf` (for `.docx` and `.pptx`) use automated background processes to render layout definitions precisely. If a command prompts you with a missing dependency, install LibreOffice on your path using your platform package engine:
111
+
112
+ * **macOS:** `brew install libreoffice`
113
+ * **Linux (Ubuntu/Debian):** `sudo apt install libreoffice`
114
+ * **Windows (PowerShell WinGet):** `winget install LibreOffice.LibreOffice`
115
+
116
+ ---
117
+
118
+ ## 🟢 Open Source Contributions
119
+
120
+ We value input from engineering collaborators and the robotics community!
121
+
122
+ To add optimization modules or extend format parsers:
123
+ 1. Review our structured guidelines inside [CONTRIBUTING.md](CONTRIBUTING.md).
124
+ 2. Set up your local sandboxed tracking workspace via `uv`:
125
+ ```bash
126
+ git clone https://github.com
127
+ cd convashun
128
+ uv sync --dev
129
+ ```
130
+ 3. Run the complete automated multi-OS testing validation matrix locally before pushing changes:
131
+ ```bash
132
+ uv run pytest
133
+ ```
134
+
135
+ ---
136
+
137
+ ## 📄 License
138
+
139
+ Distributed safely under the terms of the open-source **MIT License**. Check out [LICENSE](LICENSE) for exact operational data guidelines.
@@ -0,0 +1,9 @@
1
+ convashun/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
+ convashun/cli.py,sha256=70O-LqwGPxdou_J-ldoN7AUfWmraYsJtqraTs7x9WyQ,753
3
+ convashun/document.py,sha256=S7Drl0_zYnVsn3T3KmIj-rohXjlFIXBGWTcLu6qN54E,21826
4
+ convashun/image.py,sha256=-TTirZxpPKgACbrSmowo3Re3uulqqgSZ-0LvsMLjCpw,3831
5
+ convashun-0.1.0.dist-info/METADATA,sha256=1UAdOrlZPg8gaEBvjwGNN7O0k8wWfqg9KTHA4B78T6s,5652
6
+ convashun-0.1.0.dist-info/WHEEL,sha256=QccIxa26bgl1E6uMy58deGWi-0aeIkkangHcxk2kWfw,87
7
+ convashun-0.1.0.dist-info/entry_points.txt,sha256=W94Q7BFnXOqRywszW8xU4aVOCQjmkrQEXLkTtfTqjgg,46
8
+ convashun-0.1.0.dist-info/licenses/LICENSE,sha256=VLGELCbLgyTym14JsR_0iY2m-rnwaAlCpoTFtgYW9nA,1067
9
+ convashun-0.1.0.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: hatchling 1.29.0
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ convert = convashun.cli:app
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) Emmanuel Omoiya
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.