@heroiclands/package-build 22.14.3 → 22.14.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/CHANGELOG.md +6 -0
- package/docs/api.md +3 -2
- package/docs/content-format.md +22 -19
- package/docs/getting-started.md +4 -3
- package/engine/content-images.mjs +20 -10
- package/engine/pdf-render.mjs +30 -7
- package/package.json +1 -1
- package/types/engine/content-images.d.mts +7 -4
- package/types/engine/pdf-render.d.mts +9 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
# @heroiclands/package-build
|
|
2
2
|
|
|
3
|
+
## 22.14.4
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
**Illustrations** — Named image sizes set the display width on pages and in books. Images without a size use their natural dimensions within the available space.
|
|
8
|
+
|
|
3
9
|
## 22.14.3
|
|
4
10
|
|
|
5
11
|
### Patch Changes
|
package/docs/api.md
CHANGED
|
@@ -965,12 +965,12 @@ Raw HTML in a note's prose, reported. **A note is markdown.** What markdown cann
|
|
|
965
965
|
|
|
966
966
|
### `engine.contentImages`
|
|
967
967
|
|
|
968
|
-
An image saying how wide it is and where it sits. A
|
|
968
|
+
An image saying how wide it is and where it sits. A directive in the curly-attribute convention Pandoc and Kramdown use supplies a width class, a named `size:`, and a `float:` position. The named size sets a maximum display width for each surface.
|
|
969
969
|
|
|
970
970
|
| Export | Signature | Returns | Use it when |
|
|
971
971
|
| --------------------- | ----------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------- |
|
|
972
972
|
| `IMAGE_CLASSES` | `const IMAGE_CLASSES` | — | The width classes an image may carry, and what each means to a renderer. |
|
|
973
|
-
| `IMAGE_SIZES` | `const IMAGE_SIZES` | — | The named sizes an image directive accepts
|
|
973
|
+
| `IMAGE_SIZES` | `const IMAGE_SIZES` | — | The named sizes an image directive accepts. |
|
|
974
974
|
| `IMAGE_FLOATS` | `const IMAGE_FLOATS` | — | The `float:` positions an image may take, and where each puts it. |
|
|
975
975
|
| `IMAGE_FIGURE_CLASS` | `const IMAGE_FIGURE_CLASS` | — | The class every figure carries, whatever its width or position. |
|
|
976
976
|
| `IMAGE_PATTERN` | `const IMAGE_PATTERN` | — | A markdown image, with the directive it may carry. |
|
|
@@ -1167,6 +1167,7 @@ A note's markdown, and a document plan, rendered as Typst source. **This module
|
|
|
1167
1167
|
| `renderBook` | `renderBook({ plan, bodies, title, subtitle, front, fonts, version, preamble, banners })` | {string} A complete `.typ` document. | The whole book, as one Typst document. |
|
|
1168
1168
|
| `resolveDanglingLabels` | `function resolveDanglingLabels(source, findings` | {string} The same document, with no reference left dangling. | Point every internal link at a label the document actually declares. |
|
|
1169
1169
|
| `iconNamesIn` | `function iconNamesIn(markdown)` | {string[]} The names, in order of appearance, with repeats. | Every icon name a body uses, so a build can resolve them once. |
|
|
1170
|
+
| `BOOK_IMAGE_WIDTHS` | `const BOOK_IMAGE_WIDTHS` | — | The book measure requested by each named image size. |
|
|
1170
1171
|
| `bookTypstPreamble` | `function bookTypstPreamble()` | {string} Typst markup. | The Typst definitions the book's page furniture is drawn with. |
|
|
1171
1172
|
|
|
1172
1173
|
### `engine.pdfFonts`
|
package/docs/content-format.md
CHANGED
|
@@ -2066,10 +2066,10 @@ place for those words and this is it.
|
|
|
2066
2066
|
|
|
2067
2067
|
##### Width is a class, and the ordinary width carries no marker
|
|
2068
2068
|
|
|
2069
|
-
| `class` value | book
|
|
2070
|
-
| ------------- |
|
|
2071
|
-
| _(none)_ |
|
|
2072
|
-
| `.full-width` |
|
|
2069
|
+
| `class` value | book | website | Foundry journal |
|
|
2070
|
+
| ------------- | ----------------------------- | ------------------ | --------------- |
|
|
2071
|
+
| _(none)_ | natural width within a column | natural width | natural width |
|
|
2072
|
+
| `.full-width` | page measure | full content width | full page width |
|
|
2073
2073
|
|
|
2074
2074
|
The simple case needs no spelling, which is why the ordinary width has no name.
|
|
2075
2075
|
|
|
@@ -2080,21 +2080,24 @@ refused.
|
|
|
2080
2080
|
|
|
2081
2081
|
##### Named size is `size:`
|
|
2082
2082
|
|
|
2083
|
-
| `size` value |
|
|
2084
|
-
| ------------ |
|
|
2085
|
-
| `auto` |
|
|
2086
|
-
| `small` |
|
|
2087
|
-
| `medium` |
|
|
2088
|
-
| `large` |
|
|
2089
|
-
| `xlarge` |
|
|
2090
|
-
| `full-width` |
|
|
2091
|
-
|
|
2092
|
-
|
|
2093
|
-
|
|
2094
|
-
|
|
2095
|
-
|
|
2096
|
-
|
|
2097
|
-
|
|
2083
|
+
| `size` value | website and Foundry | book |
|
|
2084
|
+
| ------------ | ------------------- | ------------ |
|
|
2085
|
+
| `auto` | natural size | natural size |
|
|
2086
|
+
| `small` | 64 CSS pixels | 1.6 cm |
|
|
2087
|
+
| `medium` | 128 CSS pixels | 3.2 cm |
|
|
2088
|
+
| `large` | 256 CSS pixels | 5.6 cm |
|
|
2089
|
+
| `xlarge` | 512 CSS pixels | 8 cm |
|
|
2090
|
+
| `full-width` | content measure | page measure |
|
|
2091
|
+
|
|
2092
|
+
These are maximum display widths for Markdown images and embedded assets;
|
|
2093
|
+
height follows the file's aspect ratio. The available column, page, or browser
|
|
2094
|
+
measure caps every size, and print also caps image height within the page.
|
|
2095
|
+
Omitting `size:` means `auto`, which uses the file's natural dimensions within
|
|
2096
|
+
those bounds. `full-width` spans the book's page rather than one column. The
|
|
2097
|
+
`.full-width` class also grants page scope in print; when it accompanies a
|
|
2098
|
+
bounded `size:`, the image keeps that bounded width inside the page figure.
|
|
2099
|
+
A `size:` may sit before or after `float:` in the same directive, and neither
|
|
2100
|
+
changes the other's behavior.
|
|
2098
2101
|
|
|
2099
2102
|
##### Position is `float:`
|
|
2100
2103
|
|
package/docs/getting-started.md
CHANGED
|
@@ -370,9 +370,10 @@ position together:
|
|
|
370
370
|
```
|
|
371
371
|
|
|
372
372
|
The accepted sizes are `auto`, `small`, `medium`, `large`, `xlarge`, and
|
|
373
|
-
`full-width`. Omitting `size:` means `auto
|
|
374
|
-
|
|
375
|
-
|
|
373
|
+
`full-width`. Omitting `size:` means `auto`, which uses the image's natural
|
|
374
|
+
dimensions within the available measure. The other names set maximum display
|
|
375
|
+
widths for the website, Foundry, and book. See
|
|
376
|
+
[Images](content-format.md#images) for the surface sizes and layout rules.
|
|
376
377
|
|
|
377
378
|
## Step 6 — check the tree
|
|
378
379
|
|
|
@@ -25,7 +25,8 @@
|
|
|
25
25
|
* simple case needs no spelling. {@link IMAGE_CLASSES} holds the one class
|
|
26
26
|
* there is. **Position is `float:`**, and {@link IMAGE_FLOATS} holds the five
|
|
27
27
|
* values it takes. **Named size is `size:`**, and {@link IMAGE_SIZES} holds its
|
|
28
|
-
* values.
|
|
28
|
+
* values. The HTML surfaces receive a size class; the book receives a print
|
|
29
|
+
* measure.
|
|
29
30
|
*
|
|
30
31
|
* All three are closed, and an unrecognised value is **refused with a located
|
|
31
32
|
* diagnostic** rather than ignored. Ignoring is the failure worth preventing:
|
|
@@ -105,7 +106,7 @@ export const IMAGE_CLASSES = Object.freeze({
|
|
|
105
106
|
},
|
|
106
107
|
});
|
|
107
108
|
|
|
108
|
-
/** The named sizes an image directive accepts
|
|
109
|
+
/** The named sizes an image directive accepts. */
|
|
109
110
|
export const IMAGE_SIZES = Object.freeze([
|
|
110
111
|
"auto",
|
|
111
112
|
"small",
|
|
@@ -326,16 +327,17 @@ export function parseImageDirective(raw) {
|
|
|
326
327
|
/**
|
|
327
328
|
* The classes a figure carries, from a parsed directive.
|
|
328
329
|
*
|
|
329
|
-
* @param {{classes?: string[], float?: string}} [directive] - As parsed.
|
|
330
|
+
* @param {{classes?: string[], size?: string, float?: string}} [directive] - As parsed.
|
|
330
331
|
* @returns {string} A space-separated class list, always naming
|
|
331
332
|
* {@link IMAGE_FIGURE_CLASS} first.
|
|
332
333
|
*/
|
|
333
|
-
export function figureClasses({ classes = [], float = "" } = {}) {
|
|
334
|
+
export function figureClasses({ classes = [], size = "auto", float = "" } = {}) {
|
|
334
335
|
const names = [IMAGE_FIGURE_CLASS];
|
|
335
336
|
for (const name of classes) {
|
|
336
337
|
const spec = IMAGE_CLASSES[/** @type {keyof typeof IMAGE_CLASSES} */ (name)];
|
|
337
338
|
if (spec) names.push(spec.class);
|
|
338
339
|
}
|
|
340
|
+
if (size !== "auto" && IMAGE_SIZES.includes(size)) names.push(`note-image-size-${size}`);
|
|
339
341
|
const position = IMAGE_FLOATS[/** @type {keyof typeof IMAGE_FLOATS} */ (float)];
|
|
340
342
|
if (position) names.push(position.class);
|
|
341
343
|
return names.join(" ");
|
|
@@ -367,13 +369,14 @@ export function escapeHtml(text) {
|
|
|
367
369
|
* @param {string} image.src - The address, resolved for the surface.
|
|
368
370
|
* @param {string} [image.alt] - The alt text, which is also the caption.
|
|
369
371
|
* @param {string[]} [image.classes] - Width classes, from the directive.
|
|
372
|
+
* @param {string} [image.size] - Named display size, from the directive.
|
|
370
373
|
* @param {string} [image.float] - The float position, from the directive.
|
|
371
374
|
* @returns {string} The figure, as one HTML block.
|
|
372
375
|
*/
|
|
373
|
-
export function imageFigureHtml({ src, alt = "", classes = [], float = "" }) {
|
|
376
|
+
export function imageFigureHtml({ src, alt = "", classes = [], size = "auto", float = "" }) {
|
|
374
377
|
const caption = alt ? `\n<figcaption>${escapeHtml(alt)}</figcaption>` : "";
|
|
375
378
|
return (
|
|
376
|
-
`<figure class="${figureClasses({ classes, float })}">\n` +
|
|
379
|
+
`<figure class="${figureClasses({ classes, size, float })}">\n` +
|
|
377
380
|
`<img src="${escapeHtml(src)}" alt="${escapeHtml(alt)}">${caption}\n` +
|
|
378
381
|
`</figure>`
|
|
379
382
|
);
|
|
@@ -644,10 +647,16 @@ export function renderImageFigures(body, resolveSrc = (src) => src) {
|
|
|
644
647
|
let last = 0;
|
|
645
648
|
for (const image of imagesIn(text)) {
|
|
646
649
|
if (!image.block || imageSourceProblem(image.src) || image.title) continue;
|
|
647
|
-
const { classes, float, problems } = parseImageDirective(image.directive);
|
|
650
|
+
const { classes, size, float, problems } = parseImageDirective(image.directive);
|
|
648
651
|
if (problems.length) continue;
|
|
649
652
|
out += text.slice(last, image.index);
|
|
650
|
-
out += imageFigureHtml({
|
|
653
|
+
out += imageFigureHtml({
|
|
654
|
+
src: resolveSrc(image.src),
|
|
655
|
+
alt: image.alt,
|
|
656
|
+
classes,
|
|
657
|
+
size,
|
|
658
|
+
float,
|
|
659
|
+
});
|
|
651
660
|
last = image.index + image.length;
|
|
652
661
|
}
|
|
653
662
|
return out + text.slice(last);
|
|
@@ -696,6 +705,7 @@ export function imagePlugin(resolveSrc = (src) => src) {
|
|
|
696
705
|
src: resolveSrc(token.attrGet("src") ?? ""),
|
|
697
706
|
alt: token.content ?? "",
|
|
698
707
|
classes: token.meta.classes,
|
|
708
|
+
size: token.meta.size,
|
|
699
709
|
float: token.meta.float,
|
|
700
710
|
})}\n`;
|
|
701
711
|
};
|
|
@@ -733,12 +743,12 @@ function attachImageDirectives(tokens) {
|
|
|
733
743
|
directive = brace[0];
|
|
734
744
|
}
|
|
735
745
|
|
|
736
|
-
const { classes, float, problems } = parseImageDirective(directive);
|
|
746
|
+
const { classes, size, float, problems } = parseImageDirective(directive);
|
|
737
747
|
// Not ours to consume: leaving the braces in place is what makes an
|
|
738
748
|
// unrecognised value visible on the page instead of passing as ordinary.
|
|
739
749
|
if (problems.length) continue;
|
|
740
750
|
|
|
741
|
-
image.meta = { ...(image.meta ?? {}), block: true, classes, float };
|
|
751
|
+
image.meta = { ...(image.meta ?? {}), block: true, classes, size, float };
|
|
742
752
|
if (trailing) trailing.content = trailing.content.slice(directive.length);
|
|
743
753
|
// The figure is a block, so the paragraph that held it renders nothing.
|
|
744
754
|
tokens[i].hidden = true;
|
package/engine/pdf-render.mjs
CHANGED
|
@@ -66,6 +66,16 @@ import MarkdownIt from "markdown-it";
|
|
|
66
66
|
import { bookDraftNoticePreamble } from "./draft-notice.mjs";
|
|
67
67
|
import { iconPlugin, ICON_PATTERN } from "./content-icons.mjs";
|
|
68
68
|
import { IMAGE_CLASSES, IMAGE_FLOATS, imagePlugin } from "./content-images.mjs";
|
|
69
|
+
|
|
70
|
+
/** Requested print width for every named image size. */
|
|
71
|
+
export const BOOK_IMAGE_WIDTHS = Object.freeze({
|
|
72
|
+
auto: "auto",
|
|
73
|
+
small: "1.6cm",
|
|
74
|
+
medium: "3.2cm",
|
|
75
|
+
large: "5.6cm",
|
|
76
|
+
xlarge: "8cm",
|
|
77
|
+
"full-width": '"full-width"',
|
|
78
|
+
});
|
|
69
79
|
import { slugify } from "./content-slug.mjs";
|
|
70
80
|
|
|
71
81
|
/**
|
|
@@ -706,17 +716,19 @@ function renderLink(href, inner, ctx) {
|
|
|
706
716
|
*/
|
|
707
717
|
function renderImage(token, ctx) {
|
|
708
718
|
const alt = token.content || token.attrGet?.("alt") || "";
|
|
709
|
-
const caption =
|
|
710
|
-
alt ? `\n #text(size: 7.6pt, style: "italic", fill: luma(45%))[${escapeTypst(alt)}]` : "";
|
|
719
|
+
const caption = alt ? `[${escapeTypst(alt)}]` : "none";
|
|
711
720
|
const staged = ctx.images.get(token.attrGet?.("src") ?? "");
|
|
712
|
-
if (!staged)
|
|
721
|
+
if (!staged)
|
|
722
|
+
return alt ?
|
|
723
|
+
`\n#block(below: 0.6em)[#text(size: 7.6pt, style: "italic", fill: luma(45%))${caption}]\n\n`
|
|
724
|
+
: "";
|
|
713
725
|
|
|
714
|
-
const
|
|
715
|
-
|
|
716
|
-
` #image("${escapeTypstString(staged)}", width: 100%)${caption}\n]`;
|
|
726
|
+
const size = BOOK_IMAGE_WIDTHS[token.meta?.size] ?? BOOK_IMAGE_WIDTHS.auto;
|
|
727
|
+
const figure = `#book-image("${escapeTypstString(staged)}", requested: ${size}, caption: ${caption})`;
|
|
717
728
|
|
|
718
729
|
const width = token.meta?.classes?.[0];
|
|
719
|
-
const scope =
|
|
730
|
+
const scope =
|
|
731
|
+
token.meta?.size === "full-width" ? "parent" : (IMAGE_CLASSES[width]?.scope ?? "column");
|
|
720
732
|
const float = IMAGE_FLOATS[token.meta?.float];
|
|
721
733
|
// In the flow where it was written: no class asking for the page, and no
|
|
722
734
|
// position asking for the top or the bottom of the column.
|
|
@@ -804,6 +816,17 @@ export function bookTypstPreamble() {
|
|
|
804
816
|
"#let book-page-height = 11in",
|
|
805
817
|
"#let book-text-width = book-page-width - 2 * book-margin",
|
|
806
818
|
"#let book-text-height = book-page-height - 2 * book-margin",
|
|
819
|
+
// Use the file's natural measure for auto. Every requested width is
|
|
820
|
+
// bounded by the available measure and by the height of a page.
|
|
821
|
+
"#let book-image(path, requested: auto, caption: none) = layout(size => {\n" +
|
|
822
|
+
" let natural = measure(image(path))\n" +
|
|
823
|
+
' let target = if requested == auto { natural.width } else if requested == "full-width" { size.width } else { requested }\n' +
|
|
824
|
+
" let width = calc.min(target, size.width, book-text-height * 0.8 * (natural.width / natural.height))\n" +
|
|
825
|
+
" block(width: width, below: 0.6em)[\n" +
|
|
826
|
+
' #image(path, width: 100%, fit: "contain")\n' +
|
|
827
|
+
' #if caption != none { text(size: 7.6pt, style: "italic", fill: luma(45%))[#caption] }\n' +
|
|
828
|
+
" ]\n" +
|
|
829
|
+
"})",
|
|
807
830
|
// The title sets short of the measure, so a plate's last line does not
|
|
808
831
|
// run to the trimmed edge of the paper.
|
|
809
832
|
"#let book-title-measure = book-text-width - 2.5cm",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@heroiclands/package-build",
|
|
3
|
-
"version": "22.14.
|
|
3
|
+
"version": "22.14.4",
|
|
4
4
|
"description": "Shared toolchain for building and shipping a HeroicLands Foundry VTT package — content compilation, manifest, localization, staging, bundle, release and deployment.",
|
|
5
5
|
"license": "GPL-3.0-or-later",
|
|
6
6
|
"type": "module",
|
|
@@ -32,12 +32,13 @@ export function parseImageDirective(raw?: string): {
|
|
|
32
32
|
/**
|
|
33
33
|
* The classes a figure carries, from a parsed directive.
|
|
34
34
|
*
|
|
35
|
-
* @param {{classes?: string[], float?: string}} [directive] - As parsed.
|
|
35
|
+
* @param {{classes?: string[], size?: string, float?: string}} [directive] - As parsed.
|
|
36
36
|
* @returns {string} A space-separated class list, always naming
|
|
37
37
|
* {@link IMAGE_FIGURE_CLASS} first.
|
|
38
38
|
*/
|
|
39
|
-
export function figureClasses({ classes, float }?: {
|
|
39
|
+
export function figureClasses({ classes, size, float }?: {
|
|
40
40
|
classes?: string[];
|
|
41
|
+
size?: string;
|
|
41
42
|
float?: string;
|
|
42
43
|
}): string;
|
|
43
44
|
/**
|
|
@@ -58,13 +59,15 @@ export function escapeHtml(text: string): string;
|
|
|
58
59
|
* @param {string} image.src - The address, resolved for the surface.
|
|
59
60
|
* @param {string} [image.alt] - The alt text, which is also the caption.
|
|
60
61
|
* @param {string[]} [image.classes] - Width classes, from the directive.
|
|
62
|
+
* @param {string} [image.size] - Named display size, from the directive.
|
|
61
63
|
* @param {string} [image.float] - The float position, from the directive.
|
|
62
64
|
* @returns {string} The figure, as one HTML block.
|
|
63
65
|
*/
|
|
64
|
-
export function imageFigureHtml({ src, alt, classes, float }: {
|
|
66
|
+
export function imageFigureHtml({ src, alt, classes, size, float }: {
|
|
65
67
|
src: string;
|
|
66
68
|
alt?: string | undefined;
|
|
67
69
|
classes?: string[] | undefined;
|
|
70
|
+
size?: string | undefined;
|
|
68
71
|
float?: string | undefined;
|
|
69
72
|
}): string;
|
|
70
73
|
/**
|
|
@@ -249,7 +252,7 @@ export const IMAGE_CLASSES: Readonly<Record<string, {
|
|
|
249
252
|
scope: string;
|
|
250
253
|
describe: string;
|
|
251
254
|
}>>;
|
|
252
|
-
/** The named sizes an image directive accepts
|
|
255
|
+
/** The named sizes an image directive accepts. */
|
|
253
256
|
export const IMAGE_SIZES: readonly string[];
|
|
254
257
|
/**
|
|
255
258
|
* The `float:` positions an image may take, and where each puts it.
|
|
@@ -252,3 +252,12 @@ export function resolveDanglingLabels(source: string, findings?: object[]): stri
|
|
|
252
252
|
* @returns {string[]} The names, in order of appearance, with repeats.
|
|
253
253
|
*/
|
|
254
254
|
export function iconNamesIn(markdown: string): string[];
|
|
255
|
+
/** Requested print width for every named image size. */
|
|
256
|
+
export const BOOK_IMAGE_WIDTHS: Readonly<{
|
|
257
|
+
auto: "auto";
|
|
258
|
+
small: "1.6cm";
|
|
259
|
+
medium: "3.2cm";
|
|
260
|
+
large: "5.6cm";
|
|
261
|
+
xlarge: "8cm";
|
|
262
|
+
"full-width": "\"full-width\"";
|
|
263
|
+
}>;
|