# Latex PDF Preprocess

> Tool for preparing PDF figure files for embedding in LaTeX (e.g., with \includegraphics).

- Skill: `richiehakim/latex-pdf-preprocess` (Agent Skill)
- Install (CLI): `npx skillmds@latest add richiehakim/latex-pdf-preprocess`
- Raw SKILL.md: https://api.skillmd.com/api/skills/richiehakim/latex-pdf-preprocess/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: AI & ML
- Author: RichieHakim (https://skillmd.com/u/richiehakim)
- Updated: 2026-09-21
- Page: https://skillmd.com/skills/richiehakim/latex-pdf-preprocess

---


# LaTeX PDF Figure Preprocessing

Fix PDF figures (especially from Adobe Illustrator) so they embed cleanly in xelatex.

## The problem

Adobe Illustrator exports PDFs with ICC-based color profiles as indirect stream references inside page-level transparency groups. **xdvipdfmx** (xelatex's PDF backend) crashes on these:

```
Assertion failed: (!indirect->pf), function write_indirect, file pdfobj.c
```

## The fix

Replace the ICC color space reference in the page-level transparency `/Group` with `/DeviceRGB`. This is a single metadata swap — no rasterization, no PostScript roundtrip, no content changes. Transparency and blending are fully preserved.

## Core Command

```python
import pikepdf, sys

pdf = pikepdf.open(sys.argv[1])
for page in pdf.pages:
    if "/Group" in page:
        g = page["/Group"]
        if "/CS" in g and isinstance(g["/CS"], pikepdf.Array):
            g["/CS"] = pikepdf.Name("/DeviceRGB")
pdf.save(sys.argv[2])
```

Usage:
```bash
python3 fix_pdf.py input.pdf output.pdf
```

## Batch Processing

```python
import pikepdf, os

for d in sorted(os.listdir("figures")):
    fpath = os.path.join("figures", d, "figure.pdf")
    if not os.path.isfile(fpath):
        continue
    pdf = pikepdf.open(fpath)
    fixed = False
    for page in pdf.pages:
        if "/Group" in page:
            g = page["/Group"]
            if "/CS" in g and isinstance(g["/CS"], pikepdf.Array):
                g["/CS"] = pikepdf.Name("/DeviceRGB")
                fixed = True
    if fixed:
        tmp = fpath + ".tmp"
        pdf.save(tmp)
        pdf.close()
        os.replace(tmp, fpath)
        print(f"Fixed: {d}")
    else:
        pdf.close()
        print(f"OK:    {d} (no ICC group)")
```

## Dependencies

```bash
pip install pikepdf
```

## Verifying the Output

```bash
# Page dimensions should be identical
pdfinfo input.pdf | grep 'Page size'
pdfinfo output.pdf | grep 'Page size'
```

Or confirm the fix programmatically:
```python
import pikepdf
pdf = pikepdf.open("output.pdf")
for page in pdf.pages:
    if "/Group" in page and "/CS" in page["/Group"]:
        cs = page["/Group"]["/CS"]
        assert cs == pikepdf.Name("/DeviceRGB"), f"Still ICC: {cs}"
print("OK")
```

## Cropping whitespace

PDF figures (especially from Illustrator) often have excess whitespace from the artboard being larger than the artwork. Use `pdfcrop` (ships with TeX Live) to trim to content bounds:

```bash
pdfcrop --margins 2 input.pdf output.pdf
```

The `--margins` value is in bp (1bp = 1/72 inch). A small margin (1–2bp) prevents clipping artifacts at content edges.

Batch crop:
```bash
for f in figures/*.pdf; do
    pdfcrop --margins 2 "$f" "${f%.pdf}_cropped.pdf" && mv "${f%.pdf}_cropped.pdf" "$f"
done
```

## What this does NOT do

- Does not rasterize anything — vectors, images, and text are untouched
- Does not change PDF version
- Does not flatten transparency — blending modes and opacity are preserved
- Does not touch Form XObject groups (only the page-level group needs fixing)

## Background

The page-level `/Group` dictionary controls the transparency blending color space for the entire page. Illustrator sets this to `[/ICCBased <stream>]` where the stream is an embedded sRGB ICC profile. xdvipdfmx crashes when it encounters this indirect stream reference during PDF output. Replacing it with the equivalent `/DeviceRGB` name avoids the indirect reference while preserving identical color behavior (the ICC profile is sRGB).

