# 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.)
