132 lines
10 KiB
Markdown
132 lines
10 KiB
Markdown
# effectcraft-pdf
|
||
|
||
Vector footage from PDF files, PDF-compatible Adobe Illustrator files (`.ai`, which are PDF
|
||
documents) and Encapsulated PostScript (`.eps`). Files parse into the same render tree as SVG
|
||
(`effectcraft_svg::Doc`), so they rasterise at any scale (Continuously Rasterize), convert to shape
|
||
layers (Layer ▸ Create ▸ Create Shapes from Vector Layer) and import as compositions with one
|
||
layer per file layer (File ▸ Import ▸ Composition).
|
||
|
||
Specifications used: **ISO 32000-1:2008** (PDF 1.7; the public Adobe edition) for the file
|
||
structure, filters, content streams, graphics state, colour spaces, functions, shadings, text,
|
||
fonts, images, patterns, transparency (blend modes, soft masks) and optional content; Adobe
|
||
**Technical Note #5176** (The Compact Font Format Specification) and **#5177** (The Type 2
|
||
Charstring Format) for CFF font programs; **Adobe Type 1 Font Format** (version 1.1) for Type 1
|
||
font programs; the **PostScript Language Reference, third edition** and the **Encapsulated
|
||
PostScript File Format Specification 3.0** for EPS; **ITU-T T.4** (07/2003) and **ITU-T T.6**
|
||
(11/1988) for CCITT fax images. TrueType font programs are read through skrifa and JPEG (DCT)
|
||
images through zune-jpeg (both permissively licensed, pure Rust). No third-party
|
||
PDF/PostScript/font/codec code was read; test files (including the CFF and Type 1 fonts and the
|
||
CCITT data) are generated in code ([`write`](src/write.rs) and the tests). One CCITT fixture was
|
||
produced once by libtiff's `tiffcp` (an external encoder used only as a fixture generator, like
|
||
ffmpeg elsewhere) to check the code tables against an independent implementation.
|
||
|
||
## What is read
|
||
|
||
- **Structure:** indirect objects found by scanning (damaged or incremental cross-reference
|
||
sections do not matter), object streams, the page tree with inherited resources, MediaBox /
|
||
CropBox and `/Rotate`. Filters: Flate (with PNG predictors), LZW, ASCIIHex, ASCII85,
|
||
RunLength, CCITTFax (Group 3 one- and two-dimensional, Group 4; `K`, `Columns`, `Rows`,
|
||
`EndOfLine`, `EncodedByteAlign`, `EndOfBlock`, `BlackIs1`; damaged data keeps the rows
|
||
decoded before the damage). Encrypted files are refused.
|
||
- **Content:** `q`/`Q`, `cm`, line width / cap / join / miter / dash, ExtGState (`CA`, `ca`, `LW`,
|
||
`LC`, `LJ`, `ML`, `D`), all path construction and painting operators (non-zero and even-odd),
|
||
clipping (`W`, `W*`) as clipping groups, colour in DeviceGray / RGB / CMYK, ICCBased (by
|
||
component count), CalGray / CalRGB, Lab, Indexed, Separation and DeviceN (tint transforms of
|
||
function types 0, 2 and 3), axial and radial shadings as pattern fills and through `sh`, form
|
||
XObjects (matrix, bounding-box clip, nested resources).
|
||
- **Layers:** top-level optional-content marked sequences (`/OC … BDC`, Illustrator's layers)
|
||
become the document's layers, named after their OCG; content outside them is gathered into
|
||
layers "Layer N".
|
||
- **EPS:** a PostScript interpreter for the subset exported vector EPS uses: operand and
|
||
dictionary stacks, procedures and `def`/`bind`/`load`/`where`, arithmetic, comparisons,
|
||
`if`/`ifelse`/`for`/`repeat`/`loop`/`forall`, arrays and strings, `gsave`/`grestore`,
|
||
`save`/`restore`, matrices (`concat`, `translate`, `scale`, `rotate`, `transform` …), path
|
||
construction including `arc`/`arcn` and relative operators, `fill`/`eofill`/`stroke`,
|
||
`clip`/`eoclip`, `rectfill`/`rectstroke`/`rectclip`, gray / RGB / CMYK / HSB colour. DOS EPS
|
||
binary headers and `%%HiResBoundingBox` / `%%BoundingBox` are honoured; `%%BeginData` /
|
||
`%%BeginBinary` / preview sections are skipped. **Text:** `findfont` / `findresource`,
|
||
`scalefont`, `makefont`, `selectfont`, `setfont`, `currentfont`, `definefont` (re-encoded
|
||
copies made with `forall` and a new `/Encoding`), `StandardEncoding` / `ISOLatin1Encoding`,
|
||
`show`, `ashow`, `widthshow`, `awidthshow`, `xshow` / `yshow` / `xyshow`, `kshow` / `cshow`
|
||
(the procedure is not run), `glyphshow`, `charpath` (glyph outlines join the path, e.g. for
|
||
clipping) and `stringwidth`. Fonts embedded in the file — Type 1 programs (`… currentfile
|
||
eexec … cleartomark`, binary or hexadecimal) and CFF font sets (`StartData`) — are found by
|
||
name before the program runs; other fonts are drawn with the bundled fonts as for PDF. Each
|
||
`show` becomes one compound shape named "Text: …".
|
||
|
||
- **Text** (§9): text objects and state (`Tc`, `Tw`, `Tz`, `TL`, `Ts`, `Tr`, `Tf`, `Td`,
|
||
`TD`, `Tm`, `T*`), `Tj`, `TJ` (with kerning), `'` and `"`; render modes fill / stroke /
|
||
fill-and-stroke / invisible and the clipping modes (glyph outlines clip until `Q`). Glyphs
|
||
become outlines (one compound shape per show operator, named "Text: …" after the string
|
||
through `ToUnicode` or the glyph names). Fonts: simple fonts with `/Widths`, base encodings
|
||
(Standard, WinAnsi, MacRoman), `/Differences` and built-in encodings; composite (Type 0) fonts
|
||
with Identity-H / Identity-V and embedded CMaps (`cidrange` / `cidchar`, `/WMode`), `/W` and
|
||
`/DW` widths, vertical metrics (`/W2`, `/DW2`: the vertical displacement and the position
|
||
vector from the horizontal to the vertical origin) and `CIDToGIDMap`. Embedded programs: TrueType (`FontFile2`, cmap (3,1) / (3,0) /
|
||
(1,0) and `post` names), CFF (`FontFile3` `/Type1C`, `/CIDFontType0C`, `/OpenType`;
|
||
name-keyed and CID-keyed with FDArray / FDSelect, flex, hint masks, `seac`), Type 1
|
||
(`FontFile`, eexec, `/Subrs`, flex and hint replacement through othersubrs, `seac`) and Type 3
|
||
glyph procedures. Fonts that are not embedded (the standard 14 and others) are drawn with
|
||
the bundled OFL fonts by name: Inter (sans serif, Regular / Italic / SemiBold / Bold), Noto
|
||
Serif (Times and other serif names) and JetBrains Mono (Courier), with their own advances
|
||
when the PDF gives no widths.
|
||
- **Images** (§8.9): image XObjects and inline images (`BI … ID … EI`, abbreviated keys);
|
||
1, 2, 4, 8 and 16 bits per component with `/Decode`; any colour space above (Indexed through
|
||
its palette, ICCBased through its `/Alternate` space); DCT (JPEG, through zune-jpeg; Adobe
|
||
inverted CMYK) and the general filters; stencil masks (`/ImageMask`) in the fill colour;
|
||
soft masks (`/SMask` images) and colour-key and explicit `/Mask`s. Images become image nodes
|
||
of the render tree (bilinear, box-filtered when minified).
|
||
- **Transparency:** blend modes (`/BM`, all 16 PDF modes) and soft masks in the graphics state
|
||
(`/SMask` with `/S /Luminosity` and `/BC` backdrops, or `/S /Alpha`) as groups of the render
|
||
tree, open until the state changes or `Q`: what is painted while a soft mask is set is
|
||
rendered to an offscreen buffer and the mask applies to that combined result. Transparency
|
||
group XObjects (§11.6.6) composite as one object: the current alpha, blend mode and soft mask
|
||
apply to the group's result, and its contents start from the initial alpha. Isolated groups
|
||
(`/I true`) composite over a transparent backdrop; non-isolated groups over the page, with
|
||
the backdrop's contribution removed again (§11.4.8) so group alpha and masks apply only to
|
||
the group; knockout groups (`/K true`) composite each child with the group's initial
|
||
backdrop. Clipping groups are not transparency groups: blend modes inside them reach the
|
||
backdrop.
|
||
- **Patterns:** tiling patterns (coloured and uncoloured, `XStep` / `YStep`, the cell clipped
|
||
to `/BBox`, at most 4096 tiles per fill) for fills; strokes with a tiling pattern use its
|
||
colour (uncoloured) or grey.
|
||
- **Shadings** (§8.7.4.5): axial and radial shadings become gradients. Function-based shadings
|
||
(type 1, `Domain` and `Matrix`) and the mesh shadings — free-form (type 4, edge flags 0–2)
|
||
and lattice-form (type 5) Gouraud-shaded triangle meshes, Coons patch meshes (type 6) and
|
||
tensor-product patch meshes (type 7, flags 0–3 sharing the previous patch's edges) with
|
||
`BitsPerCoordinate` / `BitsPerComponent` / `BitsPerFlag` / `Decode` and an optional
|
||
parametric `Function` — are rasterised into an image node in page space (up to 4 pixels per
|
||
point, at most 1024 pixels a side, 2×2 supersampled): patches are tessellated into triangles
|
||
(up to 48×48 cells), colours (or `t`) interpolated across each triangle. They paint through
|
||
`sh` (their own extent only) and as shading-pattern fills (clipped to the path).
|
||
- **Functions:** sampled (one or several inputs, multilinear interpolation), exponential,
|
||
stitching and PostScript calculator (type 4) functions.
|
||
- **Pages:** any page ([`parse_page`], [`page_count`]); File ▸ Import takes `page` (from 1) and
|
||
the Import dialog asks for it; footage remembers its page (`Footage::page`).
|
||
|
||
## Limitations
|
||
|
||
- JPX and JBIG2 images, predefined CJK CMaps other than Identity-H / Identity-V, and transfer
|
||
functions (`/TR`) are not read. JBIG2 is deferred. Predefined CMaps (`UniGB-UCS2-H`,
|
||
`90ms-RKSJ-H` …) need the CMap data, and there is no permissively licensed pure-Rust source
|
||
of it (Adobe's CMap resources are Adobe assets, which this project does not ship); text in
|
||
such fonts is reported in `Doc::skipped`. Mesh shadings are raster images in the render tree
|
||
(sharp to about 4× the document size). Knockout uses each child's coverage as its shape (a
|
||
child with constant opacity knocks out fully where it covers). What a document skipped is
|
||
listed in `Doc::skipped`.
|
||
- Illustrator EPS files whose drawing depends on Adobe's procedure-set resources (defined in
|
||
the file through constructs beyond this subset) and Level 3 `shfill` gradients in EPS draw
|
||
only what the subset understands; save such artwork as PDF-compatible `.ai` or PDF instead.
|
||
EPS images are skipped; Type 3 (procedure) fonts defined in EPS files draw nothing (reported).
|
||
- **Create Shapes from Vector Layer:** clips that enclose the whole page become layer masks
|
||
(the first clip Add, later ones Intersect). Nested clipping groups become Merge Paths: each
|
||
closed shape inside them is a group with its geometry merged into one compound path, the clip
|
||
paths, and Merge Paths ▸ Intersect before its fill and stroke, so fills match exactly and
|
||
strokes follow the clipped outline; open stroked paths inside clips stay unclipped. Blend
|
||
modes become the groups' Blend Mode. Images become footage layers: each is written as
|
||
`<file> Image <n>.png` next to the source and imported, placed by its matrix (anchor at its
|
||
top-left corner; position, rotation and scale — a skew is reported) and parented to the shape
|
||
layer; the shapes drawn between images go to shape layers of their own (`<name> Outlines
|
||
<n>`), so the layer stack keeps the document's paint order. Soft masks, and the clips and
|
||
masks of images, are not converted (the command's `skipped` list says so).
|