@kensio/colophon 2.13.1 → 2.14.1
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/README.md +10 -0
- package/dist/config/defaults.d.ts +69 -2
- package/dist/config/defaults.d.ts.map +1 -1
- package/dist/config/defaults.js +61 -2
- package/dist/config/defaults.js.map +1 -1
- package/dist/config/index.d.ts.map +1 -1
- package/dist/config/index.js +2 -0
- package/dist/config/index.js.map +1 -1
- package/dist/config/safe-area.d.ts +27 -0
- package/dist/config/safe-area.d.ts.map +1 -0
- package/dist/config/safe-area.js +81 -0
- package/dist/config/safe-area.js.map +1 -0
- package/dist/config/size.d.ts.map +1 -1
- package/dist/config/size.js +1 -0
- package/dist/config/size.js.map +1 -1
- package/dist/core/index.d.ts +1 -1
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/index.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/layout/block.d.ts +6 -0
- package/dist/layout/block.d.ts.map +1 -1
- package/dist/layout/block.js.map +1 -1
- package/dist/layout/draw.d.ts.map +1 -1
- package/dist/layout/draw.js +3 -0
- package/dist/layout/draw.js.map +1 -1
- package/dist/layout/index.d.ts +1 -1
- package/dist/layout/index.d.ts.map +1 -1
- package/dist/layout/index.js +1 -1
- package/dist/layout/index.js.map +1 -1
- package/dist/templates/article/index.js +1 -1
- package/dist/templates/article/index.js.map +1 -1
- package/dist/templates/banner/badge.d.ts +8 -1
- package/dist/templates/banner/badge.d.ts.map +1 -1
- package/dist/templates/banner/badge.js +8 -4
- package/dist/templates/banner/badge.js.map +1 -1
- package/dist/templates/banner/index.d.ts.map +1 -1
- package/dist/templates/banner/index.js +10 -25
- package/dist/templates/banner/index.js.map +1 -1
- package/dist/templates/bottom.d.ts +1 -1
- package/dist/templates/bottom.d.ts.map +1 -1
- package/dist/templates/bottom.js +5 -5
- package/dist/templates/bottom.js.map +1 -1
- package/dist/templates/card/index.d.ts.map +1 -1
- package/dist/templates/card/index.js +9 -25
- package/dist/templates/card/index.js.map +1 -1
- package/dist/templates/cover/index.d.ts +21 -0
- package/dist/templates/cover/index.d.ts.map +1 -0
- package/dist/templates/cover/index.js +67 -0
- package/dist/templates/cover/index.js.map +1 -0
- package/dist/templates/cover/lines.d.ts +21 -0
- package/dist/templates/cover/lines.d.ts.map +1 -0
- package/dist/templates/cover/lines.js +77 -0
- package/dist/templates/cover/lines.js.map +1 -0
- package/dist/templates/cover/lockup.d.ts +54 -0
- package/dist/templates/cover/lockup.d.ts.map +1 -0
- package/dist/templates/cover/lockup.js +78 -0
- package/dist/templates/cover/lockup.js.map +1 -0
- package/dist/templates/cover/tracking.d.ts +16 -0
- package/dist/templates/cover/tracking.d.ts.map +1 -0
- package/dist/templates/cover/tracking.js +40 -0
- package/dist/templates/cover/tracking.js.map +1 -0
- package/dist/templates/docs/index.js +3 -3
- package/dist/templates/docs/index.js.map +1 -1
- package/dist/templates/event/index.js +3 -3
- package/dist/templates/event/index.js.map +1 -1
- package/dist/templates/footer.d.ts +3 -0
- package/dist/templates/footer.d.ts.map +1 -1
- package/dist/templates/footer.js +16 -1
- package/dist/templates/footer.js.map +1 -1
- package/dist/templates/frame.d.ts +27 -3
- package/dist/templates/frame.d.ts.map +1 -1
- package/dist/templates/frame.js +19 -7
- package/dist/templates/frame.js.map +1 -1
- package/dist/templates/index.d.ts +1 -0
- package/dist/templates/index.d.ts.map +1 -1
- package/dist/templates/index.js +3 -0
- package/dist/templates/index.js.map +1 -1
- package/dist/templates/logo.d.ts +6 -2
- package/dist/templates/logo.d.ts.map +1 -1
- package/dist/templates/logo.js +10 -6
- package/dist/templates/logo.js.map +1 -1
- package/dist/templates/photo/index.d.ts.map +1 -1
- package/dist/templates/photo/index.js +9 -4
- package/dist/templates/photo/index.js.map +1 -1
- package/dist/templates/quote/index.js +2 -2
- package/dist/templates/quote/index.js.map +1 -1
- package/dist/templates/release/index.js +3 -3
- package/dist/templates/release/index.js.map +1 -1
- package/dist/templates/stat/index.js +3 -3
- package/dist/templates/stat/index.js.map +1 -1
- package/dist/templates/thumbnail/index.js +3 -3
- package/dist/templates/thumbnail/index.js.map +1 -1
- package/dist/templates/wordmark/index.js +2 -2
- package/dist/templates/wordmark/index.js.map +1 -1
- package/dist/text/element.d.ts +5 -0
- package/dist/text/element.d.ts.map +1 -1
- package/dist/text/element.js +4 -1
- package/dist/text/element.js.map +1 -1
- package/dist/text/index.d.ts +1 -0
- package/dist/text/index.d.ts.map +1 -1
- package/dist/text/index.js +1 -0
- package/dist/text/index.js.map +1 -1
- package/dist/text/track.d.ts +30 -0
- package/dist/text/track.d.ts.map +1 -0
- package/dist/text/track.js +44 -0
- package/dist/text/track.js.map +1 -0
- package/dist/types.d.ts +49 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/validate/collect.d.ts.map +1 -1
- package/dist/validate/collect.js +2 -1
- package/dist/validate/collect.js.map +1 -1
- package/dist/validate/keys.d.ts +2 -0
- package/dist/validate/keys.d.ts.map +1 -1
- package/dist/validate/keys.js +9 -0
- package/dist/validate/keys.js.map +1 -1
- package/dist/validate/overrides.d.ts.map +1 -1
- package/dist/validate/overrides.js +2 -1
- package/dist/validate/overrides.js.map +1 -1
- package/package.json +1 -1
|
@@ -2,15 +2,19 @@ import { box, textElement } from "../../layout/index.js";
|
|
|
2
2
|
/**
|
|
3
3
|
* The corner badge: a rounded plate with the badge text on it, sized from the
|
|
4
4
|
* plate height so it scales with the image rather than the text length.
|
|
5
|
+
*
|
|
6
|
+
* `corner` is where the plate's top-left goes, which the template works out
|
|
7
|
+
* from its frame rather than from the image, so that a safe area brings the
|
|
8
|
+
* badge in along with everything else.
|
|
5
9
|
*/
|
|
6
|
-
export function renderBadge(badge, fontFamily, brand,
|
|
10
|
+
export function renderBadge(badge, fontFamily, brand, corner, height) {
|
|
7
11
|
const fontSize = Math.round(height * 0.6);
|
|
8
12
|
const padX = Math.round(height * 0.36);
|
|
9
13
|
const width = Math.round(badge.text.length * fontSize * 0.64 + padX * 2);
|
|
10
|
-
const plate = box({ x:
|
|
14
|
+
const plate = box({ x: corner.x, y: corner.y, width, height }, { radius: Math.round(height * 0.1), fill: badge.background ?? "#ffffff" });
|
|
11
15
|
const text = textElement(badge.text, {
|
|
12
|
-
x:
|
|
13
|
-
y:
|
|
16
|
+
x: corner.x + padX,
|
|
17
|
+
y: corner.y + Math.round(height * 0.72),
|
|
14
18
|
fontFamily,
|
|
15
19
|
fontSize,
|
|
16
20
|
fontWeight: 900,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"badge.js","sourceRoot":"","sources":["../../../src/templates/banner/badge.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AAGzD
|
|
1
|
+
{"version":3,"file":"badge.js","sourceRoot":"","sources":["../../../src/templates/banner/badge.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AAGzD;;;;;;;GAOG;AACH,MAAM,UAAU,WAAW,CACzB,KAAY,EACZ,UAAkB,EAClB,KAAa,EACb,MAAkD,EAClD,MAAc;IAEd,MAAM,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,GAAG,GAAG,CAAC,CAAC;IAC1C,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IACvC,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,GAAG,QAAQ,GAAG,IAAI,GAAG,IAAI,GAAG,CAAC,CAAC,CAAC;IAEzE,MAAM,KAAK,GAAG,GAAG,CACf,EAAE,CAAC,EAAE,MAAM,CAAC,CAAC,EAAE,CAAC,EAAE,MAAM,CAAC,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,EAC3C,EAAE,MAAM,EAAE,IAAI,CAAC,KAAK,CAAC,MAAM,GAAG,GAAG,CAAC,EAAE,IAAI,EAAE,KAAK,CAAC,UAAU,IAAI,SAAS,EAAE,CAC1E,CAAC;IAEF,MAAM,IAAI,GAAG,WAAW,CAAC,KAAK,CAAC,IAAI,EAAE;QACnC,CAAC,EAAE,MAAM,CAAC,CAAC,GAAG,IAAI;QAClB,CAAC,EAAE,MAAM,CAAC,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,GAAG,IAAI,CAAC;QACvC,UAAU;QACV,QAAQ;QACR,UAAU,EAAE,GAAG;QACf,IAAI,EAAE,KAAK,CAAC,KAAK,IAAI,KAAK;KAC3B,CAAC,CAAC;IAEH,OAAO,KAAK,GAAG,IAAI,CAAC;AACtB,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/templates/banner/index.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,QAAQ,EAAmB,MAAM,gBAAgB,CAAC;AAQhE;;;;;;;;GAQG;AACH,eAAO,MAAM,cAAc,EAAE,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/templates/banner/index.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,QAAQ,EAAmB,MAAM,gBAAgB,CAAC;AAQhE;;;;;;;;GAQG;AACH,eAAO,MAAM,cAAc,EAAE,QA+D5B,CAAC"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { drawLines
|
|
1
|
+
import { drawLines } from "../../layout/index.js";
|
|
2
2
|
import { optionalString } from "../../props.js";
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
3
|
+
import { footerLine } from "../bottom.js";
|
|
4
|
+
import { contentArea, imageFrame, markRoom } from "../frame.js";
|
|
5
5
|
import { logoElement, logoRect } from "../logo.js";
|
|
6
6
|
import { badgeFor } from "./badge-props.js";
|
|
7
7
|
import { renderBadge } from "./badge.js";
|
|
@@ -18,27 +18,19 @@ import { bannerLines } from "./lines.js";
|
|
|
18
18
|
export const bannerTemplate = {
|
|
19
19
|
name: "banner",
|
|
20
20
|
render({ props, config, dimensions, measure, logo, avatar, }) {
|
|
21
|
-
const {
|
|
21
|
+
const { height } = dimensions;
|
|
22
22
|
const { fontFamily } = config;
|
|
23
|
-
const
|
|
24
|
-
const
|
|
23
|
+
const frame = imageFrame(dimensions, config, avatar);
|
|
24
|
+
const { pad, full } = frame;
|
|
25
25
|
const badgeHeight = Math.round(height * 0.072);
|
|
26
|
-
const mark = logoRect(logo,
|
|
26
|
+
const mark = logoRect(logo, full, pad, "start");
|
|
27
27
|
const badge = badgeFor(props, config);
|
|
28
28
|
const subtitle = optionalString(props.subtitle);
|
|
29
29
|
const hasSubtitle = subtitle !== undefined && subtitle !== "";
|
|
30
30
|
// The text sits between whatever the marks along the top and the footer
|
|
31
31
|
// along the bottom leave behind.
|
|
32
32
|
const topMark = Math.max(badge === undefined ? 0 : badgeHeight, mark?.height ?? 0);
|
|
33
|
-
const area =
|
|
34
|
-
top: pad + (topMark === 0 ? 0 : topMark + Math.round(height * 0.03)),
|
|
35
|
-
right: pad,
|
|
36
|
-
left: pad,
|
|
37
|
-
bottom: pad +
|
|
38
|
-
(hasFooter(config) || avatar !== undefined
|
|
39
|
-
? footerFs + Math.round(height * 0.02)
|
|
40
|
-
: 0),
|
|
41
|
-
});
|
|
33
|
+
const area = contentArea(frame, markRoom(topMark, Math.round(height * 0.03)));
|
|
42
34
|
const lines = bannerLines(props, {
|
|
43
35
|
height,
|
|
44
36
|
contentWidth: area.width,
|
|
@@ -55,15 +47,8 @@ export const bannerTemplate = {
|
|
|
55
47
|
});
|
|
56
48
|
const badgeMark = badge === undefined
|
|
57
49
|
? ""
|
|
58
|
-
: renderBadge(badge, fontFamily, config.colors.brand, pad, badgeHeight);
|
|
59
|
-
const footer =
|
|
60
|
-
x: pad,
|
|
61
|
-
y: footerBaseline(height, pad, footerFs),
|
|
62
|
-
fontSize: footerFs,
|
|
63
|
-
opacity: 0.78,
|
|
64
|
-
left: pad,
|
|
65
|
-
width: width - pad * 2,
|
|
66
|
-
}, avatar, measure);
|
|
50
|
+
: renderBadge(badge, fontFamily, config.colors.brand, { x: full.x + pad, y: full.y + pad }, badgeHeight);
|
|
51
|
+
const footer = footerLine(config, frame, avatar, measure, "start", 0.78);
|
|
67
52
|
return badgeMark + logoElement(logo, mark) + body + footer;
|
|
68
53
|
},
|
|
69
54
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/templates/banner/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/templates/banner/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -10,7 +10,7 @@ import type { Frame } from "./frame.js";
|
|
|
10
10
|
* else, such as the `article` with its byline at the other end of the same
|
|
11
11
|
* line, still calls `attribution` itself.
|
|
12
12
|
*/
|
|
13
|
-
export declare function footerLine(config: ResolvedConfig, frame: Frame,
|
|
13
|
+
export declare function footerLine(config: ResolvedConfig, frame: Frame, avatar: ImageAsset | undefined, measure: MeasureText, align?: "start" | "middle", opacity?: number): string;
|
|
14
14
|
/**
|
|
15
15
|
* Draw a block of lines in a slot a `stack` handed back, or nothing where
|
|
16
16
|
* there was no slot to draw it in.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bottom.d.ts","sourceRoot":"","sources":["../../src/templates/bottom.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AACrE,OAAO,KAAK,EAAE,UAAU,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAE3E,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AAKxC;;;;;;;;GAQG;AACH,wBAAgB,UAAU,CACxB,MAAM,EAAE,cAAc,EACtB,KAAK,EAAE,KAAK,EACZ,
|
|
1
|
+
{"version":3,"file":"bottom.d.ts","sourceRoot":"","sources":["../../src/templates/bottom.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AACrE,OAAO,KAAK,EAAE,UAAU,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAE3E,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AAKxC;;;;;;;;GAQG;AACH,wBAAgB,UAAU,CACxB,MAAM,EAAE,cAAc,EACtB,KAAK,EAAE,KAAK,EACZ,MAAM,EAAE,UAAU,GAAG,SAAS,EAC9B,OAAO,EAAE,WAAW,EACpB,KAAK,GAAE,OAAO,GAAG,QAAkB,EACnC,OAAO,GAAE,MAAuB,GAC/B,MAAM,CAkBR;AAED;;;;;;;GAOG;AACH,wBAAgB,QAAQ,CACtB,KAAK,EAAE,SAAS,QAAQ,EAAE,EAC1B,IAAI,EAAE,IAAI,GAAG,SAAS,EACtB,KAAK,EAAE,UAAU,GAChB,MAAM,CAER"}
|
package/dist/templates/bottom.js
CHANGED
|
@@ -11,16 +11,16 @@ const defaultOpacity = 0.75;
|
|
|
11
11
|
* else, such as the `article` with its byline at the other end of the same
|
|
12
12
|
* line, still calls `attribution` itself.
|
|
13
13
|
*/
|
|
14
|
-
export function footerLine(config, frame,
|
|
15
|
-
const { pad } = frame;
|
|
14
|
+
export function footerLine(config, frame, avatar, measure, align = "start", opacity = defaultOpacity) {
|
|
15
|
+
const { pad, full } = frame;
|
|
16
16
|
return attribution(config, {
|
|
17
|
-
x: align === "middle" ? Math.round(
|
|
17
|
+
x: align === "middle" ? Math.round(full.x + full.width / 2) : full.x + pad,
|
|
18
18
|
y: frame.footerY,
|
|
19
19
|
fontSize: frame.footerFontSize,
|
|
20
20
|
opacity,
|
|
21
21
|
...(align === "middle" && { anchor: "middle" }),
|
|
22
|
-
left: pad,
|
|
23
|
-
width:
|
|
22
|
+
left: full.x + pad,
|
|
23
|
+
width: full.width - pad * 2,
|
|
24
24
|
}, avatar, measure);
|
|
25
25
|
}
|
|
26
26
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bottom.js","sourceRoot":"","sources":["../../src/templates/bottom.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAG/C,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAG/C,4DAA4D;AAC5D,MAAM,cAAc,GAAG,IAAI,CAAC;AAE5B;;;;;;;;GAQG;AACH,MAAM,UAAU,UAAU,CACxB,MAAsB,EACtB,KAAY,EACZ,
|
|
1
|
+
{"version":3,"file":"bottom.js","sourceRoot":"","sources":["../../src/templates/bottom.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAG/C,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAG/C,4DAA4D;AAC5D,MAAM,cAAc,GAAG,IAAI,CAAC;AAE5B;;;;;;;;GAQG;AACH,MAAM,UAAU,UAAU,CACxB,MAAsB,EACtB,KAAY,EACZ,MAA8B,EAC9B,OAAoB,EACpB,KAAK,GAAuB,OAAO,EACnC,OAAO,GAAW,cAAc;IAEhC,MAAM,EAAE,GAAG,EAAE,IAAI,EAAE,GAAG,KAAK,CAAC;IAE5B,OAAO,WAAW,CAChB,MAAM,EACN;QACE,CAAC,EACC,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,GAAG,IAAI,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,GAAG,GAAG;QACzE,CAAC,EAAE,KAAK,CAAC,OAAO;QAChB,QAAQ,EAAE,KAAK,CAAC,cAAc;QAC9B,OAAO;QACP,GAAG,CAAC,KAAK,KAAK,QAAQ,IAAI,EAAE,MAAM,EAAE,QAAiB,EAAE,CAAC;QACxD,IAAI,EAAE,IAAI,CAAC,CAAC,GAAG,GAAG;QAClB,KAAK,EAAE,IAAI,CAAC,KAAK,GAAG,GAAG,GAAG,CAAC;KAC5B,EACD,MAAM,EACN,OAAO,CACR,CAAC;AACJ,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,QAAQ,CACtB,KAA0B,EAC1B,IAAsB,EACtB,KAAiB;IAEjB,OAAO,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC,KAAK,EAAE,IAAI,EAAE,KAAK,CAAC,CAAC;AACjE,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/templates/card/index.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,QAAQ,EAAmB,MAAM,gBAAgB,CAAC;AAMhE;;;;;;GAMG;AACH,eAAO,MAAM,YAAY,EAAE,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/templates/card/index.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,QAAQ,EAAmB,MAAM,gBAAgB,CAAC;AAMhE;;;;;;GAMG;AACH,eAAO,MAAM,YAAY,EAAE,QAoC1B,CAAC"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { drawLines
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
1
|
+
import { drawLines } from "../../layout/index.js";
|
|
2
|
+
import { footerLine } from "../bottom.js";
|
|
3
|
+
import { contentArea, imageFrame, markRoom } from "../frame.js";
|
|
4
4
|
import { logoElement, logoRect } from "../logo.js";
|
|
5
5
|
import { cardLines } from "./lines.js";
|
|
6
6
|
/**
|
|
@@ -13,19 +13,11 @@ import { cardLines } from "./lines.js";
|
|
|
13
13
|
export const cardTemplate = {
|
|
14
14
|
name: "card",
|
|
15
15
|
render({ props, config, dimensions, measure, logo, avatar, }) {
|
|
16
|
-
const {
|
|
17
|
-
const
|
|
18
|
-
const
|
|
19
|
-
const mark = logoRect(logo,
|
|
20
|
-
const area =
|
|
21
|
-
top: pad + (mark === undefined ? 0 : mark.height + pad / 2),
|
|
22
|
-
right: pad,
|
|
23
|
-
left: pad,
|
|
24
|
-
bottom: pad +
|
|
25
|
-
(hasFooter(config) || avatar !== undefined
|
|
26
|
-
? footerFs + Math.round(height * 0.02)
|
|
27
|
-
: 0),
|
|
28
|
-
});
|
|
16
|
+
const { height } = dimensions;
|
|
17
|
+
const frame = imageFrame(dimensions, config, avatar, { padScale: 0.09 });
|
|
18
|
+
const { pad } = frame;
|
|
19
|
+
const mark = logoRect(logo, frame.full, pad, "middle");
|
|
20
|
+
const area = contentArea(frame, markRoom(mark?.height, pad / 2));
|
|
29
21
|
const lines = cardLines(props, {
|
|
30
22
|
measure,
|
|
31
23
|
fontFamily: config.fontFamily,
|
|
@@ -40,15 +32,7 @@ export const cardTemplate = {
|
|
|
40
32
|
lineHeight: 1.28,
|
|
41
33
|
anchor: "middle",
|
|
42
34
|
});
|
|
43
|
-
const footer =
|
|
44
|
-
x: Math.round(width / 2),
|
|
45
|
-
y: footerBaseline(height, pad, footerFs),
|
|
46
|
-
fontSize: footerFs,
|
|
47
|
-
opacity: 0.75,
|
|
48
|
-
anchor: "middle",
|
|
49
|
-
left: pad,
|
|
50
|
-
width: width - pad * 2,
|
|
51
|
-
}, avatar, measure);
|
|
35
|
+
const footer = footerLine(config, frame, avatar, measure, "middle");
|
|
52
36
|
return logoElement(logo, mark) + body + footer;
|
|
53
37
|
},
|
|
54
38
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/templates/card/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/templates/card/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAElD,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC1C,OAAO,EAAE,WAAW,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAChE,OAAO,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AACnD,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAEvC;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,YAAY,GAAa;IACpC,IAAI,EAAE,MAAM;IACZ,MAAM,CAAC,EACL,KAAK,EACL,MAAM,EACN,UAAU,EACV,OAAO,EACP,IAAI,EACJ,MAAM,GACU;QAChB,MAAM,EAAE,MAAM,EAAE,GAAG,UAAU,CAAC;QAC9B,MAAM,KAAK,GAAG,UAAU,CAAC,UAAU,EAAE,MAAM,EAAE,MAAM,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC;QACzE,MAAM,EAAE,GAAG,EAAE,GAAG,KAAK,CAAC;QACtB,MAAM,IAAI,GAAG,QAAQ,CAAC,IAAI,EAAE,KAAK,CAAC,IAAI,EAAE,GAAG,EAAE,QAAQ,CAAC,CAAC;QACvD,MAAM,IAAI,GAAG,WAAW,CAAC,KAAK,EAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC;QAEjE,MAAM,KAAK,GAAG,SAAS,CAAC,KAAK,EAAE;YAC7B,OAAO;YACP,UAAU,EAAE,MAAM,CAAC,UAAU;YAC7B,YAAY,EAAE,IAAI,CAAC,KAAK;YACxB,OAAO,EAAE,IAAI,CAAC,KAAK,CAAC,MAAM,GAAG,GAAG,CAAC;YACjC,KAAK,EAAE,IAAI,CAAC,KAAK,CAAC,MAAM,GAAG,KAAK,CAAC;YACjC,MAAM;SACP,CAAC,CAAC;QAEH,MAAM,IAAI,GAAG,SAAS,CAAC,KAAK,EAAE,IAAI,EAAE;YAClC,UAAU,EAAE,MAAM,CAAC,UAAU;YAC7B,IAAI,EAAE,MAAM,CAAC,MAAM,CAAC,UAAU;YAC9B,UAAU,EAAE,IAAI;YAChB,MAAM,EAAE,QAAQ;SACjB,CAAC,CAAC;QAEH,MAAM,MAAM,GAAG,UAAU,CAAC,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,QAAQ,CAAC,CAAC;QAEpE,OAAO,WAAW,CAAC,IAAI,EAAE,IAAI,CAAC,GAAG,IAAI,GAAG,MAAM,CAAC;IACjD,CAAC;CACF,CAAC"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { Template } from "../../types.js";
|
|
2
|
+
/**
|
|
3
|
+
* A profile cover: the mark beside the name, with a tagline under it.
|
|
4
|
+
*
|
|
5
|
+
* This is the header image at the top of a profile rather than a card attached
|
|
6
|
+
* to a post, so it is made once for a site and usually declared in
|
|
7
|
+
* `config.extra`. What makes it its own template rather than the `wordmark` at
|
|
8
|
+
* other proportions is the shape of the space: a cover is a strip somewhere
|
|
9
|
+
* between 2.6:1 and 6:1, and a layout that stacks a mark above a name has
|
|
10
|
+
* nothing left to set the name in.
|
|
11
|
+
*
|
|
12
|
+
* Every platform crops a cover and draws a circular avatar over one corner of
|
|
13
|
+
* it, so where the words may go is not the image. That is what `config.safeArea`
|
|
14
|
+
* describes and what the cover presets in `SIZE_PRESETS` carry, and by the time
|
|
15
|
+
* a template sees a frame it has already been applied. The one thing this
|
|
16
|
+
* template does about it is size its text from the room it actually has rather
|
|
17
|
+
* than from the image, which on a YouTube banner is the difference between 423
|
|
18
|
+
* pixels and 1440.
|
|
19
|
+
*/
|
|
20
|
+
export declare const coverTemplate: Template;
|
|
21
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/templates/cover/index.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,QAAQ,EAAmB,MAAM,gBAAgB,CAAC;AAoBhE;;;;;;;;;;;;;;;;;GAiBG;AACH,eAAO,MAAM,aAAa,EAAE,QA0C3B,CAAC"}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { safeRect } from "../../config/safe-area.js";
|
|
2
|
+
import { drawLines } from "../../layout/index.js";
|
|
3
|
+
import { footerLine } from "../bottom.js";
|
|
4
|
+
import { contentArea, imageFrame } from "../frame.js";
|
|
5
|
+
import { coverLines } from "./lines.js";
|
|
6
|
+
import { linesWidth, lockup, markElement, markSpace } from "./lockup.js";
|
|
7
|
+
/**
|
|
8
|
+
* The margin, as a fraction of the frame's height rather than its width.
|
|
9
|
+
*
|
|
10
|
+
* A cover is a strip between about 2.6:1 and 6:1, so a margin taken from the
|
|
11
|
+
* width is a margin measured against the one dimension that has room to spare.
|
|
12
|
+
* On a LinkedIn Page cover the usual 7.5% of the width is 208px above and
|
|
13
|
+
* below a band 504px tall. The safe area has already done the horizontal work,
|
|
14
|
+
* which is the other half of why this is the axis that matters here.
|
|
15
|
+
*/
|
|
16
|
+
const padScale = 0.09;
|
|
17
|
+
/** How the name and its tagline are stacked. */
|
|
18
|
+
const lineHeight = 1.15;
|
|
19
|
+
/**
|
|
20
|
+
* A profile cover: the mark beside the name, with a tagline under it.
|
|
21
|
+
*
|
|
22
|
+
* This is the header image at the top of a profile rather than a card attached
|
|
23
|
+
* to a post, so it is made once for a site and usually declared in
|
|
24
|
+
* `config.extra`. What makes it its own template rather than the `wordmark` at
|
|
25
|
+
* other proportions is the shape of the space: a cover is a strip somewhere
|
|
26
|
+
* between 2.6:1 and 6:1, and a layout that stacks a mark above a name has
|
|
27
|
+
* nothing left to set the name in.
|
|
28
|
+
*
|
|
29
|
+
* Every platform crops a cover and draws a circular avatar over one corner of
|
|
30
|
+
* it, so where the words may go is not the image. That is what `config.safeArea`
|
|
31
|
+
* describes and what the cover presets in `SIZE_PRESETS` carry, and by the time
|
|
32
|
+
* a template sees a frame it has already been applied. The one thing this
|
|
33
|
+
* template does about it is size its text from the room it actually has rather
|
|
34
|
+
* than from the image, which on a YouTube banner is the difference between 423
|
|
35
|
+
* pixels and 1440.
|
|
36
|
+
*/
|
|
37
|
+
export const coverTemplate = {
|
|
38
|
+
name: "cover",
|
|
39
|
+
render({ props, config, dimensions, measure, logo, avatar, }) {
|
|
40
|
+
const { fontFamily } = config;
|
|
41
|
+
const safe = safeRect(dimensions, config.safeArea);
|
|
42
|
+
const frame = imageFrame(dimensions, config, avatar, {
|
|
43
|
+
pad: Math.round(safe.height * padScale),
|
|
44
|
+
});
|
|
45
|
+
const area = contentArea(frame);
|
|
46
|
+
const mark = markSpace(logo, area.height);
|
|
47
|
+
const lines = coverLines(props, {
|
|
48
|
+
measure,
|
|
49
|
+
fontFamily,
|
|
50
|
+
contentWidth: area.width - mark.width - mark.gap,
|
|
51
|
+
contentHeight: area.height,
|
|
52
|
+
});
|
|
53
|
+
const placed = lockup(area, mark, linesWidth(lines, measure, fontFamily));
|
|
54
|
+
const body = drawLines(lines, placed.text, {
|
|
55
|
+
fontFamily,
|
|
56
|
+
fill: config.colors.foreground,
|
|
57
|
+
lineHeight,
|
|
58
|
+
anchor: "start",
|
|
59
|
+
});
|
|
60
|
+
return (markElement(logo, placed.mark) +
|
|
61
|
+
body +
|
|
62
|
+
// Centred, because the lockup above it is, and a footer set against the
|
|
63
|
+
// left margin of a strip has nothing above it to line up with.
|
|
64
|
+
footerLine(config, frame, avatar, measure, "middle"));
|
|
65
|
+
},
|
|
66
|
+
};
|
|
67
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/templates/cover/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,2BAA2B,CAAC;AACrD,OAAO,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAElD,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC1C,OAAO,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACtD,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AACxC,OAAO,EAAE,UAAU,EAAE,MAAM,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAEzE;;;;;;;;GAQG;AACH,MAAM,QAAQ,GAAG,IAAI,CAAC;AAEtB,gDAAgD;AAChD,MAAM,UAAU,GAAG,IAAI,CAAC;AAExB;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,CAAC,MAAM,aAAa,GAAa;IACrC,IAAI,EAAE,OAAO;IACb,MAAM,CAAC,EACL,KAAK,EACL,MAAM,EACN,UAAU,EACV,OAAO,EACP,IAAI,EACJ,MAAM,GACU;QAChB,MAAM,EAAE,UAAU,EAAE,GAAG,MAAM,CAAC;QAC9B,MAAM,IAAI,GAAG,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC,QAAQ,CAAC,CAAC;QACnD,MAAM,KAAK,GAAG,UAAU,CAAC,UAAU,EAAE,MAAM,EAAE,MAAM,EAAE;YACnD,GAAG,EAAE,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,GAAG,QAAQ,CAAC;SACxC,CAAC,CAAC;QACH,MAAM,IAAI,GAAG,WAAW,CAAC,KAAK,CAAC,CAAC;QAEhC,MAAM,IAAI,GAAG,SAAS,CAAC,IAAI,EAAE,IAAI,CAAC,MAAM,CAAC,CAAC;QAC1C,MAAM,KAAK,GAAG,UAAU,CAAC,KAAK,EAAE;YAC9B,OAAO;YACP,UAAU;YACV,YAAY,EAAE,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC,GAAG;YAChD,aAAa,EAAE,IAAI,CAAC,MAAM;SAC3B,CAAC,CAAC;QAEH,MAAM,MAAM,GAAG,MAAM,CAAC,IAAI,EAAE,IAAI,EAAE,UAAU,CAAC,KAAK,EAAE,OAAO,EAAE,UAAU,CAAC,CAAC,CAAC;QAE1E,MAAM,IAAI,GAAG,SAAS,CAAC,KAAK,EAAE,MAAM,CAAC,IAAI,EAAE;YACzC,UAAU;YACV,IAAI,EAAE,MAAM,CAAC,MAAM,CAAC,UAAU;YAC9B,UAAU;YACV,MAAM,EAAE,OAAO;SAChB,CAAC,CAAC;QAEH,OAAO,CACL,WAAW,CAAC,IAAI,EAAE,MAAM,CAAC,IAAI,CAAC;YAC9B,IAAI;YACJ,wEAAwE;YACxE,+DAA+D;YAC/D,UAAU,CAAC,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,QAAQ,CAAC,CACrD,CAAC;IACJ,CAAC;CACF,CAAC"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { TextLine } from "../../layout/index.js";
|
|
2
|
+
import type { MeasureText, MetaImageProps } from "../../types.js";
|
|
3
|
+
/** The space the cover's text has, and how to measure it into it. */
|
|
4
|
+
export interface CoverText {
|
|
5
|
+
readonly measure: MeasureText;
|
|
6
|
+
readonly fontFamily: string;
|
|
7
|
+
readonly contentWidth: number;
|
|
8
|
+
/**
|
|
9
|
+
* The height the text is sized against, which is the content area's rather
|
|
10
|
+
* than the image's.
|
|
11
|
+
*
|
|
12
|
+
* Every other template takes its sizes from the image, and on a cover that
|
|
13
|
+
* is the wrong number by a wide margin: a YouTube banner is 1440 tall and
|
|
14
|
+
* the band anyone reads is 423 of it, so a title at a tenth of the image
|
|
15
|
+
* would be a third of the height of the part that is seen.
|
|
16
|
+
*/
|
|
17
|
+
readonly contentHeight: number;
|
|
18
|
+
}
|
|
19
|
+
/** The name over its tagline, each fitted to the room the lockup has. */
|
|
20
|
+
export declare function coverLines(props: MetaImageProps, text: CoverText): TextLine[];
|
|
21
|
+
//# sourceMappingURL=lines.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lines.d.ts","sourceRoot":"","sources":["../../../src/templates/cover/lines.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AAEtD,OAAO,KAAK,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAgClE,qEAAqE;AACrE,MAAM,WAAW,SAAS;IACxB,QAAQ,CAAC,OAAO,EAAE,WAAW,CAAC;IAC9B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B;;;;;;;;OAQG;IACH,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;CAChC;AA4CD,yEAAyE;AACzE,wBAAgB,UAAU,CAAC,KAAK,EAAE,cAAc,EAAE,IAAI,EAAE,SAAS,GAAG,QAAQ,EAAE,CAqB7E"}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { blockLines, clampLine, measureIn } from "../../layout/index.js";
|
|
2
|
+
import { trackTagline } from "./tracking.js";
|
|
3
|
+
/**
|
|
4
|
+
* One line for the name, for the reason the `wordmark` has one: a name broken
|
|
5
|
+
* across two lines stops reading as a name. On a strip it is also the only
|
|
6
|
+
* arrangement that fits, since a second line of a title set to fill the band
|
|
7
|
+
* would be a second band.
|
|
8
|
+
*/
|
|
9
|
+
const maxNameLines = 1;
|
|
10
|
+
const maxTaglineLines = 2;
|
|
11
|
+
/** The weights the two blocks are drawn at, and so measured at. */
|
|
12
|
+
const nameWeight = 800;
|
|
13
|
+
const taglineWeight = 500;
|
|
14
|
+
const nameFloor = 0.5;
|
|
15
|
+
const taglineFloor = 0.7;
|
|
16
|
+
/**
|
|
17
|
+
* The name's size, as a fraction of the room it has. It gives up a third of
|
|
18
|
+
* that where there is a tagline under it, which is what leaves the two
|
|
19
|
+
* reading as a pair rather than as one line with a caption squeezed beneath.
|
|
20
|
+
*/
|
|
21
|
+
const nameScale = 0.48;
|
|
22
|
+
const nameWithTaglineScale = 0.3;
|
|
23
|
+
/** The tagline's size, and the clear space above it, both of the same room. */
|
|
24
|
+
const taglineScale = 0.15;
|
|
25
|
+
const taglineGapScale = 0.09;
|
|
26
|
+
/**
|
|
27
|
+
* The name as one line: shrunk until it fits, and cut with an ellipsis where
|
|
28
|
+
* even the floor is not small enough.
|
|
29
|
+
*
|
|
30
|
+
* Wrapping is what the fitting does at that point, and on a strip there is no
|
|
31
|
+
* second line to wrap onto. This is `wordmark`'s treatment of the same string,
|
|
32
|
+
* and it is here rather than shared because the two differ in the fitting
|
|
33
|
+
* around it rather than in this.
|
|
34
|
+
*/
|
|
35
|
+
function nameLines(props, text, fontSize) {
|
|
36
|
+
const { measure, fontFamily, contentWidth } = text;
|
|
37
|
+
const name = props.title;
|
|
38
|
+
const [line] = blockLines(name, measure, fontFamily, {
|
|
39
|
+
maxWidth: contentWidth,
|
|
40
|
+
maxLines: maxNameLines,
|
|
41
|
+
fontSize,
|
|
42
|
+
floor: nameFloor,
|
|
43
|
+
fontWeight: nameWeight,
|
|
44
|
+
opacity: 1,
|
|
45
|
+
});
|
|
46
|
+
if (line === undefined || line.text === name) {
|
|
47
|
+
return line === undefined ? [] : [line];
|
|
48
|
+
}
|
|
49
|
+
return [
|
|
50
|
+
{
|
|
51
|
+
...line,
|
|
52
|
+
text: clampLine(line.text, contentWidth, measureIn(measure, fontFamily, nameWeight), line.fontSize),
|
|
53
|
+
},
|
|
54
|
+
];
|
|
55
|
+
}
|
|
56
|
+
/** The name over its tagline, each fitted to the room the lockup has. */
|
|
57
|
+
export function coverLines(props, text) {
|
|
58
|
+
const { measure, fontFamily, contentWidth, contentHeight } = text;
|
|
59
|
+
const tagline = blockLines(props.subtitle, measure, fontFamily, {
|
|
60
|
+
maxWidth: contentWidth,
|
|
61
|
+
maxLines: maxTaglineLines,
|
|
62
|
+
fontSize: Math.round(contentHeight * taglineScale),
|
|
63
|
+
floor: taglineFloor,
|
|
64
|
+
fontWeight: taglineWeight,
|
|
65
|
+
opacity: 0.85,
|
|
66
|
+
gapBefore: Math.round(contentHeight * taglineGapScale),
|
|
67
|
+
});
|
|
68
|
+
const scale = tagline.length === 0 ? nameScale : nameWithTaglineScale;
|
|
69
|
+
const name = nameLines(props, text, Math.round(contentHeight * scale));
|
|
70
|
+
return [
|
|
71
|
+
...name,
|
|
72
|
+
...(props["tracking"] === "fill"
|
|
73
|
+
? trackTagline(name, tagline, measure, fontFamily)
|
|
74
|
+
: tagline),
|
|
75
|
+
];
|
|
76
|
+
}
|
|
77
|
+
//# sourceMappingURL=lines.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lines.js","sourceRoot":"","sources":["../../../src/templates/cover/lines.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAEzE,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAE7C;;;;;GAKG;AACH,MAAM,YAAY,GAAG,CAAC,CAAC;AAEvB,MAAM,eAAe,GAAG,CAAC,CAAC;AAE1B,mEAAmE;AACnE,MAAM,UAAU,GAAG,GAAG,CAAC;AACvB,MAAM,aAAa,GAAG,GAAG,CAAC;AAE1B,MAAM,SAAS,GAAG,GAAG,CAAC;AACtB,MAAM,YAAY,GAAG,GAAG,CAAC;AAEzB;;;;GAIG;AACH,MAAM,SAAS,GAAG,IAAI,CAAC;AACvB,MAAM,oBAAoB,GAAG,GAAG,CAAC;AAEjC,+EAA+E;AAC/E,MAAM,YAAY,GAAG,IAAI,CAAC;AAC1B,MAAM,eAAe,GAAG,IAAI,CAAC;AAmB7B;;;;;;;;GAQG;AACH,SAAS,SAAS,CAChB,KAAqB,EACrB,IAAe,EACf,QAAgB;IAEhB,MAAM,EAAE,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,GAAG,IAAI,CAAC;IACnD,MAAM,IAAI,GAAG,KAAK,CAAC,KAAK,CAAC;IACzB,MAAM,CAAC,IAAI,CAAC,GAAG,UAAU,CAAC,IAAI,EAAE,OAAO,EAAE,UAAU,EAAE;QACnD,QAAQ,EAAE,YAAY;QACtB,QAAQ,EAAE,YAAY;QACtB,QAAQ;QACR,KAAK,EAAE,SAAS;QAChB,UAAU,EAAE,UAAU;QACtB,OAAO,EAAE,CAAC;KACX,CAAC,CAAC;IAEH,IAAI,IAAI,KAAK,SAAS,IAAI,IAAI,CAAC,IAAI,KAAK,IAAI,EAAE,CAAC;QAC7C,OAAO,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;IAC1C,CAAC;IAED,OAAO;QACL;YACE,GAAG,IAAI;YACP,IAAI,EAAE,SAAS,CACb,IAAI,CAAC,IAAI,EACT,YAAY,EACZ,SAAS,CAAC,OAAO,EAAE,UAAU,EAAE,UAAU,CAAC,EAC1C,IAAI,CAAC,QAAQ,CACd;SACF;KACF,CAAC;AACJ,CAAC;AAED,yEAAyE;AACzE,MAAM,UAAU,UAAU,CAAC,KAAqB,EAAE,IAAe;IAC/D,MAAM,EAAE,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,aAAa,EAAE,GAAG,IAAI,CAAC;IAClE,MAAM,OAAO,GAAG,UAAU,CAAC,KAAK,CAAC,QAAQ,EAAE,OAAO,EAAE,UAAU,EAAE;QAC9D,QAAQ,EAAE,YAAY;QACtB,QAAQ,EAAE,eAAe;QACzB,QAAQ,EAAE,IAAI,CAAC,KAAK,CAAC,aAAa,GAAG,YAAY,CAAC;QAClD,KAAK,EAAE,YAAY;QACnB,UAAU,EAAE,aAAa;QACzB,OAAO,EAAE,IAAI;QACb,SAAS,EAAE,IAAI,CAAC,KAAK,CAAC,aAAa,GAAG,eAAe,CAAC;KACvD,CAAC,CAAC;IAEH,MAAM,KAAK,GAAG,OAAO,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,oBAAoB,CAAC;IACtE,MAAM,IAAI,GAAG,SAAS,CAAC,KAAK,EAAE,IAAI,EAAE,IAAI,CAAC,KAAK,CAAC,aAAa,GAAG,KAAK,CAAC,CAAC,CAAC;IAEvE,OAAO;QACL,GAAG,IAAI;QACP,GAAG,CAAC,KAAK,CAAC,UAAU,CAAC,KAAK,MAAM;YAC9B,CAAC,CAAC,YAAY,CAAC,IAAI,EAAE,OAAO,EAAE,OAAO,EAAE,UAAU,CAAC;YAClD,CAAC,CAAC,OAAO,CAAC;KACb,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import type { Rect, TextLine } from "../../layout/index.js";
|
|
2
|
+
import type { ImageAsset, MeasureText } from "../../types.js";
|
|
3
|
+
/**
|
|
4
|
+
* The widest of a block of lines, which is how wide the block is.
|
|
5
|
+
*
|
|
6
|
+
* Tracking counts towards it, because a tracked line is drawn wider than it
|
|
7
|
+
* measures. Leaving it out would centre the lockup on the width the words
|
|
8
|
+
* would have had, which is the one place a stretched line would push past its
|
|
9
|
+
* own margin.
|
|
10
|
+
*/
|
|
11
|
+
export declare function linesWidth(lines: readonly TextLine[], measure: MeasureText, fontFamily: string): number;
|
|
12
|
+
/** The room the mark takes along the row, including the gap after it. */
|
|
13
|
+
export interface Mark {
|
|
14
|
+
readonly width: number;
|
|
15
|
+
readonly gap: number;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* How much of the row the mark claims, or nothing at all where the project has
|
|
19
|
+
* no logo, which is what takes the gap with it.
|
|
20
|
+
*
|
|
21
|
+
* The width and the gap come back together because the words need both before
|
|
22
|
+
* they can be fitted, and deriving either of them twice is how the room
|
|
23
|
+
* reserved stops matching the room used.
|
|
24
|
+
*/
|
|
25
|
+
export declare function markSpace(logo: ImageAsset | undefined, contentHeight: number): Mark;
|
|
26
|
+
/** Where the mark and the words go: side by side, centred in the area. */
|
|
27
|
+
export interface Lockup {
|
|
28
|
+
readonly mark: Rect | undefined;
|
|
29
|
+
readonly text: Rect;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Set the mark beside the words and centre the pair.
|
|
33
|
+
*
|
|
34
|
+
* A cover is a strip between about 2.6:1 and 6:1, so the mark goes next to the
|
|
35
|
+
* name rather than above it as the `wordmark` template has it: stacked, three
|
|
36
|
+
* elements in 400px of height leave nothing legible. The words are then set
|
|
37
|
+
* from the left of the slot they were given rather than centred within it, so
|
|
38
|
+
* that a wrapped name lines up under itself and the whole lockup still sits in
|
|
39
|
+
* the middle.
|
|
40
|
+
*
|
|
41
|
+
* A project with no logo takes the same path with a mark of no width, which is
|
|
42
|
+
* one arrangement rather than two: `row` centres a single item exactly as it
|
|
43
|
+
* centres a pair.
|
|
44
|
+
*/
|
|
45
|
+
export declare function lockup(area: Rect, mark: Mark, textWidth: number): Lockup;
|
|
46
|
+
/**
|
|
47
|
+
* The mark, centred down the slot it was given.
|
|
48
|
+
*
|
|
49
|
+
* Its height comes from the room the lockup has rather than from the slot,
|
|
50
|
+
* which is the whole content area tall: `row` gives every item the full height
|
|
51
|
+
* so that each can sit where it likes within it.
|
|
52
|
+
*/
|
|
53
|
+
export declare function markElement(logo: ImageAsset | undefined, slot: Rect | undefined): string;
|
|
54
|
+
//# sourceMappingURL=lockup.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lockup.d.ts","sourceRoot":"","sources":["../../../src/templates/cover/lockup.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AAE5D,OAAO,KAAK,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAQ9D;;;;;;;GAOG;AACH,wBAAgB,UAAU,CACxB,KAAK,EAAE,SAAS,QAAQ,EAAE,EAC1B,OAAO,EAAE,WAAW,EACpB,UAAU,EAAE,MAAM,GACjB,MAAM,CAiBR;AAED,yEAAyE;AACzE,MAAM,WAAW,IAAI;IACnB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;CACtB;AAED;;;;;;;GAOG;AACH,wBAAgB,SAAS,CACvB,IAAI,EAAE,UAAU,GAAG,SAAS,EAC5B,aAAa,EAAE,MAAM,GACpB,IAAI,CASN;AAED,0EAA0E;AAC1E,MAAM,WAAW,MAAM;IACrB,QAAQ,CAAC,IAAI,EAAE,IAAI,GAAG,SAAS,CAAC;IAChC,QAAQ,CAAC,IAAI,EAAE,IAAI,CAAC;CACrB;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,MAAM,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,CAUxE;AAED;;;;;;GAMG;AACH,wBAAgB,WAAW,CACzB,IAAI,EAAE,UAAU,GAAG,SAAS,EAC5B,IAAI,EAAE,IAAI,GAAG,SAAS,GACrB,MAAM,CAiBR"}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { image, row, trackedWidth } from "../../layout/index.js";
|
|
2
|
+
/** The mark's height, as a fraction of the room the lockup has. */
|
|
3
|
+
const markScale = 0.72;
|
|
4
|
+
/** The clear space between the mark and the words, of the same room. */
|
|
5
|
+
const markGapScale = 0.24;
|
|
6
|
+
/**
|
|
7
|
+
* The widest of a block of lines, which is how wide the block is.
|
|
8
|
+
*
|
|
9
|
+
* Tracking counts towards it, because a tracked line is drawn wider than it
|
|
10
|
+
* measures. Leaving it out would centre the lockup on the width the words
|
|
11
|
+
* would have had, which is the one place a stretched line would push past its
|
|
12
|
+
* own margin.
|
|
13
|
+
*/
|
|
14
|
+
export function linesWidth(lines, measure, fontFamily) {
|
|
15
|
+
return lines.reduce((widest, line) => Math.max(widest, trackedWidth(line.text, measure(line.text, {
|
|
16
|
+
fontFamily,
|
|
17
|
+
fontSize: line.fontSize,
|
|
18
|
+
fontWeight: line.fontWeight,
|
|
19
|
+
}), line.letterSpacing ?? 0)), 0);
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* How much of the row the mark claims, or nothing at all where the project has
|
|
23
|
+
* no logo, which is what takes the gap with it.
|
|
24
|
+
*
|
|
25
|
+
* The width and the gap come back together because the words need both before
|
|
26
|
+
* they can be fitted, and deriving either of them twice is how the room
|
|
27
|
+
* reserved stops matching the room used.
|
|
28
|
+
*/
|
|
29
|
+
export function markSpace(logo, contentHeight) {
|
|
30
|
+
if (logo === undefined) {
|
|
31
|
+
return { width: 0, gap: 0 };
|
|
32
|
+
}
|
|
33
|
+
return {
|
|
34
|
+
width: Math.round(Math.round(contentHeight * markScale) * logo.aspect),
|
|
35
|
+
gap: Math.round(contentHeight * markGapScale),
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Set the mark beside the words and centre the pair.
|
|
40
|
+
*
|
|
41
|
+
* A cover is a strip between about 2.6:1 and 6:1, so the mark goes next to the
|
|
42
|
+
* name rather than above it as the `wordmark` template has it: stacked, three
|
|
43
|
+
* elements in 400px of height leave nothing legible. The words are then set
|
|
44
|
+
* from the left of the slot they were given rather than centred within it, so
|
|
45
|
+
* that a wrapped name lines up under itself and the whole lockup still sits in
|
|
46
|
+
* the middle.
|
|
47
|
+
*
|
|
48
|
+
* A project with no logo takes the same path with a mark of no width, which is
|
|
49
|
+
* one arrangement rather than two: `row` centres a single item exactly as it
|
|
50
|
+
* centres a pair.
|
|
51
|
+
*/
|
|
52
|
+
export function lockup(area, mark, textWidth) {
|
|
53
|
+
const [markSlot, textSlot] = row([{ size: mark.width }, { size: textWidth, gapBefore: mark.gap }], area);
|
|
54
|
+
return {
|
|
55
|
+
mark: mark.width === 0 ? undefined : markSlot,
|
|
56
|
+
text: textSlot ?? area,
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* The mark, centred down the slot it was given.
|
|
61
|
+
*
|
|
62
|
+
* Its height comes from the room the lockup has rather than from the slot,
|
|
63
|
+
* which is the whole content area tall: `row` gives every item the full height
|
|
64
|
+
* so that each can sit where it likes within it.
|
|
65
|
+
*/
|
|
66
|
+
export function markElement(logo, slot) {
|
|
67
|
+
if (logo === undefined || slot === undefined) {
|
|
68
|
+
return "";
|
|
69
|
+
}
|
|
70
|
+
const height = Math.round(slot.height * markScale);
|
|
71
|
+
return image({
|
|
72
|
+
x: slot.x,
|
|
73
|
+
y: Math.round(slot.y + (slot.height - height) / 2),
|
|
74
|
+
width: slot.width,
|
|
75
|
+
height,
|
|
76
|
+
}, logo.href, { fit: "contain" });
|
|
77
|
+
}
|
|
78
|
+
//# sourceMappingURL=lockup.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lockup.js","sourceRoot":"","sources":["../../../src/templates/cover/lockup.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,EAAE,GAAG,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAGjE,mEAAmE;AACnE,MAAM,SAAS,GAAG,IAAI,CAAC;AAEvB,wEAAwE;AACxE,MAAM,YAAY,GAAG,IAAI,CAAC;AAE1B;;;;;;;GAOG;AACH,MAAM,UAAU,UAAU,CACxB,KAA0B,EAC1B,OAAoB,EACpB,UAAkB;IAElB,OAAO,KAAK,CAAC,MAAM,CACjB,CAAC,MAAM,EAAE,IAAI,EAAE,EAAE,CACf,IAAI,CAAC,GAAG,CACN,MAAM,EACN,YAAY,CACV,IAAI,CAAC,IAAI,EACT,OAAO,CAAC,IAAI,CAAC,IAAI,EAAE;QACjB,UAAU;QACV,QAAQ,EAAE,IAAI,CAAC,QAAQ;QACvB,UAAU,EAAE,IAAI,CAAC,UAAU;KAC5B,CAAC,EACF,IAAI,CAAC,aAAa,IAAI,CAAC,CACxB,CACF,EACH,CAAC,CACF,CAAC;AACJ,CAAC;AAQD;;;;;;;GAOG;AACH,MAAM,UAAU,SAAS,CACvB,IAA4B,EAC5B,aAAqB;IAErB,IAAI,IAAI,KAAK,SAAS,EAAE,CAAC;QACvB,OAAO,EAAE,KAAK,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,CAAC;IAC9B,CAAC;IAED,OAAO;QACL,KAAK,EAAE,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,aAAa,GAAG,SAAS,CAAC,GAAG,IAAI,CAAC,MAAM,CAAC;QACtE,GAAG,EAAE,IAAI,CAAC,KAAK,CAAC,aAAa,GAAG,YAAY,CAAC;KAC9C,CAAC;AACJ,CAAC;AAQD;;;;;;;;;;;;;GAaG;AACH,MAAM,UAAU,MAAM,CAAC,IAAU,EAAE,IAAU,EAAE,SAAiB;IAC9D,MAAM,CAAC,QAAQ,EAAE,QAAQ,CAAC,GAAG,GAAG,CAC9B,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,KAAK,EAAE,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE,CAAC,EAChE,IAAI,CACL,CAAC;IAEF,OAAO;QACL,IAAI,EAAE,IAAI,CAAC,KAAK,KAAK,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,QAAQ;QAC7C,IAAI,EAAE,QAAQ,IAAI,IAAI;KACvB,CAAC;AACJ,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,WAAW,CACzB,IAA4B,EAC5B,IAAsB;IAEtB,IAAI,IAAI,KAAK,SAAS,IAAI,IAAI,KAAK,SAAS,EAAE,CAAC;QAC7C,OAAO,EAAE,CAAC;IACZ,CAAC;IAED,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,GAAG,SAAS,CAAC,CAAC;IAEnD,OAAO,KAAK,CACV;QACE,CAAC,EAAE,IAAI,CAAC,CAAC;QACT,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC;QAClD,KAAK,EAAE,IAAI,CAAC,KAAK;QACjB,MAAM;KACP,EACD,IAAI,CAAC,IAAI,EACT,EAAE,GAAG,EAAE,SAAS,EAAE,CACnB,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { TextLine } from "../../layout/index.js";
|
|
2
|
+
import type { MeasureText } from "../../types.js";
|
|
3
|
+
/**
|
|
4
|
+
* Stretch a single line of tagline out to the width of the name above it.
|
|
5
|
+
*
|
|
6
|
+
* This is the adjustment somebody makes by hand in a drawing program and then
|
|
7
|
+
* has to make again every time the words change: the wordmark and the line
|
|
8
|
+
* under it share both edges, so the pair reads as one block rather than as two
|
|
9
|
+
* lines that happen to be centred together.
|
|
10
|
+
*
|
|
11
|
+
* Only a tagline of one line is tracked. Justifying every line of a wrapped
|
|
12
|
+
* one to the same width is a different thing to want, and it goes wrong on the
|
|
13
|
+
* short last line in the way justified text always does.
|
|
14
|
+
*/
|
|
15
|
+
export declare function trackTagline(name: readonly TextLine[], tagline: readonly TextLine[], measure: MeasureText, fontFamily: string): readonly TextLine[];
|
|
16
|
+
//# sourceMappingURL=tracking.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tracking.d.ts","sourceRoot":"","sources":["../../../src/templates/cover/tracking.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AAEtD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AA0BlD;;;;;;;;;;;GAWG;AACH,wBAAgB,YAAY,CAC1B,IAAI,EAAE,SAAS,QAAQ,EAAE,EACzB,OAAO,EAAE,SAAS,QAAQ,EAAE,EAC5B,OAAO,EAAE,WAAW,EACpB,UAAU,EAAE,MAAM,GACjB,SAAS,QAAQ,EAAE,CAmBrB"}
|