gutterpress 0.11.20-alpha.2 → 0.11.20-alpha.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/api/index.js +3 -2
- package/dist/{build-qmjg9r2d.js → build-epdcrm2c.js} +5 -4
- package/dist/{cli-zacevwcy.js → cli-0ey902sx.js} +267 -455
- package/dist/{cli-j802s31j.js → cli-61vd4ews.js} +83 -41
- package/dist/{cli-7z29z4wz.js → cli-fmksazej.js} +1 -1
- package/dist/{cli-gh0ym80r.js → cli-h4tfmpg3.js} +121 -20
- package/dist/cli-sth219v1.js +418 -0
- package/dist/cli.js +12 -12
- package/dist/{doctor-fe7tcs4v.js → doctor-78es5scm.js} +2 -2
- package/dist/engine/compiler/build.d.ts +1 -1
- package/dist/engine/shared/margin-box-support.d.ts +2 -2
- package/dist/engine/shared/pin-edges.d.ts +127 -0
- package/dist/engine/shared/print-area.d.ts +68 -0
- package/dist/{engine-9xb1ky8d.js → engine-67f68bxs.js} +1 -1
- package/dist/{engine-yax79k9m.js → engine-7ha27n2b.js} +2 -2
- package/dist/{ext-ahwxt6gv.js → ext-9mc7sr0d.js} +5 -4
- package/dist/{gutterpress-agent-cazqstr1.js → gutterpress-agent-mrdnwktc.js} +78 -13
- package/dist/{gutterpress-viewer-retj6ym2.js → gutterpress-viewer-jr30yw8t.js} +155 -56
- package/dist/{index-6xwtb7cw.js → index-ad95fws8.js} +82 -40
- package/dist/{index-3nr5rzzp.js → index-gm5xbx5w.js} +3 -3
- package/dist/index-sth219v1.js +418 -0
- package/dist/{index-razpty47.js → index-t13p8y08.js} +493 -582
- package/dist/index.js +4 -3
- package/dist/lib/build-fingerprint.d.ts +8 -0
- package/dist/lib/extension-manifest.d.ts +13 -0
- package/dist/lib/ghostscript.d.ts +70 -16
- package/dist/lib/markdown/assemble.d.ts +12 -2
- package/dist/lib/markdown/gutterpress-css.d.ts +46 -30
- package/dist/lib/markdown/index.d.ts +5 -0
- package/dist/lib/pdf-inspect.d.ts +45 -0
- package/dist/{manifest.schema-74s1ykyv.json → manifest.schema-13dfax80.json} +16 -4
- package/dist/{new-ftd4fec4.js → new-s4f0tnya.js} +5 -4
- package/dist/pdf-inspect-zdmy7qca.js +35 -0
- package/dist/{preflight-8sqarcsr.js → preflight-7ck4rbsq.js} +5 -4
- package/dist/preview/file-watcher.d.ts +4 -10
- package/dist/{preview-q76xn95h.js → preview-j91sxadd.js} +5 -4
- package/dist/{publish-7gdy6gbf.js → publish-f12xaenm.js} +5 -4
- package/dist/render.js +57 -6
- package/dist/schema/manifest.types.d.ts +28 -8
- package/dist/test-helpers/testkit.d.ts +2 -2
- package/dist/{validate-e3ryegce.js → validate-pb5gysvr.js} +5 -4
- package/package.json +1 -1
- package/dist/engine/shared/flush.d.ts +0 -59
package/dist/index.js
CHANGED
|
@@ -154,14 +154,15 @@ import {
|
|
|
154
154
|
testRemoteAccess,
|
|
155
155
|
updateExtensions,
|
|
156
156
|
validateProjectExtensions
|
|
157
|
-
} from "./index-
|
|
157
|
+
} from "./index-t13p8y08.js";
|
|
158
158
|
import"./index-ht1bcqdd.js";
|
|
159
159
|
import {
|
|
160
160
|
BUILD_DIAGNOSTIC_CODES
|
|
161
|
-
} from "./index-
|
|
161
|
+
} from "./index-ad95fws8.js";
|
|
162
162
|
import {
|
|
163
163
|
BuildError
|
|
164
|
-
} from "./index-
|
|
164
|
+
} from "./index-gm5xbx5w.js";
|
|
165
|
+
import"./index-sth219v1.js";
|
|
165
166
|
import {
|
|
166
167
|
DEFAULT_PRINT_OPTS,
|
|
167
168
|
assertMilestone,
|
|
@@ -5,6 +5,14 @@ type PdfxFingerprintConfig = {
|
|
|
5
5
|
stripAnnotations: boolean | null;
|
|
6
6
|
/** How near-black text was separated (#331); null when no PDF/X pass ran. Provenance only. */
|
|
7
7
|
blackText?: "icc" | "k-only" | null;
|
|
8
|
+
/**
|
|
9
|
+
* How pages with transparency were handled (#385): `pdfx.transparency`,
|
|
10
|
+
* `pdfx.flattenPages` and `pdfx.rasterDpi`. null when no PDF/X pass ran
|
|
11
|
+
* (and, for rasterDpi, when unset). Provenance only.
|
|
12
|
+
*/
|
|
13
|
+
transparency?: "flatten-page" | "preserve-text" | null;
|
|
14
|
+
flattenPages?: number[] | null;
|
|
15
|
+
rasterDpi?: number | null;
|
|
8
16
|
};
|
|
9
17
|
export type BuildFingerprintInput = {
|
|
10
18
|
command: "build";
|
|
@@ -70,6 +70,12 @@ export interface ExtensionMetadata {
|
|
|
70
70
|
* does the merge.
|
|
71
71
|
*/
|
|
72
72
|
snippets?: string;
|
|
73
|
+
/**
|
|
74
|
+
* npm `engines.gutterpress` — the Gutterpress versions the extension works
|
|
75
|
+
* with, as a semver range (e.g. `">=0.11.20-alpha.3"`). Checked when the
|
|
76
|
+
* extension loads ({@link assertSupportsThisGutterpress}).
|
|
77
|
+
*/
|
|
78
|
+
gutterpressRange?: string;
|
|
73
79
|
/**
|
|
74
80
|
* The removed `engineStyles` field (#266), carried through from the
|
|
75
81
|
* `gutterpress` block ONLY so {@link assertExtensionContained} can reject it
|
|
@@ -119,6 +125,13 @@ export declare function extensionCarries(meta: ExtensionMetadata): ExtensionCarr
|
|
|
119
125
|
* doctrine {@link readExtensionMeta}'s tolerant JSON parse already follows).
|
|
120
126
|
*/
|
|
121
127
|
export declare function pathEscapesFolder(rel: string): boolean;
|
|
128
|
+
/**
|
|
129
|
+
* Refuse to load an extension whose `engines.gutterpress` range excludes this
|
|
130
|
+
* Gutterpress, so a book built with too old a version fails with the fix
|
|
131
|
+
* instead of quietly printing differently. Prereleases count: 0.12.0-alpha.1
|
|
132
|
+
* satisfies `>=0.11.20`.
|
|
133
|
+
*/
|
|
134
|
+
export declare function assertSupportsThisGutterpress(meta: ExtensionMetadata, ref: string): void;
|
|
122
135
|
/**
|
|
123
136
|
* Every path an extension declares must live INSIDE its own folder — an
|
|
124
137
|
* extension is self-contained by contract (apply/install copies the whole
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { UnsplittableReason } from "./pdf-inspect";
|
|
1
2
|
/**
|
|
2
3
|
* Resolve the Ghostscript command on every supported platform.
|
|
3
4
|
*
|
|
@@ -8,38 +9,39 @@
|
|
|
8
9
|
*/
|
|
9
10
|
export declare function resolveGhostscript(targetPlatform?: NodeJS.Platform, env?: NodeJS.ProcessEnv): Promise<string | undefined>;
|
|
10
11
|
/**
|
|
11
|
-
*
|
|
12
|
-
* feature, in ALL the forms Chromium emits it, not just
|
|
12
|
+
* The 1-based page numbers of `pdfBytes` that use live PDF transparency — the
|
|
13
|
+
* PDF 1.4 imaging feature, in ALL the forms Chromium emits it, not just
|
|
14
|
+
* alpha-channel images:
|
|
13
15
|
*
|
|
14
16
|
* - a transparency **group** (`/Group << /S /Transparency >>`), which is what
|
|
15
17
|
* CSS `opacity`, `mix-blend-mode` and friends compile down to;
|
|
16
18
|
* - an **image soft mask** (`/SMask` on an image XObject, pointing at a real
|
|
17
19
|
* mask rather than the literal `/None` an opaque image also carries) — an
|
|
18
20
|
* RGBA source image;
|
|
19
|
-
* - a **graphics-state** soft mask
|
|
20
|
-
* on an `/ExtGState`)
|
|
21
|
+
* - a **graphics-state** soft mask, constant alpha (`/ca`, `/CA` below 1) or
|
|
22
|
+
* non-Normal blend mode (`/BM`) on an `/ExtGState` — soft `rgba()`
|
|
23
|
+
* shadows and fills.
|
|
21
24
|
*
|
|
22
25
|
* PDF/X-1a and PDF/X-3 (as this codebase generates them, `-dCompatibilityLevel=1.3`
|
|
23
26
|
* in {@link convertToPdfxCmyk}) are both based on PDF 1.3, which predates the
|
|
24
27
|
* PDF 1.4 transparency model entirely — live transparency has nothing a
|
|
25
28
|
* PDF-1.3 file can represent it with. Ghostscript's only PDF/X-compliant
|
|
26
29
|
* option is to flatten the page it appears ON down to a single raster image
|
|
27
|
-
* (B.10, measured:
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
* discovering a fontless PDF/X file after the fact.
|
|
30
|
+
* (B.10, measured: text and fonts on that page are lost, identically on both
|
|
31
|
+
* engines — a Ghostscript/PDF-1.3 property, not an engine bug).
|
|
32
|
+
* {@link flattenTransparentPages} (opt-in, `pdfx.transparency: preserve-text`)
|
|
33
|
+
* can instead keep the text live on the pages this returns.
|
|
32
34
|
*
|
|
33
|
-
* This walks
|
|
34
|
-
* scan is NOT sufficient and produced a false "all clear" in review: image
|
|
35
|
+
* This walks each page's parsed object graph rather than scanning raw bytes. A
|
|
36
|
+
* byte scan is NOT sufficient and produced a false "all clear" in review: image
|
|
35
37
|
* XObjects are streams, so their dicts do sit uncompressed at top level, but
|
|
36
38
|
* `/ExtGState` and `/Group` dicts are plain objects that Chromium packs into
|
|
37
39
|
* **object streams** (`/Type /ObjStm`, deflate-compressed), where no regex can
|
|
38
|
-
* see them.
|
|
39
|
-
*
|
|
40
|
-
*
|
|
40
|
+
* see them. Fonts, `/Parent` (which leads to every other page) and `/Annots`
|
|
41
|
+
* are not followed: an annotation's appearance stream is not page content, and
|
|
42
|
+
* PDF/X builds strip annotations before conversion.
|
|
41
43
|
*/
|
|
42
|
-
export declare function
|
|
44
|
+
export declare function transparentPages(pdfBytes: Uint8Array): Promise<number[]>;
|
|
43
45
|
/**
|
|
44
46
|
* 1-based numbers of the pages that paint a `/ShadingType 1` shading whose
|
|
45
47
|
* function is `/FunctionType 4` (a PostScript calculator function).
|
|
@@ -56,7 +58,7 @@ export declare function hasLiveTransparency(pdfBytes: Uint8Array): Promise<boole
|
|
|
56
58
|
*
|
|
57
59
|
* Walks page resources (and form XObjects they draw) rather than every
|
|
58
60
|
* object, so only shadings a page can actually paint are reported. Like
|
|
59
|
-
* {@link
|
|
61
|
+
* {@link transparentPages}, it never fails a build: an unparseable PDF
|
|
60
62
|
* reports no pages.
|
|
61
63
|
*/
|
|
62
64
|
export declare function findPostScriptShadingPages(pdfBytes: Uint8Array): Promise<number[]>;
|
|
@@ -81,6 +83,58 @@ export declare function findPostScriptShadingPages(pdfBytes: Uint8Array): Promis
|
|
|
81
83
|
* directly (pdf-lib, in-process, no second external tool).
|
|
82
84
|
*/
|
|
83
85
|
export declare function stripAnnotations(pdfPath: string, stagingDir: string): Promise<void>;
|
|
86
|
+
/** Why a transparent page is flattened whole under `preserve-text` instead of split. */
|
|
87
|
+
export type FallbackCause = Exclude<UnsplittableReason, "no-text"> | "listed" | "gradient";
|
|
88
|
+
/**
|
|
89
|
+
* Sort each page that uses transparency (1-based, from {@link transparentPages})
|
|
90
|
+
* for `pdfx.transparency: preserve-text`:
|
|
91
|
+
* - `split`: its artwork is flattened and its text kept live
|
|
92
|
+
* ({@link flattenTransparentPages});
|
|
93
|
+
* - `artOnly`: it shows no text, so there is nothing to keep and Ghostscript
|
|
94
|
+
* flattens it whole, which is as good;
|
|
95
|
+
* - `kept`: it has text but is flattened whole anyway, with the cause the
|
|
96
|
+
* build reports. The author's `pdfx.flattenPages` list wins, then the
|
|
97
|
+
* text's own reason (`unsplittableTextReasons`), then a repeating or conic
|
|
98
|
+
* gradient (#386), whose PostScript-function shading the artwork pass
|
|
99
|
+
* distorts.
|
|
100
|
+
* Every transparent page lands in exactly one group.
|
|
101
|
+
*/
|
|
102
|
+
export declare function planTransparentPages(transparent: number[], reasons: Map<number, UnsplittableReason>, flattenPages: number[], gradientPages: number[]): {
|
|
103
|
+
split: number[];
|
|
104
|
+
artOnly: number[];
|
|
105
|
+
kept: Map<number, FallbackCause>;
|
|
106
|
+
};
|
|
107
|
+
/**
|
|
108
|
+
* Make the pages in `pages` (1-based; the `split` group of
|
|
109
|
+
* {@link planTransparentPages}) free of transparency while keeping
|
|
110
|
+
* their text live, so PDF/X (PDF 1.3) conversion has nothing left to flatten
|
|
111
|
+
* into a single bitmap. Opt-in: `pdfx.transparency: preserve-text`.
|
|
112
|
+
*
|
|
113
|
+
* Per affected page, two Ghostscript passes split it in two and pdf-lib
|
|
114
|
+
* stacks the halves back on the ORIGINAL page object:
|
|
115
|
+
*
|
|
116
|
+
* 1. ARTWORK — `pdfimage24 -dFILTERTEXT -r<dpi>` drops the glyphs and
|
|
117
|
+
* composites everything left (soft shadows, alpha images, opacity) into
|
|
118
|
+
* one opaque image (300 DPI unless `pdfx.rasterDpi` says otherwise),
|
|
119
|
+
* straight from the source;
|
|
120
|
+
* 2. TEXT — `-dFILTERIMAGE -dFILTERVECTOR` keeps only the glyphs. It needs
|
|
121
|
+
* `-dNOTRANSPARENCY`: without it Chromium's transparency groups and
|
|
122
|
+
* `/ca`/`/SMask` ExtGStates survive in the text-only file and the next
|
|
123
|
+
* step flattens the page anyway. That is only faithful for text with no
|
|
124
|
+
* transparency of its own, which is why callers leave other pages out;
|
|
125
|
+
* 3. the page's content is replaced by the artwork with the text drawn on
|
|
126
|
+
* top. Editing the original page keeps its boxes (Media/Crop/Trim/Bleed),
|
|
127
|
+
* annotations, the document's metadata, outlines and labels exactly as
|
|
128
|
+
* they were; only its `/Contents`, `/Resources` and `/Group` change.
|
|
129
|
+
*
|
|
130
|
+
* Pages not listed are never touched. Known limits, both consequences of
|
|
131
|
+
* splitting a page into "picture" and "text": the page's vector chrome
|
|
132
|
+
* (rules, backgrounds, shapes) becomes the picture, and text is always the
|
|
133
|
+
* top layer, so text another element covers shows through.
|
|
134
|
+
*/
|
|
135
|
+
export declare function flattenTransparentPages(pdfPath: string, pageNumbers: number[], stagingDir: string,
|
|
136
|
+
/** artwork resolution (`pdfx.rasterDpi`) */
|
|
137
|
+
dpi?: number): Promise<void>;
|
|
84
138
|
type PdfxFlavor = "x1a" | "x3";
|
|
85
139
|
/**
|
|
86
140
|
* Stamp the Creator metadata field on an existing PDF.
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { type LoadedPlugin } from "./renderer";
|
|
2
2
|
import { type PrintPadding } from "../../engine/shared/synthesis.ts";
|
|
3
|
+
import { type PrintAreaSource } from "../../engine/shared/print-area.ts";
|
|
3
4
|
/** Reader injected by the host: resolve a project-root-relative file → its text. */
|
|
4
5
|
export type ReadText = (relPath: string) => Promise<string>;
|
|
5
6
|
/**
|
|
@@ -9,8 +10,8 @@ export type ReadText = (relPath: string) => Promise<string>;
|
|
|
9
10
|
* `type`s (`ambiguous_marker_token`, `unrecognized_marker_token`,
|
|
10
11
|
* `extra_bare_marker_token`, `unknown_marker`, `nested_spread`,
|
|
11
12
|
* `continue_without_section`, `spread_without_pages`, `spread_eof_close`,
|
|
12
|
-
* `page_outside_spread`, `pin_outside_page`, `unknown_gp_class` —
|
|
13
|
-
* emitted by `gp-pin-scope.js`'s `gp_pin_scope_check`, same as
|
|
13
|
+
* `page_outside_spread`, `pin_outside_page`, `unknown_gp_class`, `flush_art` —
|
|
14
|
+
* the last two emitted by `gp-pin-scope.js`'s `gp_pin_scope_check`, same as
|
|
14
15
|
* `pin_outside_page`, see #226).
|
|
15
16
|
*
|
|
16
17
|
* #240 (declarative container components) adds three more, all emitted by
|
|
@@ -126,6 +127,15 @@ export interface AssembleBookHtmlOptions {
|
|
|
126
127
|
* engine/shared/synthesis.ts) and the two page counts agree.
|
|
127
128
|
*/
|
|
128
129
|
print?: PrintPadding;
|
|
130
|
+
/**
|
|
131
|
+
* The project's print area — the resolved manifest's `page` block plus its
|
|
132
|
+
* preset id and printer spec — written as `<meta name="gp-print-area">`
|
|
133
|
+
* (engine/shared/print-area.ts) into EVERY assembled book, preview, build
|
|
134
|
+
* and publish alike: the preview's print overlays judge the ink against it,
|
|
135
|
+
* and both engines place pinned art on its art-safe line. Metadata only:
|
|
136
|
+
* nothing renders from it but what reads it.
|
|
137
|
+
*/
|
|
138
|
+
printArea?: PrintAreaSource;
|
|
129
139
|
plugins?: LoadedPlugin[];
|
|
130
140
|
pluginCss?: string;
|
|
131
141
|
/** Add a layout-neutral source-file id to source-mapped preview blocks. */
|
|
@@ -31,7 +31,10 @@
|
|
|
31
31
|
* .gp-full — fills the page's content width.
|
|
32
32
|
* .gp-small — width 25% of the column. Sizes compose with any
|
|
33
33
|
* .gp-medium — width 50%. position (including .gp-pin, where
|
|
34
|
-
* .gp-large — width
|
|
34
|
+
* .gp-large — width 60%. the % resolves against the page
|
|
35
|
+
* .gp-xl — width 80%. root: the page box, reaching the
|
|
36
|
+
* .gp-xxl — width 90%. paper on an edge a pin uses).
|
|
37
|
+
* There is no 100% step: that is .gp-full, a position.
|
|
35
38
|
* .gp-tight — float/shape clearance 0.5em (sets --gp-gap; default 1em).
|
|
36
39
|
* .gp-loose — float/shape clearance 2em (sets --gp-gap).
|
|
37
40
|
* .gp-shape — wraps text to a floated image's alpha silhouette
|
|
@@ -47,16 +50,14 @@
|
|
|
47
50
|
* frame instead, BY DESIGN: that is how a pin is scoped to
|
|
48
51
|
* a card rather than a sheet. Page-level furniture must
|
|
49
52
|
* therefore be authored outside such a wrapper.
|
|
50
|
-
* Centered
|
|
51
|
-
* modifiers .gp-top/.gp-bottom/.gp-left/.gp-right
|
|
52
|
-
*
|
|
53
|
-
* edge
|
|
54
|
-
*
|
|
55
|
-
*
|
|
56
|
-
*
|
|
57
|
-
*
|
|
58
|
-
* which a stylesheet can do. Inert without .gp-pin + an
|
|
59
|
-
* edge word, and inert under plain markdown-it.
|
|
53
|
+
* Centered in the text block unless combined with the
|
|
54
|
+
* edge modifiers .gp-top/.gp-bottom/.gp-left/.gp-right;
|
|
55
|
+
* an edge pin sits on the printer's ART-SAFE line of that
|
|
56
|
+
* edge and never moves the text — see "the pin box" below.
|
|
57
|
+
* .gp-flush — with .gp-pin + an edge, the escape hatch: the art runs
|
|
58
|
+
* to the PAPER's edge, past the printer's safe area. It
|
|
59
|
+
* only zeroes the pin box's insets (the rule after the
|
|
60
|
+
* edge modifiers). Inert without .gp-pin + an edge word.
|
|
60
61
|
* .gp-bleed — forces its own page (break-before) and spans it
|
|
61
62
|
* edge-to-edge horizontally. This does NOT cancel the
|
|
62
63
|
* top/bottom margins, extend past the trim into printer
|
|
@@ -102,12 +103,12 @@
|
|
|
102
103
|
* paragraph with other inline content keeps its margin.
|
|
103
104
|
*
|
|
104
105
|
* Rule ORDER within the gp-* block is the contract: flow positions → sizes
|
|
105
|
-
* → spacing → .gp-pin → pin-edge modifiers. Everything is
|
|
106
|
-
* specificity, so combining classes resolves by source order: a
|
|
107
|
-
* position wins (left < right < center < full < bleed); an
|
|
108
|
-
* max-width:100% lifts the floats' 50% cap; .gp-pin's
|
|
109
|
-
* max-width:100% beat the float declarations; the edge modifiers
|
|
110
|
-
* .gp-pin's center defaults.
|
|
106
|
+
* → spacing → .gp-pin → pin-edge modifiers → .gp-flush. Everything else is
|
|
107
|
+
* flat 0-1-0 specificity, so combining classes resolves by source order: a
|
|
108
|
+
* later flow position wins (left < right < center < full < bleed); an
|
|
109
|
+
* explicit size's max-width:100% lifts the floats' 50% cap; .gp-pin's
|
|
110
|
+
* margin:0 / max-width:100% beat the float declarations; the edge modifiers
|
|
111
|
+
* beat .gp-pin's center defaults.
|
|
111
112
|
*
|
|
112
113
|
* .gp-pin semantics, stated honestly: it pins within its @page/@spread
|
|
113
114
|
* CONTAINER, not "the sheet edge". A .page div that fragments across
|
|
@@ -120,12 +121,29 @@
|
|
|
120
121
|
* compiler's engine.abspos.leak diagnostic catches the raw-HTML cases the
|
|
121
122
|
* token walk can't see.
|
|
122
123
|
*
|
|
124
|
+
* The PIN BOX — where a pin may sit, and why the text never moves. An edge
|
|
125
|
+
* pin has to be able to reach the paper, and Chromium's printable area IS
|
|
126
|
+
* the page area (engine/shared/pin-edges.ts has the measurements), so both
|
|
127
|
+
* engines free the page margin on each edge a pin names: print through a
|
|
128
|
+
* generated named page, the preview through the run's strip geometry. The
|
|
129
|
+
* page root gets that margin back as padding, on top of any of its own
|
|
130
|
+
* (`box-decoration-break: clone`), so its content box — the text — is the
|
|
131
|
+
* author's content box, same size, same place, same breaks, wherever the art
|
|
132
|
+
* is pinned. The pin's containing
|
|
133
|
+
* block is the root's padding box, and the engine sets `--gp-pin-<edge>` on
|
|
134
|
+
* the root to the art-safe line's distance from the sheet edge (the bleed
|
|
135
|
+
* plus page.artSafe on the top, bottom and outside edge; page.safe at the
|
|
136
|
+
* binding, from the manifest); `.gp-pin` reads them as its four insets.
|
|
137
|
+
* `.gp-flush` zeroes them on the pin: the paper's edge. Under plain
|
|
138
|
+
* markdown-it nothing sets them and every inset is 0.
|
|
139
|
+
*
|
|
123
140
|
* Pin CSS mechanics (all three are load-bearing, verified by
|
|
124
|
-
*
|
|
125
|
-
* self-alignment aligns within the inset-modified containing block
|
|
126
|
-
* with auto insets that collapses to the static-position
|
|
127
|
-
* alignment does nothing. The center defaults must be
|
|
128
|
-
* alignment behaves as `start` for abspos replaced
|
|
141
|
+
* marker-css-image-pin.test.ts): four non-auto insets are REQUIRED — abspos
|
|
142
|
+
* self-alignment aligns within the inset-modified containing block (the pin
|
|
143
|
+
* box), and with auto insets that collapses to the static-position
|
|
144
|
+
* rectangle, where alignment does nothing. The center defaults must be
|
|
145
|
+
* EXPLICIT — `normal` alignment behaves as `start` for abspos replaced
|
|
146
|
+
* elements, not center.
|
|
129
147
|
* And `justify-self` on .gp-left/.gp-right does double duty (flow float +
|
|
130
148
|
* pin edge) because self-alignment does not apply to in-flow floats — it is
|
|
131
149
|
* inert until .gp-pin makes the element abspos. If Chrome ever ships
|
|
@@ -133,16 +151,14 @@
|
|
|
133
151
|
* carrying gp-left could start shifting; that is standards-tracking per
|
|
134
152
|
* CLAUDE.md ("Chrome wins once it ships"), not a bug in the author's book.
|
|
135
153
|
*/
|
|
136
|
-
export declare const GUTTERPRESS_CSS = "\n/* gp-* author image/block vocabulary. One vocabulary, gp-* only \u2014 the\n pre-vocabulary utility names (.center/.float-left/.float-right/\n .full-width/.full-bleed) were REMOVED when gp-* shipped; books rename\n the classes in their markdown (see the migration note). Source ORDER is\n the contract \u2014 see the doctrine comment above. */\n\n/* flow positions */\n.gp-left {\n float: left;\n margin: 0 var(--gp-gap, 1em) var(--gp-gap, 1em) 0;\n max-width: 50%;\n}\n.gp-right {\n float: right;\n margin: 0 0 var(--gp-gap, 1em) var(--gp-gap, 1em);\n max-width: 50%;\n}\n.gp-center {\n display: block;\n float: none;\n margin-left: auto;\n margin-right: auto;\n max-width: 100%;\n}\n.gp-full {\n display: block;\n float: none;\n width: 100%;\n max-width: 100%;\n}\n@page gp-full-bleed { margin-left: 0; margin-right: 0; }\n.gp-bleed {\n display: block;\n float: none;\n break-before: page;\n page: gp-full-bleed;\n max-width: none;\n width: 100%;\n margin-left: 0;\n margin-right: 0;\n}\n\n/* sizes \u2014 AFTER the flow positions so max-width:100% lifts the floats' 50%\n cap at equal specificity */\n.gp-small { width: 25%; max-width: 100%; }\n.gp-medium { width: 50%; max-width: 100%; }\n.gp-large { width: 75%; max-width: 100%; }\n\n/* float clearance presets \u2014 consumed by var(--gp-gap) in the float rules\n above and by .gp-shape's shape-margin below; --gp-gap itself is\n author-settable CSS */\n.gp-tight { --gp-gap: 0.5em; }\n.gp-loose { --gp-gap: 2em; }\n\n/* column runs \u2014 plain CSS Multi-column, emitted by the core @columns marker\n (markers.js): @columns [1-5] ... @end-columns is the ONE way to lay\n content out in columns, and @column-break moves to the next column.\n The classes below are the CSS that marker emits, not author vocabulary.\n\n Permanent vocabulary, not a shim: Chromium implements multicol natively\n and these rules are the standard properties verbatim, so there is no\n spec gap here to remove later. Deliberately minimal \u2014 column-fill is\n NOT set (the CSS initial balance applies).\n Themes style a run through two tokens instead of writing column rules:\n --gp-column-gap default 1.5em\n --gp-column-rule default none (any column-rule shorthand value)\n Both hooks sit at zero specificity (:where), so any author rule wins.\n .gp-columns is two columns on its own; a count class (bare @columns\n emits gp-columns-2) wins because it comes later at the same specificity. */\n:where(.gp-columns, .gp-columns-1, .gp-columns-2, .gp-columns-3, .gp-columns-4, .gp-columns-5) {\n column-gap: var(--gp-column-gap, 1.5em);\n column-rule: var(--gp-column-rule, none);\n}\n.gp-columns { columns: 2; }\n.gp-columns-1 { columns: 1; }\n.gp-columns-2 { columns: 2; }\n.gp-columns-3 { columns: 3; }\n.gp-columns-4 { columns: 4; }\n.gp-columns-5 { columns: 5; }\n\n/* fragmentation controls \u2014 generic pagination utilities for any element,\n independent of the column vocabulary above (a book reached for\n these just as often outside a multicol run: keeping a card whole,\n forcing a section to start a fresh page). Standard properties only \u2014\n this project is Chromium-only (CLAUDE.md), so there are no legacy\n page-break-* twins to also emit. One name each. */\n.gp-no-break { break-inside: avoid; }\n.gp-break-before { break-before: page; }\n\n/* shape wrap \u2014 text follows the image's alpha silhouette instead of its\n rectangular box. shape-outside only applies to floats, so this is inert\n without .gp-left/.gp-right (and under .gp-pin, which un-floats). The\n shape URL cannot be written in CSS (url() contexts can't read attr()),\n so the image renderer rule (images.ts) mirrors the src into an inline\n --gp-shape:url(...) custom property whenever it sees this class --\n authors only ever type the class. threshold 0.2 ignores near-transparent\n anti-aliasing halos; shape-margin shares the float-gap vocabulary. */\nimg.gp-shape {\n shape-outside: var(--gp-shape);\n shape-image-threshold: 0.2;\n shape-margin: var(--gp-gap, 1em);\n}\n\n/* pin \u2014 within the nearest positioned ancestor (.page/.spread, rule above).\n inset:0 and the explicit centers are load-bearing; see doctrine comment. */\n.gp-pin {\n position: absolute;\n inset: 0;\n align-self: center;\n justify-self: center;\n margin: 0;\n max-width: 100%;\n}\n\n/* pin edge modifiers \u2014 AFTER .gp-pin to beat its center defaults;\n justify-self is inert on in-flow floats, so gp-left/gp-right safely do\n double duty as flow float + pin edge */\n.gp-top { align-self: start; }\n.gp-bottom { align-self: end; }\n.gp-left { justify-self: start; }\n.gp-right { justify-self: end; }\n\n/* wrapper-margin neutralization (same pattern and rationale as the\n .gp-bleed paragraph-margin note in the doctrine comment; for pin, the\n emptied paragraph would otherwise leave a phantom margin gap in flow) */\n:where(p:has(> img.gp-bleed:only-child)) { margin: 0; }\n:where(p:has(> img.gp-pin:only-child)) { margin: 0; }\n\n/* depth \u2014 a named ladder for z-index, so books stop hand-tuning bare\n integers. A real book measured 21 z-index declarations using only four\n distinct values (-1, 0, 1, 2), each written literally at its use site.\n The custom properties are the author-settable surface (a book needing a\n deeper stack raises them once); the classes are the shorthand.\n\n NOT named \"layer\": CSS Paged Media 3 \u00A73.1 already defines \"page layers\"\n (page background, canvas, borders, contents, margin boxes) and those are\n parts of the PAGE BOX, not a z-ladder for content. Reusing the word for a\n different concept would collide with the spec vocabulary this project\n tracks. The pin EDGE modifiers already own .gp-top/.gp-bottom, so the\n ladder avoids those words too.\n\n .gp-behind is the one that earns its place: it puts a pinned image UNDER\n the page's text, which is otherwise impossible to express without a bare\n negative z-index. \"Above\" needs no class \u2014 an out-of-flow pin already\n paints above in-flow content.\n\n Two things silently defeat .gp-behind, neither visible at the use site:\n - a stacking context on the .page/.spread ancestor (z-index, isolation,\n opacity, filter, transform on it traps the negative layer inside).\n Core keeps .page/.spread at 'position: relative; z-index: auto'\n precisely so they are not stacking contexts.\n - a clipping ancestor (overflow other than visible) \u2014 but only where\n the art actually overhangs that ancestor's clip box on a clipped\n axis: the overhang is cut off, the same mechanism that clips a\n .gp-bleed plate back to the wrapper's width. Clipping never reorders\n layers \u2014 within-bounds art under a clipping .page prints whole and\n still behind (measured; see the build audit's comment in\n engine/compiler/build.ts), and a static wrapper's overflow never\n binds an abspos .gp-pin at all.\n The build-time engine.layer.trapped audit reports both against the live\n ancestor chain. printsafe/page-containment is only an early source hint for\n declarations written directly on .page/.spread. */\n:root {\n --gp-z-behind: -1;\n --gp-z-base: 0;\n --gp-z-raised: 1;\n --gp-z-front: 2;\n}\n.gp-behind { z-index: var(--gp-z-behind); }\n.gp-base { z-index: var(--gp-z-base); }\n.gp-raised { z-index: var(--gp-z-raised); }\n.gp-front { z-index: var(--gp-z-front); }\n\n/* GFM-style alert/callout boxes (#237) \u2014 the DOM gfm-alerts.ts emits from\n \"> [!TYPE]\" blockquote syntax when a project opts into the bundled\n gutterpress-gfm-alerts feature (BUILTIN_OPTIONAL_PLUGINS, renderer.ts).\n The plugin is optional and inert by default; these rules are equally\n inert wherever the classes never appear, exactly like every other\n utility above, so shipping them costs nothing to a book that has not\n turned the feature on.\n\n This is authored-COMPONENT vocabulary, not the @marker structural family\n (markers.js/MARKER_CSS) \u2014 CLAUDE.md \u00A76 splits the two core CSS blocks by\n ROLE, and an author-facing box an author invokes via inline syntax is the\n same role as every other gp-* utility here, not page/chapter/section DOM.\n\n Deliberately minimal (CLAUDE.md \"thin over capable\" / the issue's own\n \"a rule and a label\"): a left border plus a bold label, :where()'d to\n zero specificity so a theme's own (unlayered) rule for any of these\n classes wins outright regardless of selector weight \u2014 see the\n cascade-layers note atop this file. Five semantic accent colors (the same\n NOTE/TIP/IMPORTANT/WARNING/CAUTION hues GitHub's own alerts use \u2014 a\n long-established, non-proprietary convention, not DC branding) are\n exposed as :root custom properties, the same author-settable-surface\n pattern as the --gp-z-* ladder above: a book retints every alert of one\n type by overriding a single property, no specificity fight required.\n --gp-alert-color cascades from the type modifier down to .gp-alert-title\n through ordinary CSS inheritance (the title is always a DOM descendant of\n the box), so it is set in exactly five places, not ten. */\n:root {\n --gp-alert-note-color: #0969da;\n --gp-alert-tip-color: #1a7f37;\n --gp-alert-important-color: #8250df;\n --gp-alert-warning-color: #9a6700;\n --gp-alert-caution-color: #cf222e;\n}\n:where(.gp-alert) {\n margin: 1em 0;\n padding: 0.5em 1em;\n border-left: 0.25em solid var(--gp-alert-color, currentColor);\n}\n:where(.gp-alert-title) {\n margin: 0 0 0.5em;\n font-weight: bold;\n color: var(--gp-alert-color, inherit);\n}\n:where(.gp-alert-note) { --gp-alert-color: var(--gp-alert-note-color); }\n:where(.gp-alert-tip) { --gp-alert-color: var(--gp-alert-tip-color); }\n:where(.gp-alert-important) { --gp-alert-color: var(--gp-alert-important-color); }\n:where(.gp-alert-warning) { --gp-alert-color: var(--gp-alert-warning-color); }\n:where(.gp-alert-caution) { --gp-alert-color: var(--gp-alert-caution-color); }\n";
|
|
154
|
+
export declare const GUTTERPRESS_CSS = "\n/* gp-* author image/block vocabulary. One vocabulary, gp-* only \u2014 the\n pre-vocabulary utility names (.center/.float-left/.float-right/\n .full-width/.full-bleed) were REMOVED when gp-* shipped; books rename\n the classes in their markdown (see the migration note). Source ORDER is\n the contract \u2014 see the doctrine comment above. */\n\n/* flow positions */\n.gp-left {\n float: left;\n margin: 0 var(--gp-gap, 1em) var(--gp-gap, 1em) 0;\n max-width: 50%;\n}\n.gp-right {\n float: right;\n margin: 0 0 var(--gp-gap, 1em) var(--gp-gap, 1em);\n max-width: 50%;\n}\n.gp-center {\n display: block;\n float: none;\n margin-left: auto;\n margin-right: auto;\n max-width: 100%;\n}\n.gp-full {\n display: block;\n float: none;\n width: 100%;\n max-width: 100%;\n}\n@page gp-full-bleed { margin-left: 0; margin-right: 0; }\n.gp-bleed {\n display: block;\n float: none;\n break-before: page;\n page: gp-full-bleed;\n max-width: none;\n width: 100%;\n margin-left: 0;\n margin-right: 0;\n}\n\n/* sizes \u2014 AFTER the flow positions so max-width:100% lifts the floats' 50%\n cap at equal specificity */\n.gp-small { width: 25%; max-width: 100%; }\n.gp-medium { width: 50%; max-width: 100%; }\n.gp-large { width: 60%; max-width: 100%; }\n.gp-xl { width: 80%; max-width: 100%; }\n.gp-xxl { width: 90%; max-width: 100%; }\n\n/* float clearance presets \u2014 consumed by var(--gp-gap) in the float rules\n above and by .gp-shape's shape-margin below; --gp-gap itself is\n author-settable CSS */\n.gp-tight { --gp-gap: 0.5em; }\n.gp-loose { --gp-gap: 2em; }\n\n/* column runs \u2014 plain CSS Multi-column, emitted by the core @columns marker\n (markers.js): @columns [1-5] ... @end-columns is the ONE way to lay\n content out in columns, and @column-break moves to the next column.\n The classes below are the CSS that marker emits, not author vocabulary.\n\n Permanent vocabulary, not a shim: Chromium implements multicol natively\n and these rules are the standard properties verbatim, so there is no\n spec gap here to remove later. Deliberately minimal \u2014 column-fill is\n NOT set (the CSS initial balance applies).\n Themes style a run through two tokens instead of writing column rules:\n --gp-column-gap default 1.5em\n --gp-column-rule default none (any column-rule shorthand value)\n Both hooks sit at zero specificity (:where), so any author rule wins.\n .gp-columns is two columns on its own; a count class (bare @columns\n emits gp-columns-2) wins because it comes later at the same specificity. */\n:where(.gp-columns, .gp-columns-1, .gp-columns-2, .gp-columns-3, .gp-columns-4, .gp-columns-5) {\n column-gap: var(--gp-column-gap, 1.5em);\n column-rule: var(--gp-column-rule, none);\n}\n.gp-columns { columns: 2; }\n.gp-columns-1 { columns: 1; }\n.gp-columns-2 { columns: 2; }\n.gp-columns-3 { columns: 3; }\n.gp-columns-4 { columns: 4; }\n.gp-columns-5 { columns: 5; }\n\n/* fragmentation controls \u2014 generic pagination utilities for any element,\n independent of the column vocabulary above (a book reached for\n these just as often outside a multicol run: keeping a card whole,\n forcing a section to start a fresh page). Standard properties only \u2014\n this project is Chromium-only (CLAUDE.md), so there are no legacy\n page-break-* twins to also emit. One name each. */\n.gp-no-break { break-inside: avoid; }\n.gp-break-before { break-before: page; }\n\n/* shape wrap \u2014 text follows the image's alpha silhouette instead of its\n rectangular box. shape-outside only applies to floats, so this is inert\n without .gp-left/.gp-right (and under .gp-pin, which un-floats). The\n shape URL cannot be written in CSS (url() contexts can't read attr()),\n so the image renderer rule (images.ts) mirrors the src into an inline\n --gp-shape:url(...) custom property whenever it sees this class --\n authors only ever type the class. threshold 0.2 ignores near-transparent\n anti-aliasing halos; shape-margin shares the float-gap vocabulary. */\nimg.gp-shape {\n shape-outside: var(--gp-shape);\n shape-image-threshold: 0.2;\n shape-margin: var(--gp-gap, 1em);\n}\n\n/* pin \u2014 within the nearest positioned ancestor (.page/.spread, rule above).\n The four insets are the pin box: 0 (the text block's edge) unless an\n engine set --gp-pin-<edge> on the page root for an edge a pin uses (the\n art-safe line). Non-auto insets and the explicit centers are\n load-bearing; see doctrine comment. */\n.gp-pin {\n position: absolute;\n top: var(--gp-pin-top, 0px);\n right: var(--gp-pin-right, 0px);\n bottom: var(--gp-pin-bottom, 0px);\n left: var(--gp-pin-left, 0px);\n align-self: center;\n justify-self: center;\n margin: 0;\n max-width: 100%;\n}\n\n/* pin edge modifiers \u2014 AFTER .gp-pin to beat its center defaults;\n justify-self is inert on in-flow floats, so gp-left/gp-right safely do\n double duty as flow float + pin edge */\n.gp-top { align-self: start; }\n.gp-bottom { align-self: end; }\n.gp-left { justify-self: start; }\n.gp-right { justify-self: end; }\n\n/* the escape hatch \u2014 the paper's edge, past the printer's safe area: the\n pin box loses its art-safe insets. Set on the pin itself, so it beats the\n values the page root hands down. */\n.gp-pin.gp-flush {\n --gp-pin-top: 0px;\n --gp-pin-right: 0px;\n --gp-pin-bottom: 0px;\n --gp-pin-left: 0px;\n}\n\n/* wrapper-margin neutralization (same pattern and rationale as the\n .gp-bleed paragraph-margin note in the doctrine comment; for pin, the\n emptied paragraph would otherwise leave a phantom margin gap in flow) */\n:where(p:has(> img.gp-bleed:only-child)) { margin: 0; }\n:where(p:has(> img.gp-pin:only-child)) { margin: 0; }\n\n/* depth \u2014 a named ladder for z-index, so books stop hand-tuning bare\n integers. A real book measured 21 z-index declarations using only four\n distinct values (-1, 0, 1, 2), each written literally at its use site.\n The custom properties are the author-settable surface (a book needing a\n deeper stack raises them once); the classes are the shorthand.\n\n NOT named \"layer\": CSS Paged Media 3 \u00A73.1 already defines \"page layers\"\n (page background, canvas, borders, contents, margin boxes) and those are\n parts of the PAGE BOX, not a z-ladder for content. Reusing the word for a\n different concept would collide with the spec vocabulary this project\n tracks. The pin EDGE modifiers already own .gp-top/.gp-bottom, so the\n ladder avoids those words too.\n\n .gp-behind is the one that earns its place: it puts a pinned image UNDER\n the page's text, which is otherwise impossible to express without a bare\n negative z-index. \"Above\" needs no class \u2014 an out-of-flow pin already\n paints above in-flow content.\n\n Two things silently defeat .gp-behind, neither visible at the use site:\n - a stacking context on the .page/.spread ancestor (z-index, isolation,\n opacity, filter, transform on it traps the negative layer inside).\n Core keeps .page/.spread at 'position: relative; z-index: auto'\n precisely so they are not stacking contexts.\n - a clipping ancestor (overflow other than visible) \u2014 but only where\n the art actually overhangs that ancestor's clip box on a clipped\n axis: the overhang is cut off, the same mechanism that clips a\n .gp-bleed plate back to the wrapper's width. Clipping never reorders\n layers \u2014 within-bounds art under a clipping .page prints whole and\n still behind (measured; see the build audit's comment in\n engine/compiler/build.ts), and a static wrapper's overflow never\n binds an abspos .gp-pin at all.\n The build-time engine.layer.trapped audit reports both against the live\n ancestor chain. printsafe/page-containment is only an early source hint for\n declarations written directly on .page/.spread. */\n:root {\n --gp-z-behind: -1;\n --gp-z-base: 0;\n --gp-z-raised: 1;\n --gp-z-front: 2;\n}\n.gp-behind { z-index: var(--gp-z-behind); }\n.gp-base { z-index: var(--gp-z-base); }\n.gp-raised { z-index: var(--gp-z-raised); }\n.gp-front { z-index: var(--gp-z-front); }\n\n/* GFM-style alert/callout boxes (#237) \u2014 the DOM gfm-alerts.ts emits from\n \"> [!TYPE]\" blockquote syntax when a project opts into the bundled\n gutterpress-gfm-alerts feature (BUILTIN_OPTIONAL_PLUGINS, renderer.ts).\n The plugin is optional and inert by default; these rules are equally\n inert wherever the classes never appear, exactly like every other\n utility above, so shipping them costs nothing to a book that has not\n turned the feature on.\n\n This is authored-COMPONENT vocabulary, not the @marker structural family\n (markers.js/MARKER_CSS) \u2014 CLAUDE.md \u00A76 splits the two core CSS blocks by\n ROLE, and an author-facing box an author invokes via inline syntax is the\n same role as every other gp-* utility here, not page/chapter/section DOM.\n\n Deliberately minimal (CLAUDE.md \"thin over capable\" / the issue's own\n \"a rule and a label\"): a left border plus a bold label, :where()'d to\n zero specificity so a theme's own (unlayered) rule for any of these\n classes wins outright regardless of selector weight \u2014 see the\n cascade-layers note atop this file. Five semantic accent colors (the same\n NOTE/TIP/IMPORTANT/WARNING/CAUTION hues GitHub's own alerts use \u2014 a\n long-established, non-proprietary convention, not DC branding) are\n exposed as :root custom properties, the same author-settable-surface\n pattern as the --gp-z-* ladder above: a book retints every alert of one\n type by overriding a single property, no specificity fight required.\n --gp-alert-color cascades from the type modifier down to .gp-alert-title\n through ordinary CSS inheritance (the title is always a DOM descendant of\n the box), so it is set in exactly five places, not ten. */\n:root {\n --gp-alert-note-color: #0969da;\n --gp-alert-tip-color: #1a7f37;\n --gp-alert-important-color: #8250df;\n --gp-alert-warning-color: #9a6700;\n --gp-alert-caution-color: #cf222e;\n}\n:where(.gp-alert) {\n margin: 1em 0;\n padding: 0.5em 1em;\n border-left: 0.25em solid var(--gp-alert-color, currentColor);\n}\n:where(.gp-alert-title) {\n margin: 0 0 0.5em;\n font-weight: bold;\n color: var(--gp-alert-color, inherit);\n}\n:where(.gp-alert-note) { --gp-alert-color: var(--gp-alert-note-color); }\n:where(.gp-alert-tip) { --gp-alert-color: var(--gp-alert-tip-color); }\n:where(.gp-alert-important) { --gp-alert-color: var(--gp-alert-important-color); }\n:where(.gp-alert-warning) { --gp-alert-color: var(--gp-alert-warning-color); }\n:where(.gp-alert-caution) { --gp-alert-color: var(--gp-alert-caution-color); }\n";
|
|
137
155
|
/**
|
|
138
156
|
* GP_CLASSES — every class an author may legitimately write with a `gp-`
|
|
139
157
|
* prefix: the `.gp-*` selectors in `GUTTERPRESS_CSS` above, the two
|
|
140
158
|
* structural classes `markers.js`'s `MARKER_CSS` styles (`gp-page-break`,
|
|
141
|
-
* `gp-column-break`), and
|
|
142
|
-
* no CSS rule of
|
|
143
|
-
* for author/theme styling — see markers.js's header)
|
|
144
|
-
* `.gp-pin` edge modifier implemented in the engine's layout code, not CSS —
|
|
145
|
-
* see the `.gp-flush` doctrine note above).
|
|
159
|
+
* `gp-column-break`), and one class the marker plugin EMITS but that carries
|
|
160
|
+
* no CSS rule of its own — `gp-continued` (a `@continue`d section's marker,
|
|
161
|
+
* for author/theme styling — see markers.js's header).
|
|
146
162
|
*
|
|
147
163
|
* This is the vocabulary `gp-pin-scope.js`'s `unknown_gp_class` diagnostic
|
|
148
164
|
* (#226) checks every author-facing class against: any `gp-`-prefixed class
|
|
@@ -151,8 +167,8 @@ export declare const GUTTERPRESS_CSS = "\n/* gp-* author image/block vocabulary.
|
|
|
151
167
|
* classes are none of core's business.
|
|
152
168
|
*
|
|
153
169
|
* `gutterpress-css.test.ts` asserts this set and the `.gp-*` selectors
|
|
154
|
-
* textually present in `GUTTERPRESS_CSS` + `MARKER_CSS` agree (modulo the
|
|
155
|
-
* marker-only
|
|
170
|
+
* textually present in `GUTTERPRESS_CSS` + `MARKER_CSS` agree (modulo the
|
|
171
|
+
* marker-only exception above), so the two cannot silently drift apart —
|
|
156
172
|
* whoever adds a class to one CSS block and forgets this list finds out from
|
|
157
173
|
* a failing test, not from a future bug report.
|
|
158
174
|
*/
|
|
@@ -2,6 +2,7 @@ import { type LayoutWarning } from "./assemble";
|
|
|
2
2
|
import { type AssetCopy } from "../asset-inline";
|
|
3
3
|
import type { LoadedPlugin, PluginStyleGroup } from "./renderer";
|
|
4
4
|
import type { PrintPadding } from "../../engine/shared/synthesis.ts";
|
|
5
|
+
import type { PrintAreaSource } from "../../engine/shared/print-area.ts";
|
|
5
6
|
export type { LayoutWarning } from "./assemble";
|
|
6
7
|
/**
|
|
7
8
|
* THE canonical "which markdown files make up this book, and in what order?"
|
|
@@ -33,6 +34,8 @@ export declare function renderChapters(inputDir: string, opts?: {
|
|
|
33
34
|
title?: string;
|
|
34
35
|
/** See {@link AssembleBookHtmlOptions.print}. */
|
|
35
36
|
print?: PrintPadding;
|
|
37
|
+
/** See {@link AssembleBookHtmlOptions.printArea}. */
|
|
38
|
+
printArea?: PrintAreaSource;
|
|
36
39
|
styles?: string[];
|
|
37
40
|
files?: string[] | null;
|
|
38
41
|
plugins?: LoadedPlugin[];
|
|
@@ -72,6 +75,8 @@ export declare function renderChaptersToFile(inputDir: string, outDir: string, o
|
|
|
72
75
|
title?: string;
|
|
73
76
|
/** See {@link AssembleBookHtmlOptions.print}. */
|
|
74
77
|
print?: PrintPadding;
|
|
78
|
+
/** See {@link AssembleBookHtmlOptions.printArea}. */
|
|
79
|
+
printArea?: PrintAreaSource;
|
|
75
80
|
styles?: string[];
|
|
76
81
|
files?: string[] | null;
|
|
77
82
|
plugins?: LoadedPlugin[];
|
|
@@ -134,6 +134,51 @@ export interface OpPass {
|
|
|
134
134
|
fonts: FontRef[];
|
|
135
135
|
}
|
|
136
136
|
export declare function getOpPass(doc: PDFDocumentProxy): Promise<OpPass>;
|
|
137
|
+
/**
|
|
138
|
+
* Why a page is not split from its artwork: its text cannot be (the first
|
|
139
|
+
* three), or it shows no text at all, so there is none to keep (`no-text`).
|
|
140
|
+
*/
|
|
141
|
+
export type UnsplittableReason = "text-transparency" | "type3-font" | "text-pattern-or-clip" | "no-text";
|
|
142
|
+
/**
|
|
143
|
+
* For each 1-based page that `pdfx.transparency: preserve-text` must not split,
|
|
144
|
+
* the first reason found; pages absent from the map can be split. `pages`
|
|
145
|
+
* limits the scan to those pages (the build passes the transparent ones only,
|
|
146
|
+
* so the rest of the book is never decoded).
|
|
147
|
+
*
|
|
148
|
+
* A page's text cannot be split when text SHOWS while transparency is in effect: constant alpha
|
|
149
|
+
* (`ca`/`CA` < 1), a non-Normal blend mode, an active soft mask, or any
|
|
150
|
+
* enclosing transparency group that is composited translucent, blended or
|
|
151
|
+
* masked, or is a knockout group. (Chromium draws `opacity` and
|
|
152
|
+
* `mix-blend-mode` content as a group form XObject under such a state, and
|
|
153
|
+
* resets the state INSIDE the group, so the state at the point the group
|
|
154
|
+
* begins is what counts.) Such text cannot be lifted out of its page and drawn
|
|
155
|
+
* opaque on top of a flattened picture without changing how it looks, so
|
|
156
|
+
* `pdfx.transparency: preserve-text` leaves those pages alone.
|
|
157
|
+
*
|
|
158
|
+
* Also flagged: any page that sets a Type 3 font. Chromium (Skia) emits Type 3
|
|
159
|
+
* for glyphs it can only draw as paths, e.g. the synthetic bold of a
|
|
160
|
+
* single-weight webfont. Their glyph procedures are vector paths, which the
|
|
161
|
+
* text pass's `-dFILTERVECTOR` strips, so the text would print nothing.
|
|
162
|
+
*
|
|
163
|
+
* Also flagged: text painted with a pattern (`scn`/`SCN` to a tiling pattern
|
|
164
|
+
* or shading), as Chromium emits for SVG `<text fill="url(#…)">` and gradient
|
|
165
|
+
* text fills, and text used as a clip (render modes 4-7, `background-clip:
|
|
166
|
+
* text`). The artwork pass removes the glyphs and the text pass's
|
|
167
|
+
* `-dFILTERIMAGE`/`-dFILTERVECTOR` removes their paint, so they would vanish.
|
|
168
|
+
*
|
|
169
|
+
* A plain isolated group composited at alpha 1 / Normal / no mask is NOT
|
|
170
|
+
* counted: painting it is the same as painting its content inline, and
|
|
171
|
+
* Chromium wraps the content of EVERY page in one as soon as any element of
|
|
172
|
+
* the document uses a blend mode (measured on Chromium 141), which would
|
|
173
|
+
* otherwise rule out every page of such a book. The page's OWN `/Group` is
|
|
174
|
+
* likewise ignored.
|
|
175
|
+
*
|
|
176
|
+
* Walks PDF.js's operator list, which has already inlined form XObjects
|
|
177
|
+
* between `paintFormXObjectBegin/End` and reports their `/Group` as
|
|
178
|
+
* `beginGroup/endGroup`. Graphics state is tracked per save/restore (a form
|
|
179
|
+
* XObject is an implicit save/restore).
|
|
180
|
+
*/
|
|
181
|
+
export declare function unsplittableTextReasons(pdfBytes: Uint8Array, pages?: number[]): Promise<Map<number, UnsplittableReason>>;
|
|
137
182
|
/**
|
|
138
183
|
* Does page `pageNum` (1-based) paint anything at all? False for a page with
|
|
139
184
|
* no content and for one whose operators only set state or clip. A path op
|
|
@@ -155,9 +155,21 @@
|
|
|
155
155
|
"enum": ["icc", "k-only"],
|
|
156
156
|
"default": "icc"
|
|
157
157
|
},
|
|
158
|
+
"transparency": {
|
|
159
|
+
"type": "string",
|
|
160
|
+
"description": "What a PDF/X build does with a page that uses transparency (an alpha-channel image, a soft `rgba()` shadow, CSS `opacity` or `mix-blend-mode`), which PDF/X (PDF 1.3) cannot represent. `flatten-page` leaves the page to Ghostscript, which rasterizes the WHOLE page: it looks right but loses the page's text and fonts. `preserve-text` flattens only the artwork to a picture (300 DPI, or `rasterDpi`) and draws the text live on top, so the page stays searchable. Text is then always the top layer (text another element covers shows through). A page whose text is translucent or blended, set in a Type 3 font (e.g. a synthetic bold or a text stroke), filled with a pattern or used as a clip, or that has a repeating or conic gradient, falls back to `flatten-page`, and so does any page listed in `flattenPages`. Set `validate.assets.allowAlpha: true` to use alpha-channel images. Same for every preset: `flatten-page`.",
|
|
161
|
+
"enum": ["flatten-page", "preserve-text"],
|
|
162
|
+
"default": "flatten-page"
|
|
163
|
+
},
|
|
164
|
+
"flattenPages": {
|
|
165
|
+
"type": "array",
|
|
166
|
+
"description": "With `transparency: preserve-text`: pages (1-based, the numbers the build reports) left to Ghostscript's whole-page flattening even if they could be split, e.g. a page whose look depends on a cover or tint painted over its text. Page numbers move when the book reflows. Default: none.",
|
|
167
|
+
"items": { "type": "integer", "minimum": 1 },
|
|
168
|
+
"examples": [[4, 13]]
|
|
169
|
+
},
|
|
158
170
|
"rasterDpi": {
|
|
159
171
|
"type": "integer",
|
|
160
|
-
"description": "Resolution (DPI) of
|
|
172
|
+
"description": "Resolution (DPI) of every picture made from a page that uses transparency: the whole pages Ghostscript flattens and the `preserve-text` artwork. Vector content and the book's own images are not resampled. Unset: Ghostscript's default (720) for whole pages, 300 for `preserve-text` artwork.",
|
|
161
173
|
"minimum": 72,
|
|
162
174
|
"maximum": 1200,
|
|
163
175
|
"examples": [300]
|
|
@@ -210,19 +222,19 @@
|
|
|
210
222
|
},
|
|
211
223
|
"bleed": {
|
|
212
224
|
"type": "number",
|
|
213
|
-
"description": "A bleed, in points, that the page width x height ALREADY CONTAINS on the top, bottom and OUTER edges (none on the binding edge) — how DriveThruRPG's interior file is cut (8.625 x 11.25in for an 8.5 x 11in book). It tells the preview's safe area & bleed overlay
|
|
225
|
+
"description": "A bleed, in points, that the page width x height ALREADY CONTAINS on the top, bottom and OUTER edges (none on the binding edge) — how DriveThruRPG's interior file is cut (8.625 x 11.25in for an 8.5 x 11in book). It tells the preview's safe area & bleed overlay, the `pdf.print.safe-margins` check and pinned art where the trim is, and by itself changes nothing in the PDF. Preset default: dtrpg 9 (0.125in); book and custom none.",
|
|
214
226
|
"minimum": 0,
|
|
215
227
|
"examples": [9]
|
|
216
228
|
},
|
|
217
229
|
"safe": {
|
|
218
230
|
"type": "number",
|
|
219
|
-
"description": "The TEXT rule, in points: how far inside the trim glyphs must stay, on every edge. For DriveThruRPG it is the \"text at least 0.5in inside the trim\" rule. The preview's overlay outlines it (dashed) and flags text outside it, and the `pdf.print.safe-margins` check fails a PDF whose text is closer.
|
|
231
|
+
"description": "The TEXT rule, in points: how far inside the trim glyphs must stay, on every edge. For DriveThruRPG it is the \"text at least 0.5in inside the trim\" rule. The preview's overlay outlines it (dashed) and flags text outside it, and the `pdf.print.safe-margins` check fails a PDF whose text is closer. An image pinned to the binding edge (`.gp-pin`) sits on it, and to any edge where `artSafe` is not set; no book CSS moves it. Preset default: dtrpg 36 (0.5in), book 18 (0.25in); custom none.",
|
|
220
232
|
"minimum": 0,
|
|
221
233
|
"examples": [18, 36]
|
|
222
234
|
},
|
|
223
235
|
"artSafe": {
|
|
224
236
|
"type": "number",
|
|
225
|
-
"description": "The ART rule, in points: how far inside the trim art that does not bleed (images, backgrounds, borders, rules) must stay on the top, bottom and OUTER edge; at the binding edge art uses `safe`. For DriveThruRPG it is the \"non-bleeding art at least 0.25in from the outside edges (0.5in at the binding)\" rule. Art that runs out to the page's edge is a bleed and is not judged on that edge. The preview's overlay outlines it (dotted) and flags art outside it
|
|
237
|
+
"description": "The ART rule, in points: how far inside the trim art that does not bleed (images, backgrounds, borders, rules) must stay on the top, bottom and OUTER edge; at the binding edge art uses `safe`. For DriveThruRPG it is the \"non-bleeding art at least 0.25in from the outside edges (0.5in at the binding)\" rule. Art that runs out to the page's edge is a bleed and is not judged on that edge. The preview's overlay outlines it (dotted) and flags art outside it, and an image pinned to the top, bottom or outer edge (`.gp-pin`) sits on it unless it carries `.gp-flush`. Absent = the same as `safe`. Preset default: dtrpg 18 (0.25in); book and custom none.",
|
|
226
238
|
"minimum": 0,
|
|
227
239
|
"examples": [18]
|
|
228
240
|
}
|
|
@@ -7,20 +7,21 @@ import {
|
|
|
7
7
|
TARGET_IDS,
|
|
8
8
|
scaffoldExtension,
|
|
9
9
|
scaffoldProject
|
|
10
|
-
} from "./cli-
|
|
10
|
+
} from "./cli-0ey902sx.js";
|
|
11
11
|
import {
|
|
12
12
|
resolveGhostscript
|
|
13
|
-
} from "./cli-
|
|
13
|
+
} from "./cli-h4tfmpg3.js";
|
|
14
14
|
import {
|
|
15
15
|
UsageError,
|
|
16
16
|
rejectExtraPositionals,
|
|
17
17
|
rejectUnknownFlags
|
|
18
|
-
} from "./cli-
|
|
18
|
+
} from "./cli-fmksazej.js";
|
|
19
19
|
import"./cli-ht1bcqdd.js";
|
|
20
|
-
import"./cli-
|
|
20
|
+
import"./cli-61vd4ews.js";
|
|
21
21
|
import {
|
|
22
22
|
EXIT_CODES
|
|
23
23
|
} from "./cli-46ycxe6r.js";
|
|
24
|
+
import"./cli-sth219v1.js";
|
|
24
25
|
import"./cli-eyyggect.js";
|
|
25
26
|
import {
|
|
26
27
|
isToolAvailable
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import {
|
|
2
|
+
DOC_CACHE_MAX,
|
|
3
|
+
clearPdfCache,
|
|
4
|
+
countLinkAnnotations,
|
|
5
|
+
getImageResolutions,
|
|
6
|
+
getOpPass,
|
|
7
|
+
getOutlineCount,
|
|
8
|
+
getPageLabels,
|
|
9
|
+
getPageSize,
|
|
10
|
+
getTextPass,
|
|
11
|
+
isLoadable,
|
|
12
|
+
loadPdf,
|
|
13
|
+
pageHasMarks,
|
|
14
|
+
pendingGraceDestroyCount,
|
|
15
|
+
retainPdfCache,
|
|
16
|
+
unsplittableTextReasons
|
|
17
|
+
} from "./cli-sth219v1.js";
|
|
18
|
+
import"./cli-37x76zdn.js";
|
|
19
|
+
export {
|
|
20
|
+
unsplittableTextReasons,
|
|
21
|
+
retainPdfCache,
|
|
22
|
+
pendingGraceDestroyCount,
|
|
23
|
+
pageHasMarks,
|
|
24
|
+
loadPdf,
|
|
25
|
+
isLoadable,
|
|
26
|
+
getTextPass,
|
|
27
|
+
getPageSize,
|
|
28
|
+
getPageLabels,
|
|
29
|
+
getOutlineCount,
|
|
30
|
+
getOpPass,
|
|
31
|
+
getImageResolutions,
|
|
32
|
+
countLinkAnnotations,
|
|
33
|
+
clearPdfCache,
|
|
34
|
+
DOC_CACHE_MAX
|
|
35
|
+
};
|
|
@@ -2,21 +2,22 @@ import {
|
|
|
2
2
|
executeValidation,
|
|
3
3
|
publishTargetFor,
|
|
4
4
|
reportMissingTools
|
|
5
|
-
} from "./cli-
|
|
5
|
+
} from "./cli-0ey902sx.js";
|
|
6
6
|
import {
|
|
7
7
|
log
|
|
8
|
-
} from "./cli-
|
|
8
|
+
} from "./cli-h4tfmpg3.js";
|
|
9
9
|
import {
|
|
10
10
|
UsageError,
|
|
11
11
|
rejectExtraPositionals,
|
|
12
12
|
rejectUnknownFlags
|
|
13
|
-
} from "./cli-
|
|
13
|
+
} from "./cli-fmksazej.js";
|
|
14
14
|
import"./cli-ht1bcqdd.js";
|
|
15
|
-
import"./cli-
|
|
15
|
+
import"./cli-61vd4ews.js";
|
|
16
16
|
import {
|
|
17
17
|
BuildError,
|
|
18
18
|
EXIT_CODES
|
|
19
19
|
} from "./cli-46ycxe6r.js";
|
|
20
|
+
import"./cli-sth219v1.js";
|
|
20
21
|
import"./cli-eyyggect.js";
|
|
21
22
|
import"./cli-asap4t1z.js";
|
|
22
23
|
import"./cli-5qr3z0qa.js";
|