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.
Files changed (251) hide show
  1. package/dist/__tests__/comics/art.test.d.ts +2 -0
  2. package/dist/__tests__/comics/art.test.d.ts.map +1 -0
  3. package/dist/__tests__/comics/art.test.js +96 -0
  4. package/dist/__tests__/comics/art.test.js.map +1 -0
  5. package/dist/__tests__/comics/build.test.d.ts +2 -0
  6. package/dist/__tests__/comics/build.test.d.ts.map +1 -0
  7. package/dist/__tests__/comics/build.test.js +215 -0
  8. package/dist/__tests__/comics/build.test.js.map +1 -0
  9. package/dist/__tests__/comics/canvas.test.d.ts +2 -0
  10. package/dist/__tests__/comics/canvas.test.d.ts.map +1 -0
  11. package/dist/__tests__/comics/canvas.test.js +161 -0
  12. package/dist/__tests__/comics/canvas.test.js.map +1 -0
  13. package/dist/__tests__/comics/config.test.d.ts +2 -0
  14. package/dist/__tests__/comics/config.test.d.ts.map +1 -0
  15. package/dist/__tests__/comics/config.test.js +97 -0
  16. package/dist/__tests__/comics/config.test.js.map +1 -0
  17. package/dist/__tests__/comics/geometry.test.d.ts +2 -0
  18. package/dist/__tests__/comics/geometry.test.d.ts.map +1 -0
  19. package/dist/__tests__/comics/geometry.test.js +118 -0
  20. package/dist/__tests__/comics/geometry.test.js.map +1 -0
  21. package/dist/__tests__/comics/html.test.d.ts +2 -0
  22. package/dist/__tests__/comics/html.test.d.ts.map +1 -0
  23. package/dist/__tests__/comics/html.test.js +253 -0
  24. package/dist/__tests__/comics/html.test.js.map +1 -0
  25. package/dist/__tests__/comics/lettering.test.d.ts +2 -0
  26. package/dist/__tests__/comics/lettering.test.d.ts.map +1 -0
  27. package/dist/__tests__/comics/lettering.test.js +241 -0
  28. package/dist/__tests__/comics/lettering.test.js.map +1 -0
  29. package/dist/__tests__/comics/script.test.d.ts +2 -0
  30. package/dist/__tests__/comics/script.test.d.ts.map +1 -0
  31. package/dist/__tests__/comics/script.test.js +147 -0
  32. package/dist/__tests__/comics/script.test.js.map +1 -0
  33. package/dist/__tests__/comics/split.test.d.ts +2 -0
  34. package/dist/__tests__/comics/split.test.d.ts.map +1 -0
  35. package/dist/__tests__/comics/split.test.js +120 -0
  36. package/dist/__tests__/comics/split.test.js.map +1 -0
  37. package/dist/__tests__/comics/spread.test.d.ts +2 -0
  38. package/dist/__tests__/comics/spread.test.d.ts.map +1 -0
  39. package/dist/__tests__/comics/spread.test.js +161 -0
  40. package/dist/__tests__/comics/spread.test.js.map +1 -0
  41. package/dist/__tests__/comics/strip.test.d.ts +2 -0
  42. package/dist/__tests__/comics/strip.test.d.ts.map +1 -0
  43. package/dist/__tests__/comics/strip.test.js +231 -0
  44. package/dist/__tests__/comics/strip.test.js.map +1 -0
  45. package/dist/__tests__/exports.test.js +89 -0
  46. package/dist/__tests__/exports.test.js.map +1 -1
  47. package/dist/__tests__/parse/containers.test.js +1 -1
  48. package/dist/__tests__/parse/containers.test.js.map +1 -1
  49. package/dist/canvas-backend/comic.d.ts +32 -0
  50. package/dist/canvas-backend/comic.d.ts.map +1 -0
  51. package/dist/canvas-backend/comic.js +216 -0
  52. package/dist/canvas-backend/comic.js.map +1 -0
  53. package/dist/canvas-backend/headerFooter.d.ts +4 -1
  54. package/dist/canvas-backend/headerFooter.d.ts.map +1 -1
  55. package/dist/canvas-backend/headerFooter.js +3 -1
  56. package/dist/canvas-backend/headerFooter.js.map +1 -1
  57. package/dist/canvas-backend/index.d.ts.map +1 -1
  58. package/dist/canvas-backend/index.js +7 -1
  59. package/dist/canvas-backend/index.js.map +1 -1
  60. package/dist/comics/art.d.ts +72 -0
  61. package/dist/comics/art.d.ts.map +1 -0
  62. package/dist/comics/art.js +154 -0
  63. package/dist/comics/art.js.map +1 -0
  64. package/dist/comics/fonts.d.ts +20 -0
  65. package/dist/comics/fonts.d.ts.map +1 -0
  66. package/dist/comics/fonts.js +42 -0
  67. package/dist/comics/fonts.js.map +1 -0
  68. package/dist/comics/geometry.d.ts +87 -0
  69. package/dist/comics/geometry.d.ts.map +1 -0
  70. package/dist/comics/geometry.js +272 -0
  71. package/dist/comics/geometry.js.map +1 -0
  72. package/dist/comics/index.d.ts +29 -0
  73. package/dist/comics/index.d.ts.map +1 -0
  74. package/dist/comics/index.js +18 -0
  75. package/dist/comics/index.js.map +1 -0
  76. package/dist/comics/layoutPage.d.ts +156 -0
  77. package/dist/comics/layoutPage.d.ts.map +1 -0
  78. package/dist/comics/layoutPage.js +697 -0
  79. package/dist/comics/layoutPage.js.map +1 -0
  80. package/dist/comics/lettering/__tests__/harness.test.d.ts +2 -0
  81. package/dist/comics/lettering/__tests__/harness.test.d.ts.map +1 -0
  82. package/dist/comics/lettering/__tests__/harness.test.js +273 -0
  83. package/dist/comics/lettering/__tests__/harness.test.js.map +1 -0
  84. package/dist/comics/lettering/__tests__/letterPanel.test.d.ts +2 -0
  85. package/dist/comics/lettering/__tests__/letterPanel.test.d.ts.map +1 -0
  86. package/dist/comics/lettering/__tests__/letterPanel.test.js +224 -0
  87. package/dist/comics/lettering/__tests__/letterPanel.test.js.map +1 -0
  88. package/dist/comics/lettering/__tests__/polish.test.d.ts +2 -0
  89. package/dist/comics/lettering/__tests__/polish.test.d.ts.map +1 -0
  90. package/dist/comics/lettering/__tests__/polish.test.js +357 -0
  91. package/dist/comics/lettering/__tests__/polish.test.js.map +1 -0
  92. package/dist/comics/lettering/__tests__/round2.test.d.ts +2 -0
  93. package/dist/comics/lettering/__tests__/round2.test.d.ts.map +1 -0
  94. package/dist/comics/lettering/__tests__/round2.test.js +289 -0
  95. package/dist/comics/lettering/__tests__/round2.test.js.map +1 -0
  96. package/dist/comics/lettering/__tests__/shapeText.test.d.ts +2 -0
  97. package/dist/comics/lettering/__tests__/shapeText.test.d.ts.map +1 -0
  98. package/dist/comics/lettering/__tests__/shapeText.test.js +98 -0
  99. package/dist/comics/lettering/__tests__/shapeText.test.js.map +1 -0
  100. package/dist/comics/lettering/__tests__/shapes.test.d.ts +2 -0
  101. package/dist/comics/lettering/__tests__/shapes.test.d.ts.map +1 -0
  102. package/dist/comics/lettering/__tests__/shapes.test.js +137 -0
  103. package/dist/comics/lettering/__tests__/shapes.test.js.map +1 -0
  104. package/dist/comics/lettering/__tests__/stubMeasure.d.ts +5 -0
  105. package/dist/comics/lettering/__tests__/stubMeasure.d.ts.map +1 -0
  106. package/dist/comics/lettering/__tests__/stubMeasure.js +47 -0
  107. package/dist/comics/lettering/__tests__/stubMeasure.js.map +1 -0
  108. package/dist/comics/lettering/__tests__/text.test.d.ts +2 -0
  109. package/dist/comics/lettering/__tests__/text.test.d.ts.map +1 -0
  110. package/dist/comics/lettering/__tests__/text.test.js +86 -0
  111. package/dist/comics/lettering/__tests__/text.test.js.map +1 -0
  112. package/dist/comics/lettering/geom.d.ts +78 -0
  113. package/dist/comics/lettering/geom.d.ts.map +1 -0
  114. package/dist/comics/lettering/geom.js +376 -0
  115. package/dist/comics/lettering/geom.js.map +1 -0
  116. package/dist/comics/lettering/index.d.ts +7 -0
  117. package/dist/comics/lettering/index.d.ts.map +1 -0
  118. package/dist/comics/lettering/index.js +8 -0
  119. package/dist/comics/lettering/index.js.map +1 -0
  120. package/dist/comics/lettering/letter.d.ts +7 -0
  121. package/dist/comics/lettering/letter.d.ts.map +1 -0
  122. package/dist/comics/lettering/letter.js +397 -0
  123. package/dist/comics/lettering/letter.js.map +1 -0
  124. package/dist/comics/lettering/placement.d.ts +117 -0
  125. package/dist/comics/lettering/placement.d.ts.map +1 -0
  126. package/dist/comics/lettering/placement.js +889 -0
  127. package/dist/comics/lettering/placement.js.map +1 -0
  128. package/dist/comics/lettering/presets.d.ts +15 -0
  129. package/dist/comics/lettering/presets.d.ts.map +1 -0
  130. package/dist/comics/lettering/presets.js +61 -0
  131. package/dist/comics/lettering/presets.js.map +1 -0
  132. package/dist/comics/lettering/shape-text.d.ts +72 -0
  133. package/dist/comics/lettering/shape-text.d.ts.map +1 -0
  134. package/dist/comics/lettering/shape-text.js +550 -0
  135. package/dist/comics/lettering/shape-text.js.map +1 -0
  136. package/dist/comics/lettering/shapes.d.ts +83 -0
  137. package/dist/comics/lettering/shapes.d.ts.map +1 -0
  138. package/dist/comics/lettering/shapes.js +512 -0
  139. package/dist/comics/lettering/shapes.js.map +1 -0
  140. package/dist/comics/lettering/text.d.ts +79 -0
  141. package/dist/comics/lettering/text.d.ts.map +1 -0
  142. package/dist/comics/lettering/text.js +488 -0
  143. package/dist/comics/lettering/text.js.map +1 -0
  144. package/dist/comics/lettering/types.d.ts +270 -0
  145. package/dist/comics/lettering/types.d.ts.map +1 -0
  146. package/dist/comics/lettering/types.js +9 -0
  147. package/dist/comics/lettering/types.js.map +1 -0
  148. package/dist/comics/page.d.ts +37 -0
  149. package/dist/comics/page.d.ts.map +1 -0
  150. package/dist/comics/page.js +285 -0
  151. package/dist/comics/page.js.map +1 -0
  152. package/dist/comics/paint.d.ts +65 -0
  153. package/dist/comics/paint.d.ts.map +1 -0
  154. package/dist/comics/paint.js +219 -0
  155. package/dist/comics/paint.js.map +1 -0
  156. package/dist/comics/script.d.ts +42 -0
  157. package/dist/comics/script.d.ts.map +1 -0
  158. package/dist/comics/script.js +302 -0
  159. package/dist/comics/script.js.map +1 -0
  160. package/dist/comics/split.d.ts +135 -0
  161. package/dist/comics/split.d.ts.map +1 -0
  162. package/dist/comics/split.js +514 -0
  163. package/dist/comics/split.js.map +1 -0
  164. package/dist/comics/spread.d.ts +36 -0
  165. package/dist/comics/spread.d.ts.map +1 -0
  166. package/dist/comics/spread.js +172 -0
  167. package/dist/comics/spread.js.map +1 -0
  168. package/dist/comics/strip.d.ts +77 -0
  169. package/dist/comics/strip.d.ts.map +1 -0
  170. package/dist/comics/strip.js +89 -0
  171. package/dist/comics/strip.js.map +1 -0
  172. package/dist/comics/transform.d.ts +37 -0
  173. package/dist/comics/transform.d.ts.map +1 -0
  174. package/dist/comics/transform.js +161 -0
  175. package/dist/comics/transform.js.map +1 -0
  176. package/dist/comics/types.d.ts +143 -0
  177. package/dist/comics/types.d.ts.map +1 -0
  178. package/dist/comics/types.js +13 -0
  179. package/dist/comics/types.js.map +1 -0
  180. package/dist/comics/warnings.d.ts +14 -0
  181. package/dist/comics/warnings.d.ts.map +1 -0
  182. package/dist/comics/warnings.js +94 -0
  183. package/dist/comics/warnings.js.map +1 -0
  184. package/dist/configWarnings.d.ts.map +1 -1
  185. package/dist/configWarnings.js +42 -0
  186. package/dist/configWarnings.js.map +1 -1
  187. package/dist/defaults/comics.d.ts +51 -0
  188. package/dist/defaults/comics.d.ts.map +1 -0
  189. package/dist/defaults/comics.js +385 -0
  190. package/dist/defaults/comics.js.map +1 -0
  191. package/dist/defaults/index.d.ts +1 -0
  192. package/dist/defaults/index.d.ts.map +1 -1
  193. package/dist/defaults/index.js +9 -0
  194. package/dist/defaults/index.js.map +1 -1
  195. package/dist/design/layout.d.ts +13 -1
  196. package/dist/design/layout.d.ts.map +1 -1
  197. package/dist/design/layout.js +17 -0
  198. package/dist/design/layout.js.map +1 -1
  199. package/dist/html-backend.d.ts +3 -0
  200. package/dist/html-backend.d.ts.map +1 -1
  201. package/dist/html-backend.js +44 -8
  202. package/dist/html-backend.js.map +1 -1
  203. package/dist/htmlComic.d.ts +69 -0
  204. package/dist/htmlComic.d.ts.map +1 -0
  205. package/dist/htmlComic.js +179 -0
  206. package/dist/htmlComic.js.map +1 -0
  207. package/dist/index.d.ts +8 -2
  208. package/dist/index.d.ts.map +1 -1
  209. package/dist/index.js +4 -0
  210. package/dist/index.js.map +1 -1
  211. package/dist/parse/blockParser.d.ts.map +1 -1
  212. package/dist/parse/blockParser.js +25 -1
  213. package/dist/parse/blockParser.js.map +1 -1
  214. package/dist/parse/indexMarks.d.ts.map +1 -1
  215. package/dist/parse/indexMarks.js +3 -0
  216. package/dist/parse/indexMarks.js.map +1 -1
  217. package/dist/parse/types.d.ts +7 -1
  218. package/dist/parse/types.d.ts.map +1 -1
  219. package/dist/pipeline/build.d.ts.map +1 -1
  220. package/dist/pipeline/build.js +251 -9
  221. package/dist/pipeline/build.js.map +1 -1
  222. package/dist/pipeline/config.d.ts.map +1 -1
  223. package/dist/pipeline/config.js +4 -1
  224. package/dist/pipeline/config.js.map +1 -1
  225. package/dist/pipeline/contentWarnings.d.ts.map +1 -1
  226. package/dist/pipeline/contentWarnings.js +58 -0
  227. package/dist/pipeline/contentWarnings.js.map +1 -1
  228. package/dist/pipeline/floatSlots.js +1 -1
  229. package/dist/pipeline/floatSlots.js.map +1 -1
  230. package/dist/pipeline/headerFooter.d.ts +6 -2
  231. package/dist/pipeline/headerFooter.d.ts.map +1 -1
  232. package/dist/pipeline/headerFooter.js +11 -3
  233. package/dist/pipeline/headerFooter.js.map +1 -1
  234. package/dist/pipeline/pageRoles.d.ts +1 -0
  235. package/dist/pipeline/pageRoles.d.ts.map +1 -1
  236. package/dist/pipeline/pageRoles.js +3 -0
  237. package/dist/pipeline/pageRoles.js.map +1 -1
  238. package/dist/pipeline/placement.d.ts +3 -3
  239. package/dist/pipeline/placement.d.ts.map +1 -1
  240. package/dist/pipeline/placement.js +4 -4
  241. package/dist/pipeline/placement.js.map +1 -1
  242. package/dist/types.d.ts +338 -1
  243. package/dist/types.d.ts.map +1 -1
  244. package/dist/vdt.d.ts +271 -2
  245. package/dist/vdt.d.ts.map +1 -1
  246. package/dist/vdt.js.map +1 -1
  247. package/dist/worker/fonts.d.ts +2 -1
  248. package/dist/worker/fonts.d.ts.map +1 -1
  249. package/dist/worker/fonts.js +11 -1
  250. package/dist/worker/fonts.js.map +1 -1
  251. 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