Metadata-Version: 2.4
Name: universal-printer
Version: 3.1.0
Summary: Cross-platform document printing with enhanced PDF generation
Author-email: Sharath Kumar Daroor <sharathkumardaroor@gmail.com>
Maintainer-email: Sharath Kumar Daroor <sharathkumardaroor@gmail.com>
License-Expression: MIT
Project-URL: Homepage, https://github.com/sharathkumardaroor/universal-printer
Project-URL: Repository, https://github.com/sharathkumardaroor/universal-printer.git
Project-URL: Issues, https://github.com/sharathkumardaroor/universal-printer/issues
Project-URL: Changelog, https://github.com/sharathkumardaroor/universal-printer/blob/main/CHANGELOG.md
Keywords: printing,pdf,cross-platform,document,markdown,csv,json,text,fallback,universal,printer,windows,linux,macos,batch,conversion
Classifier: Development Status :: 5 - Production/Stable
Classifier: Environment :: Console
Classifier: Intended Audience :: Developers
Classifier: Intended Audience :: End Users/Desktop
Classifier: Intended Audience :: System Administrators
Classifier: Operating System :: OS Independent
Classifier: Operating System :: Microsoft :: Windows
Classifier: Operating System :: POSIX :: Linux
Classifier: Operating System :: MacOS
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.8
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Office/Business
Classifier: Topic :: Printing
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Topic :: System :: Systems Administration
Classifier: Topic :: Text Processing :: General
Classifier: Topic :: Utilities
Requires-Python: >=3.8
Description-Content-Type: text/markdown
License-File: LICENSE
Provides-Extra: windows
Requires-Dist: pywin32>=306; sys_platform == "win32" and extra == "windows"
Provides-Extra: test
Requires-Dist: pytest>=6.0; extra == "test"
Requires-Dist: pytest-cov>=2.0; extra == "test"
Provides-Extra: dev
Requires-Dist: pytest>=6.0; extra == "dev"
Requires-Dist: pytest-cov>=2.0; extra == "dev"
Requires-Dist: build>=0.7; extra == "dev"
Requires-Dist: twine>=3.0; extra == "dev"
Dynamic: license-file

# Universal Printer

[![PyPI version](https://img.shields.io/pypi/v/universal-printer.svg)](https://pypi.org/project/universal-printer/)
[![Python versions](https://img.shields.io/pypi/pyversions/universal-printer.svg)](https://pypi.org/project/universal-printer/)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)

Cross-platform document printing with PDF generation, for Python 3.8+.

**No runtime dependencies.** Not "no third-party dependencies we happen to
bundle" — genuinely zero. Everything, including the PDF writer, is built on
the standard library.

## Why it exists

Printing a file from Python is easy on one operating system and awkward on the
other two. This library dispatches to the right mechanism for the current
platform, and — crucially — **never leaves you with nothing** if that fails. If
no printer is configured, the print daemon is down, or the file type has no
registered handler, it writes a PDF instead.

The same applies to PDF generation. Most "convert to PDF" libraries pull in
ReportLab or WeasyPrint; this one writes the PDF byte stream itself, which
means `pip install universal-printer` never conflicts with anything else in
your environment.

## Installation

```bash
pip install universal-printer
```

To print to a *named* printer on Windows (see [Limitations](#limitations)):

```bash
pip install universal-printer[windows]
```

## Quick start

```python
from universal_printer import DocumentPrinter

printer = DocumentPrinter()

# Print a file of any type
success, message, pdf_path = printer.print_file("report.docx")

# Print text directly
printer.print_text("Hello, World!")

# Generate a PDF without involving the printer at all
printer.generate_pdf("notes.md", pdf_filename="notes")
```

Every method returns a `(success, message, pdf_path)` tuple. It never raises
for an expected failure, so you can branch on the result rather than wrapping
calls in `try`/`except`.

## PDF generation

`.txt`, `.md`, `.csv` and `.json` sources are formatted before rendering:

```python
printer.generate_pdf("data.csv", title="Q3 Sales", author="Analytics")
```

| Source | Handling |
| --- | --- |
| `.md` | Headings, lists, block quotes, code blocks, links, emphasis markers |
| `.csv` | Delimiter auto-detected (`,` `;` tab `\|`), quoted fields honoured, columns aligned in a monospace face |
| `.json` | Pretty-printed with **key order preserved** |
| `.txt` | Plain text, runs of whitespace collapsed |

Generated PDFs are paginated automatically from the configured page geometry —
long documents flow onto additional pages rather than being truncated — and
carry a document information dictionary with title, author, subject, creator
and creation date.

## Command line

```bash
universal-printer print report.pdf
universal-printer print --printer "HP LaserJet" report.pdf
universal-printer pdf notes.md --output formatted --title "Meeting Notes"
universal-printer batch-print *.txt --report results.json
universal-printer batch-pdf data/*.json --output-dir ./out
universal-printer info --file data.csv
universal-printer config --create config.json
```

## Configuration

Settings can come from a JSON file, from environment variables, or be set
directly on the object:

```python
from universal_printer import DocumentPrinter, PrinterConfig

config = PrinterConfig("config.json")
config.pdf.font_size = 12
config.pdf.margin_left = 60
config.print.max_workers = 8
config.print.default_printer = "Reception"

printer = DocumentPrinter(config=config)

printer.update_config(font_size=14)             # resolved automatically
printer.update_config(**{"print.max_workers": 16})   # or explicitly
```

| Environment variable | Effect |
| --- | --- |
| `UP_PDF_FONT_SIZE` | Body font size in points |
| `UP_PDF_MARGIN` | All four margins |
| `UP_DEFAULT_PRINTER` | Default printer name |
| `UP_FALLBACK_DIR` | Where fallback PDFs are written |
| `UP_ENABLE_FALLBACK` | `true`/`false` |
| `UP_LOG_LEVEL` | `DEBUG` … `CRITICAL` |
| `UP_LOG_FILE` | Log file path (rotated) |

Every value is validated at construction time; invalid geometry (margins that
leave no printable area, non-positive font sizes) raises `ConfigurationError`
immediately rather than producing an unreadable file later.

## Batch processing

Batch operations run on a thread pool, sized by `print.max_workers`:

```python
results = printer.print_batch(["a.txt", "b.md", "c.csv"])
print(results["successful_prints"], results["pdf_fallbacks"], results["duration"])
```

Individual failures are reported per file and do not abort the run.

## Statistics

```python
stats = printer.get_statistics()
# {'total_prints': 12, 'successful_prints': 9, 'pdf_fallbacks': 3,
#  'pdfs_generated': 41, 'errors': 0, 'success_rate': 91.3, ...}
```

Counters are maintained across every print, fallback and PDF generation the
instance performs, including work done on batch worker threads.

## Platform behaviour

| Platform | Mechanism |
| --- | --- |
| Linux, macOS | `lp` (CUPS); a printer name containing `pdf` selects print-to-PDF |
| Windows | Shell print verb via `rundll32`, or `win32print` for a named printer |

Print commands honour `print_command_timeout` and are retried
`print.retry_attempts` times with `print.retry_delay` between attempts.
Configuration errors fail fast without retrying, since repeating them cannot
help.

## Limitations

These are deliberate and worth knowing before you rely on the library:

- **Text only.** Generated PDFs contain text. There are no images, tables with
  borders, hyperlinks-as-annotations, or embedded fonts.
- **WinAnsi character set.** Output uses the base-14 fonts, which is what keeps
  the dependency count at zero. Characters outside WinAnsi (CJK, emoji, most
  box-drawing glyphs) are transliterated to ASCII or replaced with `?`.
  Characters WinAnsi *does* support — smart quotes, en/em dashes, bullet,
  ellipsis, accented Latin — render correctly.
- **Wrapping is not exact.** Line breaks are estimated from average glyph
  widths, so a few words near a page boundary may wrap differently than you
  would choose. Rendering fidelity, not typesetting, is the goal.
- **Printing to a named printer on Windows** requires the `windows` extra. The
  shell print verb cannot target a specific printer, and silently sending the
  job to the default printer instead would be worse than an explicit error.
- **PDF fallback is not a conversion.** It renders the file's *content* as
  text. A `.docx` fallback describes the file; it does not reproduce the
  original layout.

## API

`DocumentPrinter`

| Method | Purpose |
| --- | --- |
| `print_file(path, ...)` | Print a file of any type |
| `print_text(text, ...)` | Print text content |
| `print_document(value, ...)` | Dispatch on whether `value` is a path or content |
| `generate_pdf(value, ...)` | Render a PDF without printing |
| `print_batch(files, ...)` | Print many files concurrently |
| `generate_batch_pdfs(files, ...)` | Convert many files concurrently |
| `get_statistics()` | Counters and success rate |
| `reset_statistics()` | Clear counters |
| `get_file_info(path)` | Size, MIME type, readability |
| `validate_pdf(path)` | Structural check and page count |
| `get_supported_formats()` | Supported extensions by category |
| `get_config()` / `update_config()` / `save_config()` | Configuration |

## License

MIT. See [LICENSE](LICENSE).
