Files
netgazet/README.md
T
seppedlandClaude Opus 4.7 93174e1e09 Initial commit: PDF→static HTML/CSS site generator
main.py converts every PDF in examples/ into a browseable, mobile-responsive
HTML archive under output/ using poppler-utils. Includes the two NETgazet
sample PDFs, project metadata, OpenWolf scaffolding, and README covering
usage, a watch-folder script, and WordPress iframe embedding.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-03 16:00:07 +02:00

155 lines
5.1 KiB
Markdown

# netgazet
Tiny static-site generator for print-format PDFs (newsletters, magazines).
Drop PDFs into `examples/`, run `main.py`, and get a browseable, mobile-friendly
HTML/CSS archive in `output/`.
## What it does
For every `*.pdf` in `examples/` it renders each page as a JPEG with
poppler-utils (`pdftocairo`) and writes:
- `output/<pdf-stem>/page-NN.html` — one wrapper per page with prev/next
navigation and a collapsed text-extract panel (from `pdftotext -layout`).
- `output/<pdf-stem>/index.html` — thumbnail grid for the issue.
- `output/index.html` — root listing of all issues.
- `output/styles.css` — single responsive stylesheet, no JavaScript.
Layout fidelity (faithful to the print PDF) over text reflow; mobile
responsiveness is via `img { max-width: 100% }` and CSS grid auto-fill.
## Requirements
- Python ≥ 3.14 (see `.python-version`)
- `poppler-utils` on `$PATH` (`pdftocairo`, `pdftotext`, `pdfinfo`):
```bash
sudo apt install poppler-utils # Debian / Ubuntu
brew install poppler # macOS
```
No third-party Python dependencies — `pyproject.toml` keeps
`dependencies = []`.
## Usage
```bash
python main.py
# or:
uv run python main.py
```
Then open `output/index.html` in a browser. Each run wipes and rebuilds
`output/<stem>/` for every PDF in `examples/`, so it's idempotent.
## Auto-convert: rebuild when a PDF is dropped in `examples/`
Watch the folder with `inotifywait` (Linux; install `inotify-tools`):
```bash
sudo apt install inotify-tools
```
Save next to `main.py` as `watch.sh`:
```bash
#!/usr/bin/env bash
set -euo pipefail
cd "$(dirname "$0")"
WATCH_DIR="${1:-examples}"
echo "[watcher] watching $WATCH_DIR for new PDFs (Ctrl-C to stop)"
inotifywait -m -q -e close_write,moved_to --format '%f' "$WATCH_DIR" \
| while read -r filename; do
case "$filename" in
*.pdf|*.PDF)
echo "[watcher] $(date '+%H:%M:%S') new PDF: $filename — rebuilding"
python main.py
# Optional: push to your server right after rebuild
# rsync -avz --delete output/ user@server:/var/www/netgazet/
;;
esac
done
```
Make executable and run:
```bash
chmod +x watch.sh
./watch.sh
```
The script rebuilds the **whole** site on every new PDF (~30 s for two
16-page issues). Fine for a handful of newsletters; for dozens, extend
`main.py` to accept a single filename and only rebuild that issue plus
the root index.
On macOS, swap `inotifywait` for `fswatch -o "$WATCH_DIR" | while read; do ...; done`.
## Embedding in a WordPress page (iframe)
Once `output/` is hosted somewhere (a vhost on your server, Cloudflare Pages,
Netlify, etc.), embed it in any WordPress page or post:
1. In the block editor, add a **Custom HTML** block.
2. Paste:
```html
<iframe
src="https://netgazet.yourdomain.tld/"
style="width:100%; height:85vh; border:0;"
loading="lazy"
title="NETgazet archive">
</iframe>
```
3. Publish.
Notes:
- Use HTTPS for `src` if your WordPress site is on HTTPS — browsers block
mixed content.
- All navigation (issue → page → next page) stays inside the iframe, so the
WordPress page doesn't reload. Don't add `target="_top"` to links in the
generated HTML — that would break out of the frame.
- `height: 85vh` works on desktop and most phones. For a fixed value use
`height: 800px;` (or similar).
- Cross-domain iframe embeds need no special config, **unless** the static
host sends `X-Frame-Options: DENY` or
`Content-Security-Policy: frame-ancestors 'none'`. Nginx/Caddy don't add
these by default — only an issue if you explicitly enabled them.
### Subdomain vs subfolder (and auto-height)
The iframe `src` can point at any reachable URL, including a **subdomain** such
as `https://gazet.netwerk-antwerpen.be/`:
```html
<iframe
src="https://gazet.netwerk-antwerpen.be/"
style="width:100%; height:90vh; border:0;"
loading="lazy"
title="NETgazet archive">
</iframe>
```
That works out of the box (it's your own static files, so nothing blocks
framing). Two things to know:
- **HTTPS must match.** If WordPress is on `https://`, the subdomain must serve
`https://` too, or the browser blocks it as mixed content. Static hosts and
most shared hosting provide free Let's Encrypt certs — just enable it.
- **A subdomain is a *different origin*** from the WordPress domain. That has one
practical consequence: **cross-origin iframes can't auto-resize to their
content height.** The browser won't let the parent page read the iframe's
height, so the `postMessage` auto-height trick doesn't work. You get one of:
| Hosting | URL example | Auto-height |
|---|---|---|
| Subdomain (different origin) | `gazet.netwerk-antwerpen.be` | ❌ — use a fixed height (`90vh` / `1200px`), accept inner scrollbars |
| Subfolder (same origin) | `netwerk-antwerpen.be/gazet/` | ✅ — same-origin, `postMessage` auto-height works |
Pick a **subfolder** only if seamless auto-resizing matters more than the
cleaner subdomain URL. Otherwise a subdomain with a fixed `height` is the
simplest setup. (The *Advanced iFrame Pro* plugin can do cross-domain
auto-height too, but it needs a helper file dropped on the subdomain.)