postext 1.19.3 → 1.20.0
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/__tests__/comics/art.test.d.ts +2 -0
- package/dist/__tests__/comics/art.test.d.ts.map +1 -0
- package/dist/__tests__/comics/art.test.js +96 -0
- package/dist/__tests__/comics/art.test.js.map +1 -0
- package/dist/__tests__/comics/build.test.d.ts +2 -0
- package/dist/__tests__/comics/build.test.d.ts.map +1 -0
- package/dist/__tests__/comics/build.test.js +215 -0
- package/dist/__tests__/comics/build.test.js.map +1 -0
- package/dist/__tests__/comics/canvas.test.d.ts +2 -0
- package/dist/__tests__/comics/canvas.test.d.ts.map +1 -0
- package/dist/__tests__/comics/canvas.test.js +161 -0
- package/dist/__tests__/comics/canvas.test.js.map +1 -0
- package/dist/__tests__/comics/config.test.d.ts +2 -0
- package/dist/__tests__/comics/config.test.d.ts.map +1 -0
- package/dist/__tests__/comics/config.test.js +97 -0
- package/dist/__tests__/comics/config.test.js.map +1 -0
- package/dist/__tests__/comics/geometry.test.d.ts +2 -0
- package/dist/__tests__/comics/geometry.test.d.ts.map +1 -0
- package/dist/__tests__/comics/geometry.test.js +118 -0
- package/dist/__tests__/comics/geometry.test.js.map +1 -0
- package/dist/__tests__/comics/html.test.d.ts +2 -0
- package/dist/__tests__/comics/html.test.d.ts.map +1 -0
- package/dist/__tests__/comics/html.test.js +253 -0
- package/dist/__tests__/comics/html.test.js.map +1 -0
- package/dist/__tests__/comics/lettering.test.d.ts +2 -0
- package/dist/__tests__/comics/lettering.test.d.ts.map +1 -0
- package/dist/__tests__/comics/lettering.test.js +241 -0
- package/dist/__tests__/comics/lettering.test.js.map +1 -0
- package/dist/__tests__/comics/script.test.d.ts +2 -0
- package/dist/__tests__/comics/script.test.d.ts.map +1 -0
- package/dist/__tests__/comics/script.test.js +147 -0
- package/dist/__tests__/comics/script.test.js.map +1 -0
- package/dist/__tests__/comics/split.test.d.ts +2 -0
- package/dist/__tests__/comics/split.test.d.ts.map +1 -0
- package/dist/__tests__/comics/split.test.js +120 -0
- package/dist/__tests__/comics/split.test.js.map +1 -0
- package/dist/__tests__/comics/spread.test.d.ts +2 -0
- package/dist/__tests__/comics/spread.test.d.ts.map +1 -0
- package/dist/__tests__/comics/spread.test.js +161 -0
- package/dist/__tests__/comics/spread.test.js.map +1 -0
- package/dist/__tests__/comics/strip.test.d.ts +2 -0
- package/dist/__tests__/comics/strip.test.d.ts.map +1 -0
- package/dist/__tests__/comics/strip.test.js +231 -0
- package/dist/__tests__/comics/strip.test.js.map +1 -0
- package/dist/__tests__/exports.test.js +89 -0
- package/dist/__tests__/exports.test.js.map +1 -1
- package/dist/__tests__/parse/containers.test.js +1 -1
- package/dist/__tests__/parse/containers.test.js.map +1 -1
- package/dist/canvas-backend/comic.d.ts +32 -0
- package/dist/canvas-backend/comic.d.ts.map +1 -0
- package/dist/canvas-backend/comic.js +216 -0
- package/dist/canvas-backend/comic.js.map +1 -0
- package/dist/canvas-backend/headerFooter.d.ts +4 -1
- package/dist/canvas-backend/headerFooter.d.ts.map +1 -1
- package/dist/canvas-backend/headerFooter.js +3 -1
- package/dist/canvas-backend/headerFooter.js.map +1 -1
- package/dist/canvas-backend/index.d.ts.map +1 -1
- package/dist/canvas-backend/index.js +7 -1
- package/dist/canvas-backend/index.js.map +1 -1
- package/dist/comics/art.d.ts +72 -0
- package/dist/comics/art.d.ts.map +1 -0
- package/dist/comics/art.js +154 -0
- package/dist/comics/art.js.map +1 -0
- package/dist/comics/fonts.d.ts +20 -0
- package/dist/comics/fonts.d.ts.map +1 -0
- package/dist/comics/fonts.js +42 -0
- package/dist/comics/fonts.js.map +1 -0
- package/dist/comics/geometry.d.ts +87 -0
- package/dist/comics/geometry.d.ts.map +1 -0
- package/dist/comics/geometry.js +272 -0
- package/dist/comics/geometry.js.map +1 -0
- package/dist/comics/index.d.ts +29 -0
- package/dist/comics/index.d.ts.map +1 -0
- package/dist/comics/index.js +18 -0
- package/dist/comics/index.js.map +1 -0
- package/dist/comics/layoutPage.d.ts +156 -0
- package/dist/comics/layoutPage.d.ts.map +1 -0
- package/dist/comics/layoutPage.js +697 -0
- package/dist/comics/layoutPage.js.map +1 -0
- package/dist/comics/lettering/__tests__/harness.test.d.ts +2 -0
- package/dist/comics/lettering/__tests__/harness.test.d.ts.map +1 -0
- package/dist/comics/lettering/__tests__/harness.test.js +273 -0
- package/dist/comics/lettering/__tests__/harness.test.js.map +1 -0
- package/dist/comics/lettering/__tests__/letterPanel.test.d.ts +2 -0
- package/dist/comics/lettering/__tests__/letterPanel.test.d.ts.map +1 -0
- package/dist/comics/lettering/__tests__/letterPanel.test.js +224 -0
- package/dist/comics/lettering/__tests__/letterPanel.test.js.map +1 -0
- package/dist/comics/lettering/__tests__/polish.test.d.ts +2 -0
- package/dist/comics/lettering/__tests__/polish.test.d.ts.map +1 -0
- package/dist/comics/lettering/__tests__/polish.test.js +357 -0
- package/dist/comics/lettering/__tests__/polish.test.js.map +1 -0
- package/dist/comics/lettering/__tests__/round2.test.d.ts +2 -0
- package/dist/comics/lettering/__tests__/round2.test.d.ts.map +1 -0
- package/dist/comics/lettering/__tests__/round2.test.js +289 -0
- package/dist/comics/lettering/__tests__/round2.test.js.map +1 -0
- package/dist/comics/lettering/__tests__/shapeText.test.d.ts +2 -0
- package/dist/comics/lettering/__tests__/shapeText.test.d.ts.map +1 -0
- package/dist/comics/lettering/__tests__/shapeText.test.js +98 -0
- package/dist/comics/lettering/__tests__/shapeText.test.js.map +1 -0
- package/dist/comics/lettering/__tests__/shapes.test.d.ts +2 -0
- package/dist/comics/lettering/__tests__/shapes.test.d.ts.map +1 -0
- package/dist/comics/lettering/__tests__/shapes.test.js +137 -0
- package/dist/comics/lettering/__tests__/shapes.test.js.map +1 -0
- package/dist/comics/lettering/__tests__/stubMeasure.d.ts +5 -0
- package/dist/comics/lettering/__tests__/stubMeasure.d.ts.map +1 -0
- package/dist/comics/lettering/__tests__/stubMeasure.js +47 -0
- package/dist/comics/lettering/__tests__/stubMeasure.js.map +1 -0
- package/dist/comics/lettering/__tests__/text.test.d.ts +2 -0
- package/dist/comics/lettering/__tests__/text.test.d.ts.map +1 -0
- package/dist/comics/lettering/__tests__/text.test.js +86 -0
- package/dist/comics/lettering/__tests__/text.test.js.map +1 -0
- package/dist/comics/lettering/geom.d.ts +78 -0
- package/dist/comics/lettering/geom.d.ts.map +1 -0
- package/dist/comics/lettering/geom.js +376 -0
- package/dist/comics/lettering/geom.js.map +1 -0
- package/dist/comics/lettering/index.d.ts +7 -0
- package/dist/comics/lettering/index.d.ts.map +1 -0
- package/dist/comics/lettering/index.js +8 -0
- package/dist/comics/lettering/index.js.map +1 -0
- package/dist/comics/lettering/letter.d.ts +7 -0
- package/dist/comics/lettering/letter.d.ts.map +1 -0
- package/dist/comics/lettering/letter.js +397 -0
- package/dist/comics/lettering/letter.js.map +1 -0
- package/dist/comics/lettering/placement.d.ts +117 -0
- package/dist/comics/lettering/placement.d.ts.map +1 -0
- package/dist/comics/lettering/placement.js +889 -0
- package/dist/comics/lettering/placement.js.map +1 -0
- package/dist/comics/lettering/presets.d.ts +15 -0
- package/dist/comics/lettering/presets.d.ts.map +1 -0
- package/dist/comics/lettering/presets.js +61 -0
- package/dist/comics/lettering/presets.js.map +1 -0
- package/dist/comics/lettering/shape-text.d.ts +72 -0
- package/dist/comics/lettering/shape-text.d.ts.map +1 -0
- package/dist/comics/lettering/shape-text.js +550 -0
- package/dist/comics/lettering/shape-text.js.map +1 -0
- package/dist/comics/lettering/shapes.d.ts +83 -0
- package/dist/comics/lettering/shapes.d.ts.map +1 -0
- package/dist/comics/lettering/shapes.js +512 -0
- package/dist/comics/lettering/shapes.js.map +1 -0
- package/dist/comics/lettering/text.d.ts +79 -0
- package/dist/comics/lettering/text.d.ts.map +1 -0
- package/dist/comics/lettering/text.js +488 -0
- package/dist/comics/lettering/text.js.map +1 -0
- package/dist/comics/lettering/types.d.ts +270 -0
- package/dist/comics/lettering/types.d.ts.map +1 -0
- package/dist/comics/lettering/types.js +9 -0
- package/dist/comics/lettering/types.js.map +1 -0
- package/dist/comics/page.d.ts +37 -0
- package/dist/comics/page.d.ts.map +1 -0
- package/dist/comics/page.js +285 -0
- package/dist/comics/page.js.map +1 -0
- package/dist/comics/paint.d.ts +65 -0
- package/dist/comics/paint.d.ts.map +1 -0
- package/dist/comics/paint.js +219 -0
- package/dist/comics/paint.js.map +1 -0
- package/dist/comics/script.d.ts +42 -0
- package/dist/comics/script.d.ts.map +1 -0
- package/dist/comics/script.js +302 -0
- package/dist/comics/script.js.map +1 -0
- package/dist/comics/split.d.ts +135 -0
- package/dist/comics/split.d.ts.map +1 -0
- package/dist/comics/split.js +514 -0
- package/dist/comics/split.js.map +1 -0
- package/dist/comics/spread.d.ts +36 -0
- package/dist/comics/spread.d.ts.map +1 -0
- package/dist/comics/spread.js +172 -0
- package/dist/comics/spread.js.map +1 -0
- package/dist/comics/strip.d.ts +77 -0
- package/dist/comics/strip.d.ts.map +1 -0
- package/dist/comics/strip.js +89 -0
- package/dist/comics/strip.js.map +1 -0
- package/dist/comics/transform.d.ts +37 -0
- package/dist/comics/transform.d.ts.map +1 -0
- package/dist/comics/transform.js +161 -0
- package/dist/comics/transform.js.map +1 -0
- package/dist/comics/types.d.ts +143 -0
- package/dist/comics/types.d.ts.map +1 -0
- package/dist/comics/types.js +13 -0
- package/dist/comics/types.js.map +1 -0
- package/dist/comics/warnings.d.ts +14 -0
- package/dist/comics/warnings.d.ts.map +1 -0
- package/dist/comics/warnings.js +94 -0
- package/dist/comics/warnings.js.map +1 -0
- package/dist/configWarnings.d.ts.map +1 -1
- package/dist/configWarnings.js +42 -0
- package/dist/configWarnings.js.map +1 -1
- package/dist/defaults/comics.d.ts +51 -0
- package/dist/defaults/comics.d.ts.map +1 -0
- package/dist/defaults/comics.js +385 -0
- package/dist/defaults/comics.js.map +1 -0
- package/dist/defaults/index.d.ts +1 -0
- package/dist/defaults/index.d.ts.map +1 -1
- package/dist/defaults/index.js +9 -0
- package/dist/defaults/index.js.map +1 -1
- package/dist/design/layout.d.ts +13 -1
- package/dist/design/layout.d.ts.map +1 -1
- package/dist/design/layout.js +17 -0
- package/dist/design/layout.js.map +1 -1
- package/dist/html-backend.d.ts +3 -0
- package/dist/html-backend.d.ts.map +1 -1
- package/dist/html-backend.js +44 -8
- package/dist/html-backend.js.map +1 -1
- package/dist/htmlComic.d.ts +69 -0
- package/dist/htmlComic.d.ts.map +1 -0
- package/dist/htmlComic.js +179 -0
- package/dist/htmlComic.js.map +1 -0
- package/dist/index.d.ts +8 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -1
- package/dist/parse/blockParser.d.ts.map +1 -1
- package/dist/parse/blockParser.js +25 -1
- package/dist/parse/blockParser.js.map +1 -1
- package/dist/parse/indexMarks.d.ts.map +1 -1
- package/dist/parse/indexMarks.js +3 -0
- package/dist/parse/indexMarks.js.map +1 -1
- package/dist/parse/types.d.ts +7 -1
- package/dist/parse/types.d.ts.map +1 -1
- package/dist/pipeline/build.d.ts.map +1 -1
- package/dist/pipeline/build.js +251 -9
- package/dist/pipeline/build.js.map +1 -1
- package/dist/pipeline/config.d.ts.map +1 -1
- package/dist/pipeline/config.js +4 -1
- package/dist/pipeline/config.js.map +1 -1
- package/dist/pipeline/contentWarnings.d.ts.map +1 -1
- package/dist/pipeline/contentWarnings.js +58 -0
- package/dist/pipeline/contentWarnings.js.map +1 -1
- package/dist/pipeline/floatSlots.js +1 -1
- package/dist/pipeline/floatSlots.js.map +1 -1
- package/dist/pipeline/headerFooter.d.ts +6 -2
- package/dist/pipeline/headerFooter.d.ts.map +1 -1
- package/dist/pipeline/headerFooter.js +11 -3
- package/dist/pipeline/headerFooter.js.map +1 -1
- package/dist/pipeline/pageRoles.d.ts +1 -0
- package/dist/pipeline/pageRoles.d.ts.map +1 -1
- package/dist/pipeline/pageRoles.js +3 -0
- package/dist/pipeline/pageRoles.js.map +1 -1
- package/dist/pipeline/placement.d.ts +3 -3
- package/dist/pipeline/placement.d.ts.map +1 -1
- package/dist/pipeline/placement.js +4 -4
- package/dist/pipeline/placement.js.map +1 -1
- package/dist/types.d.ts +338 -1
- package/dist/types.d.ts.map +1 -1
- package/dist/vdt.d.ts +271 -2
- package/dist/vdt.d.ts.map +1 -1
- package/dist/vdt.js.map +1 -1
- package/dist/worker/fonts.d.ts +2 -1
- package/dist/worker/fonts.d.ts.map +1 -1
- package/dist/worker/fonts.js +11 -1
- package/dist/worker/fonts.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What every painter of a comic page shares (#563, #565): the outline of a
|
|
3
|
+
* panel as an SVG path, its seeded rough border, the grouping of balloons
|
|
4
|
+
* into join groups, and how a balloon reads as text (its kind, its words,
|
|
5
|
+
* its speaker's name) for the outputs that set the dialogue apart from the
|
|
6
|
+
* art (tagged PDF, HTML, EPUB). Pure functions over the VDT.
|
|
7
|
+
*/
|
|
8
|
+
import type { ComicCastMember } from '../types';
|
|
9
|
+
import type { VDTComicBalloon, VDTComicPage, VDTComicPanel, VDTPoint } from '../vdt';
|
|
10
|
+
/** A small deterministic hash (FNV-1a) for seeding a rough border. */
|
|
11
|
+
export declare function comicHashString(s: string): number;
|
|
12
|
+
/** A hand-drawn version of a closed outline: points every few px pushed
|
|
13
|
+
* off the line by a seeded amount. */
|
|
14
|
+
export declare function roughOutline(points: readonly VDTPoint[], seed: number, amplitude: number, step: number): VDTPoint[];
|
|
15
|
+
/** The rough border of a panel (`border.style: 'rough'`): its polygon
|
|
16
|
+
* wobbled with a seed taken from the panel, the same on every output. */
|
|
17
|
+
export declare function comicRoughBorder(panel: Pick<VDTComicPanel, 'id' | 'index' | 'sourceStart' | 'polygon' | 'border'>): VDTPoint[];
|
|
18
|
+
/** A closed polyline as SVG path data. */
|
|
19
|
+
export declare function polygonPathData(points: readonly VDTPoint[]): string;
|
|
20
|
+
/** A panel's outline as SVG path data (page px): its polygon, or a
|
|
21
|
+
* rounded rectangle when it has a radius — the path the canvas clips to
|
|
22
|
+
* and strokes (`comicPanelPath`). */
|
|
23
|
+
export declare function comicPanelPathData(panel: Pick<VDTComicPanel, 'polygon' | 'bbox' | 'radius'>): string;
|
|
24
|
+
/** The path a panel's border is stroked along: its outline, or the
|
|
25
|
+
* wobbled one of a rough border. Empty when it has no border. */
|
|
26
|
+
export declare function comicBorderPathData(panel: VDTComicPanel): string;
|
|
27
|
+
/** What a balloon is to a reader: words a character says or thinks
|
|
28
|
+
* (`speech`), narration in a box (`caption`, also an editor's note), or a
|
|
29
|
+
* sound effect drawn into the art (`sfx`). Told from its style (`sfx`,
|
|
30
|
+
* `caption`, `note`) and whether it has a speaker. */
|
|
31
|
+
export type ComicBalloonKind = 'speech' | 'caption' | 'sfx';
|
|
32
|
+
export declare function comicBalloonKind(balloon: Pick<VDTComicBalloon, 'style' | 'speaker'> & {
|
|
33
|
+
kind?: VDTComicBalloon['kind'];
|
|
34
|
+
}): ComicBalloonKind;
|
|
35
|
+
/** The words of a balloon as plain text: its lines in order, joined by a
|
|
36
|
+
* space, or by nothing between two Chinese or Japanese characters (a
|
|
37
|
+
* line break there is no word space). */
|
|
38
|
+
export declare function comicBalloonText(balloon: Pick<VDTComicBalloon, 'text'>): string;
|
|
39
|
+
/** The name a speaker is shown under in the text outputs: its
|
|
40
|
+
* `comics.cast` entry's `name`, else its id. */
|
|
41
|
+
export declare function comicSpeakerName(speaker: string, cast: readonly ComicCastMember[] | undefined): string;
|
|
42
|
+
/** The balloons of a page as join groups, in reading order: each group's
|
|
43
|
+
* members in order, the first (the one with the `shape`, when the group
|
|
44
|
+
* has an outline) first. A group never spans two panels. */
|
|
45
|
+
export declare function comicBalloonGroups(balloons: readonly VDTComicBalloon[]): VDTComicBalloon[][];
|
|
46
|
+
/**
|
|
47
|
+
* Whether a panel of one page of a two-page spread (`comic.spread`) is the
|
|
48
|
+
* second half of a panel that crosses the spine: the part on the page read
|
|
49
|
+
* second (the right page left to right, the left page right to left). Its
|
|
50
|
+
* picture is painted, but it is read, listed and named once, with the half
|
|
51
|
+
* on the page read first.
|
|
52
|
+
*/
|
|
53
|
+
export declare function comicPanelContinues(comic: Pick<VDTComicPage, 'spread' | 'frame' | 'direction'>, panel: Pick<VDTComicPanel, 'bbox'>): boolean;
|
|
54
|
+
/** Whether `b` is the other page of the spread `a` belongs to. */
|
|
55
|
+
export declare function isComicSpreadPartner(a: Pick<VDTComicPage, 'spread' | 'sourceStart'>, b: Pick<VDTComicPage, 'spread' | 'sourceStart'> | undefined): boolean;
|
|
56
|
+
/**
|
|
57
|
+
* The two pages of a spread joined back into one comic, in the left page's
|
|
58
|
+
* coordinates (the right page set against it at the spine): each panel
|
|
59
|
+
* once, a panel crossing the spine whole again (its two halves' outlines
|
|
60
|
+
* joined; its picture's box is the same on both), balloons of both pages,
|
|
61
|
+
* in reading order. What an output that does not show pages reads (the
|
|
62
|
+
* reflowable EPUB).
|
|
63
|
+
*/
|
|
64
|
+
export declare function joinComicSpread(left: VDTComicPage, right: VDTComicPage): VDTComicPage;
|
|
65
|
+
//# sourceMappingURL=paint.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"paint.d.ts","sourceRoot":"","sources":["../../src/comics/paint.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC;AAChD,OAAO,KAAK,EAAE,eAAe,EAAE,YAAY,EAAE,aAAa,EAAE,QAAQ,EAAE,MAAM,QAAQ,CAAC;AAGrF,sEAAsE;AACtE,wBAAgB,eAAe,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAOjD;AAaD;uCACuC;AACvC,wBAAgB,YAAY,CAAC,MAAM,EAAE,SAAS,QAAQ,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,QAAQ,EAAE,CAkBnH;AAED;0EAC0E;AAC1E,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,IAAI,CAAC,aAAa,EAAE,IAAI,GAAG,OAAO,GAAG,aAAa,GAAG,SAAS,GAAG,QAAQ,CAAC,GAAG,QAAQ,EAAE,CAG9H;AAID,0CAA0C;AAC1C,wBAAgB,eAAe,CAAC,MAAM,EAAE,SAAS,QAAQ,EAAE,GAAG,MAAM,CAGnE;AAED;;sCAEsC;AACtC,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,IAAI,CAAC,aAAa,EAAE,SAAS,GAAG,MAAM,GAAG,QAAQ,CAAC,GAAG,MAAM,CAapG;AAED;kEACkE;AAClE,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,aAAa,GAAG,MAAM,CAGhE;AAED;;;uDAGuD;AACvD,MAAM,MAAM,gBAAgB,GAAG,QAAQ,GAAG,SAAS,GAAG,KAAK,CAAC;AAE5D,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,IAAI,CAAC,eAAe,EAAE,OAAO,GAAG,SAAS,CAAC,GAAG;IAAE,IAAI,CAAC,EAAE,eAAe,CAAC,MAAM,CAAC,CAAA;CAAE,GAAG,gBAAgB,CAO3I;AAID;;0CAE0C;AAC1C,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,IAAI,CAAC,eAAe,EAAE,MAAM,CAAC,GAAG,MAAM,CAW/E;AAED;iDACiD;AACjD,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,eAAe,EAAE,GAAG,SAAS,GAAG,MAAM,CAEtG;AAED;;6DAE6D;AAC7D,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,SAAS,eAAe,EAAE,GAAG,eAAe,EAAE,EAAE,CAc5F;AAED;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,IAAI,CAAC,YAAY,EAAE,QAAQ,GAAG,OAAO,GAAG,WAAW,CAAC,EAAE,KAAK,EAAE,IAAI,CAAC,aAAa,EAAE,MAAM,CAAC,GAAG,OAAO,CAQ5I;AAED,kEAAkE;AAClE,wBAAgB,oBAAoB,CAAC,CAAC,EAAE,IAAI,CAAC,YAAY,EAAE,QAAQ,GAAG,aAAa,CAAC,EAAE,CAAC,EAAE,IAAI,CAAC,YAAY,EAAE,QAAQ,GAAG,aAAa,CAAC,GAAG,SAAS,GAAG,OAAO,CAE1J;AAqBD;;;;;;;GAOG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,YAAY,EAAE,KAAK,EAAE,YAAY,GAAG,YAAY,CA4BrF"}
|
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What every painter of a comic page shares (#563, #565): the outline of a
|
|
3
|
+
* panel as an SVG path, its seeded rough border, the grouping of balloons
|
|
4
|
+
* into join groups, and how a balloon reads as text (its kind, its words,
|
|
5
|
+
* its speaker's name) for the outputs that set the dialogue apart from the
|
|
6
|
+
* art (tagged PDF, HTML, EPUB). Pure functions over the VDT.
|
|
7
|
+
*/
|
|
8
|
+
import { translateComicPage } from './transform';
|
|
9
|
+
/** A small deterministic hash (FNV-1a) for seeding a rough border. */
|
|
10
|
+
export function comicHashString(s) {
|
|
11
|
+
let h = 0x811c9dc5;
|
|
12
|
+
for (let i = 0; i < s.length; i++) {
|
|
13
|
+
h ^= s.charCodeAt(i);
|
|
14
|
+
h = Math.imul(h, 0x01000193);
|
|
15
|
+
}
|
|
16
|
+
return h >>> 0;
|
|
17
|
+
}
|
|
18
|
+
/** A seeded pseudo-random sequence in [-1, 1] (mulberry32). */
|
|
19
|
+
function noise(seed) {
|
|
20
|
+
let a = seed || 1;
|
|
21
|
+
return () => {
|
|
22
|
+
a = (a + 0x6d2b79f5) | 0;
|
|
23
|
+
let t = Math.imul(a ^ (a >>> 15), 1 | a);
|
|
24
|
+
t = (t + Math.imul(t ^ (t >>> 7), 61 | t)) ^ t;
|
|
25
|
+
return (((t ^ (t >>> 14)) >>> 0) / 4294967296) * 2 - 1;
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
/** A hand-drawn version of a closed outline: points every few px pushed
|
|
29
|
+
* off the line by a seeded amount. */
|
|
30
|
+
export function roughOutline(points, seed, amplitude, step) {
|
|
31
|
+
const rand = noise(seed);
|
|
32
|
+
const out = [];
|
|
33
|
+
for (let i = 0; i < points.length; i++) {
|
|
34
|
+
const a = points[i];
|
|
35
|
+
const b = points[(i + 1) % points.length];
|
|
36
|
+
const len = Math.hypot(b.x - a.x, b.y - a.y);
|
|
37
|
+
const n = Math.max(1, Math.round(len / step));
|
|
38
|
+
const nx = len > 0 ? -(b.y - a.y) / len : 0;
|
|
39
|
+
const ny = len > 0 ? (b.x - a.x) / len : 0;
|
|
40
|
+
for (let k = 0; k < n; k++) {
|
|
41
|
+
const t = k / n;
|
|
42
|
+
// Corners stay put; the wobble grows away from them.
|
|
43
|
+
const j = k === 0 ? 0 : rand() * amplitude;
|
|
44
|
+
out.push({ x: a.x + t * (b.x - a.x) + nx * j, y: a.y + t * (b.y - a.y) + ny * j });
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
return out;
|
|
48
|
+
}
|
|
49
|
+
/** The rough border of a panel (`border.style: 'rough'`): its polygon
|
|
50
|
+
* wobbled with a seed taken from the panel, the same on every output. */
|
|
51
|
+
export function comicRoughBorder(panel) {
|
|
52
|
+
const w = panel.border.width;
|
|
53
|
+
return roughOutline(panel.polygon, comicHashString(`${panel.id ?? ''}#${panel.index}#${panel.sourceStart}`), w * 0.6, Math.max(4, w * 6));
|
|
54
|
+
}
|
|
55
|
+
const n3 = (v) => String(Math.round(v * 1000) / 1000);
|
|
56
|
+
/** A closed polyline as SVG path data. */
|
|
57
|
+
export function polygonPathData(points) {
|
|
58
|
+
if (points.length === 0)
|
|
59
|
+
return '';
|
|
60
|
+
return `M${points.map((p) => `${n3(p.x)} ${n3(p.y)}`).join('L')}Z`;
|
|
61
|
+
}
|
|
62
|
+
/** A panel's outline as SVG path data (page px): its polygon, or a
|
|
63
|
+
* rounded rectangle when it has a radius — the path the canvas clips to
|
|
64
|
+
* and strokes (`comicPanelPath`). */
|
|
65
|
+
export function comicPanelPathData(panel) {
|
|
66
|
+
const r = panel.radius;
|
|
67
|
+
if (r > 0) {
|
|
68
|
+
const { x, y, width: w, height: h } = panel.bbox;
|
|
69
|
+
const a = `A${n3(r)} ${n3(r)} 0 0 1 `;
|
|
70
|
+
return (`M${n3(x + r)} ${n3(y)}H${n3(x + w - r)}${a}${n3(x + w)} ${n3(y + r)}` +
|
|
71
|
+
`V${n3(y + h - r)}${a}${n3(x + w - r)} ${n3(y + h)}` +
|
|
72
|
+
`H${n3(x + r)}${a}${n3(x)} ${n3(y + h - r)}` +
|
|
73
|
+
`V${n3(y + r)}${a}${n3(x + r)} ${n3(y)}Z`);
|
|
74
|
+
}
|
|
75
|
+
return polygonPathData(panel.polygon);
|
|
76
|
+
}
|
|
77
|
+
/** The path a panel's border is stroked along: its outline, or the
|
|
78
|
+
* wobbled one of a rough border. Empty when it has no border. */
|
|
79
|
+
export function comicBorderPathData(panel) {
|
|
80
|
+
if (panel.border.style === 'none' || panel.border.width <= 0)
|
|
81
|
+
return '';
|
|
82
|
+
return panel.border.style === 'rough' ? polygonPathData(comicRoughBorder(panel)) : comicPanelPathData(panel);
|
|
83
|
+
}
|
|
84
|
+
export function comicBalloonKind(balloon) {
|
|
85
|
+
if (balloon.kind === 'sfx')
|
|
86
|
+
return 'sfx';
|
|
87
|
+
if (balloon.kind === 'caption' || balloon.kind === 'note')
|
|
88
|
+
return 'caption';
|
|
89
|
+
if (balloon.kind === 'balloon')
|
|
90
|
+
return balloon.speaker ? 'speech' : 'caption';
|
|
91
|
+
if (balloon.style === 'sfx')
|
|
92
|
+
return 'sfx';
|
|
93
|
+
if (balloon.style === 'caption' || balloon.style === 'note')
|
|
94
|
+
return 'caption';
|
|
95
|
+
return balloon.speaker ? 'speech' : 'caption';
|
|
96
|
+
}
|
|
97
|
+
const CJK_EDGE = /[⺀-鿿 -〿豈--]/;
|
|
98
|
+
/** The words of a balloon as plain text: its lines in order, joined by a
|
|
99
|
+
* space, or by nothing between two Chinese or Japanese characters (a
|
|
100
|
+
* line break there is no word space). */
|
|
101
|
+
export function comicBalloonText(balloon) {
|
|
102
|
+
let out = '';
|
|
103
|
+
for (const block of balloon.text) {
|
|
104
|
+
for (const line of block.lines) {
|
|
105
|
+
const t = line.text.trim();
|
|
106
|
+
if (!t)
|
|
107
|
+
continue;
|
|
108
|
+
if (out && !(CJK_EDGE.test(out[out.length - 1]) && CJK_EDGE.test(t[0])))
|
|
109
|
+
out += ' ';
|
|
110
|
+
out += t;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
return out;
|
|
114
|
+
}
|
|
115
|
+
/** The name a speaker is shown under in the text outputs: its
|
|
116
|
+
* `comics.cast` entry's `name`, else its id. */
|
|
117
|
+
export function comicSpeakerName(speaker, cast) {
|
|
118
|
+
return cast?.find((c) => c.id === speaker)?.name?.trim() || speaker;
|
|
119
|
+
}
|
|
120
|
+
/** The balloons of a page as join groups, in reading order: each group's
|
|
121
|
+
* members in order, the first (the one with the `shape`, when the group
|
|
122
|
+
* has an outline) first. A group never spans two panels. */
|
|
123
|
+
export function comicBalloonGroups(balloons) {
|
|
124
|
+
const groups = [];
|
|
125
|
+
const byKey = new Map();
|
|
126
|
+
for (const b of balloons) {
|
|
127
|
+
const key = `${b.panelIndex}#${b.group}`;
|
|
128
|
+
let g = byKey.get(key);
|
|
129
|
+
if (!g) {
|
|
130
|
+
g = [];
|
|
131
|
+
byKey.set(key, g);
|
|
132
|
+
groups.push(g);
|
|
133
|
+
}
|
|
134
|
+
g.push(b);
|
|
135
|
+
}
|
|
136
|
+
return groups;
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* Whether a panel of one page of a two-page spread (`comic.spread`) is the
|
|
140
|
+
* second half of a panel that crosses the spine: the part on the page read
|
|
141
|
+
* second (the right page left to right, the left page right to left). Its
|
|
142
|
+
* picture is painted, but it is read, listed and named once, with the half
|
|
143
|
+
* on the page read first.
|
|
144
|
+
*/
|
|
145
|
+
export function comicPanelContinues(comic, panel) {
|
|
146
|
+
if (!comic.spread)
|
|
147
|
+
return false;
|
|
148
|
+
const f = comic.frame;
|
|
149
|
+
const crosses = comic.spread === 'left'
|
|
150
|
+
? panel.bbox.x + panel.bbox.width > f.x + f.width + 0.25
|
|
151
|
+
: panel.bbox.x < f.x - 0.25;
|
|
152
|
+
if (!crosses)
|
|
153
|
+
return false;
|
|
154
|
+
return comic.spread !== (comic.direction === 'rtl' ? 'right' : 'left');
|
|
155
|
+
}
|
|
156
|
+
/** Whether `b` is the other page of the spread `a` belongs to. */
|
|
157
|
+
export function isComicSpreadPartner(a, b) {
|
|
158
|
+
return !!a.spread && !!b?.spread && a.spread !== b.spread && a.sourceStart === b.sourceStart;
|
|
159
|
+
}
|
|
160
|
+
/** The convex hull of points (monotone chain), clockwise on the sheet. */
|
|
161
|
+
function convexHull(points) {
|
|
162
|
+
const pts = [...points].sort((p, q) => p.x - q.x || p.y - q.y);
|
|
163
|
+
if (pts.length < 3)
|
|
164
|
+
return pts;
|
|
165
|
+
const cross = (o, a, b) => (a.x - o.x) * (b.y - o.y) - (a.y - o.y) * (b.x - o.x);
|
|
166
|
+
const lower = [];
|
|
167
|
+
for (const p of pts) {
|
|
168
|
+
while (lower.length >= 2 && cross(lower[lower.length - 2], lower[lower.length - 1], p) <= 1e-9)
|
|
169
|
+
lower.pop();
|
|
170
|
+
lower.push(p);
|
|
171
|
+
}
|
|
172
|
+
const upper = [];
|
|
173
|
+
for (let i = pts.length - 1; i >= 0; i--) {
|
|
174
|
+
const p = pts[i];
|
|
175
|
+
while (upper.length >= 2 && cross(upper[upper.length - 2], upper[upper.length - 1], p) <= 1e-9)
|
|
176
|
+
upper.pop();
|
|
177
|
+
upper.push(p);
|
|
178
|
+
}
|
|
179
|
+
return [...lower.slice(0, -1), ...upper.slice(0, -1)];
|
|
180
|
+
}
|
|
181
|
+
/**
|
|
182
|
+
* The two pages of a spread joined back into one comic, in the left page's
|
|
183
|
+
* coordinates (the right page set against it at the spine): each panel
|
|
184
|
+
* once, a panel crossing the spine whole again (its two halves' outlines
|
|
185
|
+
* joined; its picture's box is the same on both), balloons of both pages,
|
|
186
|
+
* in reading order. What an output that does not show pages reads (the
|
|
187
|
+
* reflowable EPUB).
|
|
188
|
+
*/
|
|
189
|
+
export function joinComicSpread(left, right) {
|
|
190
|
+
const shift = left.frame.x + left.frame.width - right.frame.x;
|
|
191
|
+
const moved = translateComicPage(right, shift, 0);
|
|
192
|
+
const byIndex = new Map();
|
|
193
|
+
for (const p of left.panels)
|
|
194
|
+
byIndex.set(p.index, p);
|
|
195
|
+
for (const p of moved.panels) {
|
|
196
|
+
const l = byIndex.get(p.index);
|
|
197
|
+
if (!l) {
|
|
198
|
+
byIndex.set(p.index, p);
|
|
199
|
+
continue;
|
|
200
|
+
}
|
|
201
|
+
const polygon = convexHull([...l.polygon, ...p.polygon]);
|
|
202
|
+
const xs = polygon.map((q) => q.x);
|
|
203
|
+
const ys = polygon.map((q) => q.y);
|
|
204
|
+
const bbox = { x: Math.min(...xs), y: Math.min(...ys), width: Math.max(...xs) - Math.min(...xs), height: Math.max(...ys) - Math.min(...ys) };
|
|
205
|
+
byIndex.set(p.index, { ...l, polygon, bbox, radius: 0 });
|
|
206
|
+
}
|
|
207
|
+
const panels = [...byIndex.values()].sort((a, b) => a.index - b.index);
|
|
208
|
+
const balloons = [...left.balloons, ...moved.balloons].sort((a, b) => a.panelIndex - b.panelIndex || a.order - b.order);
|
|
209
|
+
const { spread: _spread, ...rest } = left;
|
|
210
|
+
void _spread;
|
|
211
|
+
return {
|
|
212
|
+
...rest,
|
|
213
|
+
frame: { x: left.frame.x, y: Math.min(left.frame.y, moved.frame.y), width: moved.frame.x + moved.frame.width - left.frame.x, height: Math.max(left.frame.height, moved.frame.height) },
|
|
214
|
+
panels,
|
|
215
|
+
splitters: [...left.splitters, ...moved.splitters],
|
|
216
|
+
balloons,
|
|
217
|
+
};
|
|
218
|
+
}
|
|
219
|
+
//# sourceMappingURL=paint.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"paint.js","sourceRoot":"","sources":["../../src/comics/paint.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The script of a comic panel (#558): one balloon per line,
|
|
3
|
+
* `key{attrs}: text`.
|
|
4
|
+
*
|
|
5
|
+
* - The colon may be fullwidth (`:`), as a Chinese or Japanese input
|
|
6
|
+
* method types it.
|
|
7
|
+
* - A line indented by two spaces or a tab continues the balloon above (a
|
|
8
|
+
* space joins the lines, none between Chinese or Japanese characters); a
|
|
9
|
+
* line ending with a backslash breaks the balloon's line there.
|
|
10
|
+
* - Blank lines and `<!-- comments -->` are ignored.
|
|
11
|
+
* - `key` is a speaker id (letters, digits, `_ . -`), the same in every
|
|
12
|
+
* translation; `caption`, `sfx` and `note` are reserved.
|
|
13
|
+
* - The text is inline Markdown, read by the same passes as a resource
|
|
14
|
+
* caption: emphasis, `:ruby`, `:tcy`, `:ltr` / `:rtl`, forced breaks.
|
|
15
|
+
*/
|
|
16
|
+
import { type ComicScriptItem, type ComicSourceRange } from './types';
|
|
17
|
+
/** One source line of a script, as the page parser hands it over: its text
|
|
18
|
+
* (comments blanked out) and where it starts. */
|
|
19
|
+
export interface ComicScriptLine {
|
|
20
|
+
text: string;
|
|
21
|
+
start: number;
|
|
22
|
+
}
|
|
23
|
+
/** `"62% 40%"` (or `62 40`, `0.62 0.4` read as fractions when both are at
|
|
24
|
+
* most 1 and written without `%`) as fractions; undefined when it is not
|
|
25
|
+
* two numbers. */
|
|
26
|
+
export declare function parseComicPoint(value: string | undefined): {
|
|
27
|
+
x: number;
|
|
28
|
+
y: number;
|
|
29
|
+
} | undefined;
|
|
30
|
+
/** The text of a balloon read as inline Markdown, spaces collapsed, with
|
|
31
|
+
* its absolute source map. `content` starts at `contentStart`. */
|
|
32
|
+
export declare function readBalloonText(content: string, contentStart: number): Pick<ComicScriptItem, 'text' | 'spans' | 'sourceMap'>;
|
|
33
|
+
/** Value ranges of an attribute blob starting at `blobStart`. */
|
|
34
|
+
export declare function attrValueSources(blob: string, blobStart: number): Record<string, ComicSourceRange>;
|
|
35
|
+
/**
|
|
36
|
+
* The balloons of a panel's script lines, in order. `markdown` is the text
|
|
37
|
+
* the offsets point into (the lines' text may have comments blanked out,
|
|
38
|
+
* at the same length). A line that is not a script line and continues
|
|
39
|
+
* nothing is lettered as a caption and marked `stray`.
|
|
40
|
+
*/
|
|
41
|
+
export declare function parseComicScript(markdown: string, lines: readonly ComicScriptLine[]): ComicScriptItem[];
|
|
42
|
+
//# sourceMappingURL=script.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"script.d.ts","sourceRoot":"","sources":["../../src/comics/script.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAOH,OAAO,EAAuB,KAAK,eAAe,EAAwB,KAAK,gBAAgB,EAAsB,MAAM,SAAS,CAAC;AAKrI;kDACkD;AAClD,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;CACf;AAcD;;mBAEmB;AACnB,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG;IAAE,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,SAAS,CAS/F;AAqBD;mEACmE;AACnE,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC,eAAe,EAAE,MAAM,GAAG,OAAO,GAAG,WAAW,CAAC,CA+C5H;AAED,iEAAiE;AACjE,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAUlG;AAyFD;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,SAAS,eAAe,EAAE,GAAG,eAAe,EAAE,CAqCvG"}
|
|
@@ -0,0 +1,302 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The script of a comic panel (#558): one balloon per line,
|
|
3
|
+
* `key{attrs}: text`.
|
|
4
|
+
*
|
|
5
|
+
* - The colon may be fullwidth (`:`), as a Chinese or Japanese input
|
|
6
|
+
* method types it.
|
|
7
|
+
* - A line indented by two spaces or a tab continues the balloon above (a
|
|
8
|
+
* space joins the lines, none between Chinese or Japanese characters); a
|
|
9
|
+
* line ending with a backslash breaks the balloon's line there.
|
|
10
|
+
* - Blank lines and `<!-- comments -->` are ignored.
|
|
11
|
+
* - `key` is a speaker id (letters, digits, `_ . -`), the same in every
|
|
12
|
+
* translation; `caption`, `sfx` and `note` are reserved.
|
|
13
|
+
* - The text is inline Markdown, read by the same passes as a resource
|
|
14
|
+
* caption: emphasis, `:ruby`, `:tcy`, `:ltr` / `:rtl`, forced breaks.
|
|
15
|
+
*/
|
|
16
|
+
import { scanDirectiveAttrs } from '../parse/attrs';
|
|
17
|
+
import { mapInlineSnippet } from '../parse/inlineSnippet';
|
|
18
|
+
import { BREAK_PLACEHOLDER } from '../parse/inlineFormatting';
|
|
19
|
+
import { COMIC_RESERVED_KEYS } from './types';
|
|
20
|
+
/** `key`, an optional `{attrs}`, then the colon. */
|
|
21
|
+
const SCRIPT_LINE_RE = /^([\p{L}\p{N}_.-]+)[ \t]*(?:\{([^}\n]*)\})?[ \t]*[::]/u;
|
|
22
|
+
/** Attribute keys a script line reads; any other bare flag names a style. */
|
|
23
|
+
const SCRIPT_KEYS = new Set(['at', 'to', 'tail', 'join', 'break', 'rotate', 'size', 'color', 'font', 'style', 'id', 'vertical', 'horizontal', 'mode']);
|
|
24
|
+
const POSITION_KEYWORDS = new Set(['top-start', 'top-end', 'bottom-start', 'bottom-end', 'top', 'bottom']);
|
|
25
|
+
const TAIL_VALUES = new Set(['none', 'auto', 'top', 'bottom', 'start', 'end']);
|
|
26
|
+
/** Whether a line continues the balloon above: two spaces or a tab of
|
|
27
|
+
* indentation. */
|
|
28
|
+
function isContinuation(text) {
|
|
29
|
+
return /^(?: {2,}|\t)/.test(text) && text.trim() !== '';
|
|
30
|
+
}
|
|
31
|
+
/** `"62% 40%"` (or `62 40`, `0.62 0.4` read as fractions when both are at
|
|
32
|
+
* most 1 and written without `%`) as fractions; undefined when it is not
|
|
33
|
+
* two numbers. */
|
|
34
|
+
export function parseComicPoint(value) {
|
|
35
|
+
if (value === undefined)
|
|
36
|
+
return undefined;
|
|
37
|
+
const m = /^\s*(-?[0-9.]+)\s*(%?)\s*[ ,]\s*(-?[0-9.]+)\s*(%?)\s*$/.exec(value);
|
|
38
|
+
if (!m)
|
|
39
|
+
return undefined;
|
|
40
|
+
const a = Number(m[1]);
|
|
41
|
+
const b = Number(m[3]);
|
|
42
|
+
if (!Number.isFinite(a) || !Number.isFinite(b))
|
|
43
|
+
return undefined;
|
|
44
|
+
const fractions = !m[2] && !m[4] && Math.abs(a) <= 1 && Math.abs(b) <= 1;
|
|
45
|
+
return fractions ? { x: a, y: b } : { x: a / 100, y: b / 100 };
|
|
46
|
+
}
|
|
47
|
+
function flagValue(attrs, key) {
|
|
48
|
+
const v = attrs[key];
|
|
49
|
+
if (v === undefined)
|
|
50
|
+
return undefined;
|
|
51
|
+
const t = v.trim().toLowerCase();
|
|
52
|
+
return !(t === 'false' || t === 'no' || t === '0');
|
|
53
|
+
}
|
|
54
|
+
function numberValue(v) {
|
|
55
|
+
if (v === undefined || v.trim() === '')
|
|
56
|
+
return undefined;
|
|
57
|
+
const n = Number(v.trim().replace(/deg$|°$|x$|×$/i, ''));
|
|
58
|
+
return Number.isFinite(n) ? n : undefined;
|
|
59
|
+
}
|
|
60
|
+
/** A reserved key names its role (`caption`, `sfx`, `note`); any other key
|
|
61
|
+
* is a speaker. */
|
|
62
|
+
function roleOf(key) {
|
|
63
|
+
return COMIC_RESERVED_KEYS.has(key) ? key : 'speech';
|
|
64
|
+
}
|
|
65
|
+
/** The text of a balloon read as inline Markdown, spaces collapsed, with
|
|
66
|
+
* its absolute source map. `content` starts at `contentStart`. */
|
|
67
|
+
export function readBalloonText(content, contentStart) {
|
|
68
|
+
const mapped = mapInlineSnippet(content);
|
|
69
|
+
const text = mapped.text;
|
|
70
|
+
// Which characters stay, and the spaces that stand for a run of
|
|
71
|
+
// whitespace (a line end and the next line's indentation).
|
|
72
|
+
const keep = new Array(text.length).fill(true);
|
|
73
|
+
const asSpace = new Array(text.length).fill(false);
|
|
74
|
+
const isWs = (c) => c === ' ' || c === '\t' || c === '\n' || c === '\r';
|
|
75
|
+
for (let i = 0; i < text.length;) {
|
|
76
|
+
if (!isWs(text[i])) {
|
|
77
|
+
i++;
|
|
78
|
+
continue;
|
|
79
|
+
}
|
|
80
|
+
let j = i;
|
|
81
|
+
while (j < text.length && isWs(text[j]))
|
|
82
|
+
j++;
|
|
83
|
+
const atEdge = i === 0 || j === text.length || text[i - 1] === BREAK_PLACEHOLDER || text[j] === BREAK_PLACEHOLDER;
|
|
84
|
+
for (let k = i; k < j; k++)
|
|
85
|
+
keep[k] = false;
|
|
86
|
+
if (!atEdge) {
|
|
87
|
+
keep[i] = true;
|
|
88
|
+
asSpace[i] = true;
|
|
89
|
+
}
|
|
90
|
+
i = j;
|
|
91
|
+
}
|
|
92
|
+
const spans = [];
|
|
93
|
+
let at = 0;
|
|
94
|
+
for (const span of mapped.spans) {
|
|
95
|
+
const end = at + span.text.length;
|
|
96
|
+
let out = '';
|
|
97
|
+
const newIndex = [];
|
|
98
|
+
for (let k = at; k < end; k++) {
|
|
99
|
+
newIndex.push(out.length);
|
|
100
|
+
if (!keep[k])
|
|
101
|
+
continue;
|
|
102
|
+
out += asSpace[k] ? ' ' : text[k];
|
|
103
|
+
}
|
|
104
|
+
newIndex.push(out.length);
|
|
105
|
+
if (out.length > 0) {
|
|
106
|
+
const next = { ...span, text: out };
|
|
107
|
+
if (span.links)
|
|
108
|
+
next.links = span.links.map((l) => ({ ...l, start: newIndex[l.start], end: newIndex[l.end] })).filter((l) => l.end > l.start);
|
|
109
|
+
spans.push(next);
|
|
110
|
+
}
|
|
111
|
+
at = end;
|
|
112
|
+
}
|
|
113
|
+
const sourceMap = [];
|
|
114
|
+
let plain = '';
|
|
115
|
+
for (let k = 0; k < text.length; k++) {
|
|
116
|
+
if (!keep[k])
|
|
117
|
+
continue;
|
|
118
|
+
plain += asSpace[k] ? ' ' : text[k];
|
|
119
|
+
sourceMap.push(contentStart + (mapped.sourceMap[k] ?? 0));
|
|
120
|
+
}
|
|
121
|
+
return { text: plain, spans, sourceMap };
|
|
122
|
+
}
|
|
123
|
+
/** Value ranges of an attribute blob starting at `blobStart`. */
|
|
124
|
+
export function attrValueSources(blob, blobStart) {
|
|
125
|
+
const out = {};
|
|
126
|
+
for (const t of scanDirectiveAttrs(blob)) {
|
|
127
|
+
if (t.valueStart === undefined || t.valueEnd === undefined) {
|
|
128
|
+
out[t.key] = { start: blobStart + t.end, end: blobStart + t.end };
|
|
129
|
+
continue;
|
|
130
|
+
}
|
|
131
|
+
out[t.key] = { start: blobStart + t.valueStart, end: blobStart + t.valueEnd };
|
|
132
|
+
}
|
|
133
|
+
return out;
|
|
134
|
+
}
|
|
135
|
+
/** The item of a script line's head: key, attributes, where its text
|
|
136
|
+
* starts. Text and range are filled once its continuation lines are
|
|
137
|
+
* known. */
|
|
138
|
+
function scriptHead(line) {
|
|
139
|
+
const lead = line.text.length - line.text.trimStart().length;
|
|
140
|
+
const body = line.text.slice(lead);
|
|
141
|
+
const m = SCRIPT_LINE_RE.exec(body);
|
|
142
|
+
if (!m)
|
|
143
|
+
return undefined;
|
|
144
|
+
const key = m[1];
|
|
145
|
+
const blob = m[2];
|
|
146
|
+
const keyStart = line.start + lead;
|
|
147
|
+
const attrs = {};
|
|
148
|
+
const styleFlags = [];
|
|
149
|
+
let attrSources = {};
|
|
150
|
+
let attrsStart;
|
|
151
|
+
let attrsEnd;
|
|
152
|
+
if (blob !== undefined) {
|
|
153
|
+
const braceAt = body.indexOf('{', key.length);
|
|
154
|
+
attrsStart = keyStart + braceAt + 1;
|
|
155
|
+
attrsEnd = attrsStart + blob.length;
|
|
156
|
+
for (const t of scanDirectiveAttrs(blob)) {
|
|
157
|
+
if (t.flag && !SCRIPT_KEYS.has(t.key)) {
|
|
158
|
+
styleFlags.push(t.key);
|
|
159
|
+
continue;
|
|
160
|
+
}
|
|
161
|
+
attrs[t.key] = t.value;
|
|
162
|
+
}
|
|
163
|
+
attrSources = attrValueSources(blob, attrsStart);
|
|
164
|
+
}
|
|
165
|
+
// The text starts after the colon and the spaces after it.
|
|
166
|
+
let textStart = keyStart + m[0].length;
|
|
167
|
+
while (textStart < line.start + line.text.length && (line.text[textStart - line.start] === ' ' || line.text[textStart - line.start] === '\t' || line.text[textStart - line.start] === ' '))
|
|
168
|
+
textStart++;
|
|
169
|
+
const role = roleOf(key);
|
|
170
|
+
const style = attrs.style?.trim() || styleFlags[0];
|
|
171
|
+
const item = {
|
|
172
|
+
key,
|
|
173
|
+
role,
|
|
174
|
+
...(role === 'speech' ? { speaker: key } : {}),
|
|
175
|
+
...(style ? { style } : {}),
|
|
176
|
+
styleFlags,
|
|
177
|
+
attrs,
|
|
178
|
+
attrSources,
|
|
179
|
+
sourceStart: keyStart,
|
|
180
|
+
keyStart,
|
|
181
|
+
keyEnd: keyStart + key.length,
|
|
182
|
+
...(attrsStart !== undefined ? { attrsStart, attrsEnd } : {}),
|
|
183
|
+
textStart,
|
|
184
|
+
};
|
|
185
|
+
const atRaw = attrs.at?.trim();
|
|
186
|
+
if (atRaw) {
|
|
187
|
+
if (POSITION_KEYWORDS.has(atRaw))
|
|
188
|
+
item.atKeyword = atRaw;
|
|
189
|
+
else {
|
|
190
|
+
const p = parseComicPoint(atRaw);
|
|
191
|
+
if (p)
|
|
192
|
+
item.at = p;
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
const to = parseComicPoint(attrs.to);
|
|
196
|
+
if (to)
|
|
197
|
+
item.to = to;
|
|
198
|
+
const tail = attrs.tail?.trim();
|
|
199
|
+
if (tail && TAIL_VALUES.has(tail))
|
|
200
|
+
item.tail = tail;
|
|
201
|
+
const join = flagValue(attrs, 'join');
|
|
202
|
+
if (join !== undefined)
|
|
203
|
+
item.join = join;
|
|
204
|
+
const brk = flagValue(attrs, 'break');
|
|
205
|
+
if (brk !== undefined)
|
|
206
|
+
item.break = brk;
|
|
207
|
+
const rotate = numberValue(attrs.rotate);
|
|
208
|
+
if (rotate !== undefined)
|
|
209
|
+
item.rotate = rotate;
|
|
210
|
+
const size = numberValue(attrs.size);
|
|
211
|
+
if (size !== undefined && size > 0)
|
|
212
|
+
item.size = size;
|
|
213
|
+
if (attrs.color?.trim())
|
|
214
|
+
item.color = attrs.color.trim();
|
|
215
|
+
if (attrs.font?.trim())
|
|
216
|
+
item.font = attrs.font.trim();
|
|
217
|
+
const mode = writingModeOf(attrs);
|
|
218
|
+
if (mode)
|
|
219
|
+
item.writingMode = mode;
|
|
220
|
+
return item;
|
|
221
|
+
}
|
|
222
|
+
/** A line's own writing mode: `mode=vertical|horizontal`, or the bare
|
|
223
|
+
* flag `vertical` / `horizontal` (an untranslated ドン kept in columns in a
|
|
224
|
+
* horizontal edition, a Latin subtitle kept in a line in a vertical
|
|
225
|
+
* one). */
|
|
226
|
+
function writingModeOf(attrs) {
|
|
227
|
+
const mode = attrs.mode?.trim().toLowerCase();
|
|
228
|
+
if (mode === 'vertical' || mode === 'horizontal')
|
|
229
|
+
return mode;
|
|
230
|
+
if (flagValue(attrs, 'vertical'))
|
|
231
|
+
return 'vertical';
|
|
232
|
+
if (flagValue(attrs, 'horizontal'))
|
|
233
|
+
return 'horizontal';
|
|
234
|
+
return undefined;
|
|
235
|
+
}
|
|
236
|
+
/**
|
|
237
|
+
* The balloons of a panel's script lines, in order. `markdown` is the text
|
|
238
|
+
* the offsets point into (the lines' text may have comments blanked out,
|
|
239
|
+
* at the same length). A line that is not a script line and continues
|
|
240
|
+
* nothing is lettered as a caption and marked `stray`.
|
|
241
|
+
*/
|
|
242
|
+
export function parseComicScript(markdown, lines) {
|
|
243
|
+
const out = [];
|
|
244
|
+
let open;
|
|
245
|
+
const close = () => {
|
|
246
|
+
if (!open)
|
|
247
|
+
return;
|
|
248
|
+
const { head, end } = open;
|
|
249
|
+
const textEnd = Math.max(head.textStart, end);
|
|
250
|
+
// Comments are blanked in the lines, not in `markdown`: read the text
|
|
251
|
+
// from the lines' own copy.
|
|
252
|
+
const content = blankedSlice(markdown, lines, head.textStart, textEnd);
|
|
253
|
+
out.push({ ...head, ...readBalloonText(content, head.textStart), textEnd, sourceEnd: textEnd > head.textStart ? textEnd : head.keyEnd });
|
|
254
|
+
open = undefined;
|
|
255
|
+
};
|
|
256
|
+
for (const line of lines) {
|
|
257
|
+
if (line.text.trim() === '')
|
|
258
|
+
continue;
|
|
259
|
+
const lineEnd = line.start + line.text.trimEnd().length;
|
|
260
|
+
if (open && isContinuation(line.text)) {
|
|
261
|
+
open.end = lineEnd;
|
|
262
|
+
continue;
|
|
263
|
+
}
|
|
264
|
+
const head = scriptHead(line);
|
|
265
|
+
close();
|
|
266
|
+
if (head) {
|
|
267
|
+
open = { head, end: lineEnd };
|
|
268
|
+
continue;
|
|
269
|
+
}
|
|
270
|
+
// Not a script line: a caption, flagged.
|
|
271
|
+
const lead = line.text.length - line.text.trimStart().length;
|
|
272
|
+
const start = line.start + lead;
|
|
273
|
+
open = {
|
|
274
|
+
head: { key: 'caption', role: 'caption', styleFlags: [], attrs: {}, attrSources: {}, sourceStart: start, keyStart: start, keyEnd: start, textStart: start, stray: true },
|
|
275
|
+
end: lineEnd,
|
|
276
|
+
};
|
|
277
|
+
}
|
|
278
|
+
close();
|
|
279
|
+
return out;
|
|
280
|
+
}
|
|
281
|
+
/** `markdown.slice(start, end)` with the lines' own text (comments
|
|
282
|
+
* blanked) where they cover it. */
|
|
283
|
+
function blankedSlice(markdown, lines, start, end) {
|
|
284
|
+
let out = '';
|
|
285
|
+
let at = start;
|
|
286
|
+
for (const line of lines) {
|
|
287
|
+
const ls = line.start;
|
|
288
|
+
const le = line.start + line.text.length;
|
|
289
|
+
if (le <= at || ls >= end)
|
|
290
|
+
continue;
|
|
291
|
+
if (ls > at)
|
|
292
|
+
out += markdown.slice(at, ls);
|
|
293
|
+
const from = Math.max(at, ls);
|
|
294
|
+
const to = Math.min(end, le);
|
|
295
|
+
out += line.text.slice(from - ls, to - ls);
|
|
296
|
+
at = to;
|
|
297
|
+
}
|
|
298
|
+
if (at < end)
|
|
299
|
+
out += markdown.slice(at, end);
|
|
300
|
+
return out;
|
|
301
|
+
}
|
|
302
|
+
//# sourceMappingURL=script.js.map
|