@dloizides/poster-render 0.1.0 → 0.2.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/CHANGELOG.md +14 -0
- package/README.md +2 -1
- package/dist/index.d.mts +22 -2
- package/dist/index.d.ts +22 -2
- package/dist/index.js +250 -48
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +249 -49
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -29,6 +29,11 @@ var LayoutId = /* @__PURE__ */ ((LayoutId2) => {
|
|
|
29
29
|
LayoutId2["PictureLeft"] = "picture-left";
|
|
30
30
|
LayoutId2["PictureBottom"] = "picture-bottom";
|
|
31
31
|
LayoutId2["List"] = "list";
|
|
32
|
+
LayoutId2["Arch"] = "arch";
|
|
33
|
+
LayoutId2["PhoneClockSafe"] = "phone-clock-safe";
|
|
34
|
+
LayoutId2["PhoneArch"] = "phone-arch";
|
|
35
|
+
LayoutId2["DesktopSafe"] = "desktop-safe";
|
|
36
|
+
LayoutId2["DesktopArch"] = "desktop-arch";
|
|
32
37
|
return LayoutId2;
|
|
33
38
|
})(LayoutId || {});
|
|
34
39
|
|
|
@@ -60,10 +65,10 @@ var FORMAT_SIZES = {
|
|
|
60
65
|
|
|
61
66
|
// src/palettes.ts
|
|
62
67
|
var PALETTES = {
|
|
63
|
-
["sand" /* Sand */]: { background: "#f5f4f2", tint: "#ece8e3", title: "#958a80", accent: "#e9b47f", ink: "#5f564f", soft: "#b3a89e", card: "#ffffff" },
|
|
64
|
-
["night" /* Night */]: { background: "#1c1a17", tint: "#25221e", title: "#e8dccb", accent: "#e9b47f", ink: "#f4efe8", soft: "#9c9284", card: "#2c2824" },
|
|
65
|
-
["sea" /* Sea */]: { background: "#eef4f3", tint: "#e1ecea", title: "#4f7d7a", accent: "#e07a5f", ink: "#2f3e46", soft: "#8aa3a1", card: "#ffffff" },
|
|
66
|
-
["rose" /* Rose */]: { background: "#f8efec", tint: "#f1e2dd", title: "#9b6b63", accent: "#c9846f", ink: "#4a3934", soft: "#b39a92", card: "#ffffff" }
|
|
68
|
+
["sand" /* Sand */]: { background: "#f5f4f2", tint: "#ece8e3", title: "#958a80", accent: "#e9b47f", ink: "#5f564f", soft: "#b3a89e", card: "#ffffff", arch: "#ede8e2" },
|
|
69
|
+
["night" /* Night */]: { background: "#1c1a17", tint: "#25221e", title: "#e8dccb", accent: "#e9b47f", ink: "#f4efe8", soft: "#9c9284", card: "#2c2824", arch: "#25221f" },
|
|
70
|
+
["sea" /* Sea */]: { background: "#eef4f3", tint: "#e1ecea", title: "#4f7d7a", accent: "#e07a5f", ink: "#2f3e46", soft: "#8aa3a1", card: "#ffffff", arch: "#e4ecea" },
|
|
71
|
+
["rose" /* Rose */]: { background: "#f8efec", tint: "#f1e2dd", title: "#9b6b63", accent: "#c9846f", ink: "#4a3934", soft: "#b39a92", card: "#ffffff", arch: "#f1e5e1" }
|
|
67
72
|
};
|
|
68
73
|
|
|
69
74
|
// src/fontSets.ts
|
|
@@ -307,18 +312,19 @@ function headerGroups(doc, p, u) {
|
|
|
307
312
|
return [{ lines: lines.filter((x) => x !== void 0), gapAfter: 0 }];
|
|
308
313
|
}
|
|
309
314
|
function itemGroup(item, l, u) {
|
|
310
|
-
const lines = [l.script?.(item.date,
|
|
315
|
+
const lines = [l.script?.(item.date, 67)];
|
|
311
316
|
item.schedule.forEach((row) => lines.push(l.strong?.(`${row.label} ${row.time}`, 42)));
|
|
312
317
|
if (item.price) {
|
|
313
318
|
lines.push(l.accent?.(item.price, 42));
|
|
314
319
|
}
|
|
315
|
-
return { lines, gapAfter:
|
|
320
|
+
return { lines, gapAfter: 48 * u };
|
|
316
321
|
}
|
|
317
|
-
function eventGroups(doc, l, u) {
|
|
322
|
+
function eventGroups(doc, l, u, withPrices) {
|
|
318
323
|
const b = doc.blocks;
|
|
319
324
|
const groups = [];
|
|
320
325
|
b.schedule?.forEach((row) => groups.push({ lines: [l.strong?.(row.label, 66), l.accent?.(row.time, 60)], gapAfter: 30 * u }));
|
|
321
|
-
|
|
326
|
+
const prices = (withPrices ? b.prices : void 0) ?? [];
|
|
327
|
+
prices.forEach((row, i) => groups.push({ lines: [l.strong?.(row.amount, 66), l.accent?.(row.label, 52)], gapAfter: 30 * u, keepWithNext: i < prices.length - 1 }));
|
|
322
328
|
return groups;
|
|
323
329
|
}
|
|
324
330
|
function listGroups(doc, l, u) {
|
|
@@ -329,18 +335,22 @@ function listGroups(doc, l, u) {
|
|
|
329
335
|
}
|
|
330
336
|
return groups;
|
|
331
337
|
}
|
|
332
|
-
function
|
|
338
|
+
function flankPriceGroups(doc, p, u) {
|
|
339
|
+
const l = makers(p, u);
|
|
340
|
+
return (doc.blocks.prices ?? []).map((row) => ({ lines: [l.strong?.(row.amount, 110), l.accent?.(row.label, 48)], gapAfter: 30 * u }));
|
|
341
|
+
}
|
|
342
|
+
function bodyGroups(doc, p, u, withPrices = true) {
|
|
333
343
|
const l = makers(p, u);
|
|
334
344
|
const b = doc.blocks;
|
|
335
345
|
const groups = [];
|
|
336
346
|
const isList = doc.layout === "list" /* List */;
|
|
337
347
|
if (b.venue) {
|
|
338
|
-
groups.push({ lines: b.venue.map((v) => l.venue?.(v, isList ? 48 : 58)), gapAfter: 40 * u, rule: { color: p.accent, height: 8 * u } });
|
|
348
|
+
groups.push({ lines: b.venue.map((v) => l.venue?.(v, isList ? 48 : 58)), gapAfter: 40 * u, rule: { color: p.accent, height: 8 * u }, icon: { color: p.accent, glyph: p.background } });
|
|
339
349
|
}
|
|
340
|
-
groups.push(...isList ? listGroups(doc, l, u) : eventGroups(doc, l, u));
|
|
350
|
+
groups.push(...isList ? listGroups(doc, l, u) : eventGroups(doc, l, u, withPrices));
|
|
341
351
|
if (b.includes) {
|
|
342
|
-
const lines = [l.accent?.(b.includes.heading,
|
|
343
|
-
groups.push({ lines, gapAfter: 24 * u,
|
|
352
|
+
const lines = [l.accent?.(b.includes.heading, 38), ...b.includes.items.map((i) => l.item?.(i, 38))];
|
|
353
|
+
groups.push({ lines, gapAfter: 24 * u, uniform: true });
|
|
344
354
|
}
|
|
345
355
|
if (b.footer) {
|
|
346
356
|
groups.push({ lines: [l.soft?.(b.footer, 34)], gapAfter: 0 });
|
|
@@ -348,6 +358,51 @@ function bodyGroups(doc, p, u) {
|
|
|
348
358
|
return groups;
|
|
349
359
|
}
|
|
350
360
|
|
|
361
|
+
// src/layout/layoutSpecs.ts
|
|
362
|
+
var flanksBeside = (pic, top, bottom2, m) => [
|
|
363
|
+
[m, top, pic[0] - 2 * m, bottom2 - top],
|
|
364
|
+
[pic[0] + pic[2] + m, top, 1 - pic[0] - pic[2] - 2 * m, bottom2 - top]
|
|
365
|
+
];
|
|
366
|
+
var ARCH_PIC = [0.22, 0.42, 0.56, 0.58];
|
|
367
|
+
var PHONE_ARCH_PIC = [0.2, 0.6, 0.6, 0.4];
|
|
368
|
+
var DESK_ARCH_PIC = [0.355, 0.07, 0.32, 0.93];
|
|
369
|
+
var ARCH_LANDSCAPE = {
|
|
370
|
+
header: [0.04, 0.06, 0.3, 0.5],
|
|
371
|
+
body: [0.04, 0.58, 0.3, 0.36],
|
|
372
|
+
picture: [0.36, 0.08, 0.3, 0.92],
|
|
373
|
+
alignX: 0.5,
|
|
374
|
+
flanks: [[0.68, 0.08, 0.28, 0.42], [0.68, 0.52, 0.28, 0.42]],
|
|
375
|
+
arch: 0.06
|
|
376
|
+
};
|
|
377
|
+
var LAYOUT_SPECS = {
|
|
378
|
+
["arch" /* Arch */]: {
|
|
379
|
+
portrait: { header: [0.045, 0.025, 0.91, 0.18], body: [0.045, 0.215, 0.91, 0.19], picture: ARCH_PIC, alignX: 0.5, flanks: flanksBeside(ARCH_PIC, 0.44, 0.97, 0.03), arch: 0.04 },
|
|
380
|
+
landscape: ARCH_LANDSCAPE
|
|
381
|
+
},
|
|
382
|
+
["phone-clock-safe" /* PhoneClockSafe */]: {
|
|
383
|
+
portrait: { header: [0.045, 0.28, 0.91, 0.2], body: [0.045, 0.5, 0.42, 0.44], picture: [0.48, 0.5, 0.52, 0.5], alignX: 1 },
|
|
384
|
+
landscape: { header: [0.04, 0.2, 0.52, 0.3], body: [0.04, 0.52, 0.52, 0.42], picture: [0.6, 0.12, 0.4, 0.88], alignX: 1 }
|
|
385
|
+
},
|
|
386
|
+
["phone-arch" /* PhoneArch */]: {
|
|
387
|
+
portrait: { header: [0.045, 0.3, 0.91, 0.13], body: [0.045, 0.44, 0.91, 0.14], picture: PHONE_ARCH_PIC, alignX: 0.5, flanks: flanksBeside(PHONE_ARCH_PIC, 0.63, 0.94, 0.03), arch: 0.08 },
|
|
388
|
+
landscape: { ...ARCH_LANDSCAPE, header: [0.04, 0.16, 0.3, 0.42], picture: [0.36, 0.16, 0.3, 0.84] }
|
|
389
|
+
},
|
|
390
|
+
["desktop-safe" /* DesktopSafe */]: {
|
|
391
|
+
portrait: { header: [0.08, 0.06, 0.84, 0.24], body: [0.08, 0.31, 0.46, 0.58], picture: [0.56, 0.36, 0.44, 0.64], alignX: 1 },
|
|
392
|
+
landscape: { header: [0.0625, 0.05, 0.58, 0.44], body: [0.0625, 0.5, 0.58, 0.38], picture: [0.67, 0.0972, 0.29, 0.9028], alignX: 1, logo: [0.4, 0.045, 0.09, 0.17] }
|
|
393
|
+
},
|
|
394
|
+
["desktop-arch" /* DesktopArch */]: {
|
|
395
|
+
portrait: { header: [0.08, 0.06, 0.84, 0.2], body: [0.08, 0.27, 0.84, 0.2], picture: [0.14, 0.48, 0.72, 0.52], alignX: 0.5, arch: 0.05 },
|
|
396
|
+
landscape: { header: [0.07, 0.3, 0.26, 0.45], body: [0.7, 0.25, 0.27, 0.65], picture: DESK_ARCH_PIC, alignX: 0.5, logo: [0.86, 0.03, 0.11, 0.18], arch: 0.05 }
|
|
397
|
+
}
|
|
398
|
+
};
|
|
399
|
+
var r = Math.round;
|
|
400
|
+
var toRect = (f, w, h) => {
|
|
401
|
+
const x = r(f[0] * w);
|
|
402
|
+
const y = r(f[1] * h);
|
|
403
|
+
return { x, y, w: r((f[0] + f[2]) * w) - x, h: r((f[1] + f[3]) * h) - y };
|
|
404
|
+
};
|
|
405
|
+
|
|
351
406
|
// src/layout/geometry.ts
|
|
352
407
|
var MARGIN = 0.045;
|
|
353
408
|
var HEADER_PORTRAIT = 0.3;
|
|
@@ -360,12 +415,12 @@ var PICTURE_SIDE = {
|
|
|
360
415
|
["list" /* List */]: [0.5, 0.36]
|
|
361
416
|
};
|
|
362
417
|
var ANCHOR_Y = { ["top" /* Top */]: 0, ["center" /* Center */]: 0.5, ["bottom" /* Bottom */]: 1 };
|
|
363
|
-
var
|
|
418
|
+
var r2 = Math.round;
|
|
364
419
|
function side(w, h, fraction) {
|
|
365
|
-
const m =
|
|
420
|
+
const m = r2(Math.min(w, h) * MARGIN);
|
|
366
421
|
if (h >= w) {
|
|
367
|
-
const split2 =
|
|
368
|
-
const top2 =
|
|
422
|
+
const split2 = r2(w * (1 - fraction[0]));
|
|
423
|
+
const top2 = r2(h * HEADER_PORTRAIT);
|
|
369
424
|
return {
|
|
370
425
|
header: { x: m, y: m, w: w - 2 * m, h: top2 - 2 * m },
|
|
371
426
|
picture: { x: split2, y: top2, w: w - split2, h: h - top2 },
|
|
@@ -373,19 +428,19 @@ function side(w, h, fraction) {
|
|
|
373
428
|
alignX: 1
|
|
374
429
|
};
|
|
375
430
|
}
|
|
376
|
-
const split =
|
|
377
|
-
const top =
|
|
431
|
+
const split = r2(w * (1 - fraction[1]));
|
|
432
|
+
const top = r2(h * HEADER_LANDSCAPE);
|
|
378
433
|
return {
|
|
379
434
|
header: { x: m, y: m, w: split - 2 * m, h: top - m },
|
|
380
435
|
picture: { x: split, y: 0, w: w - split, h },
|
|
381
|
-
body: { x: m, y: top +
|
|
436
|
+
body: { x: m, y: top + r2(m / 2), w: split - 2 * m, h: h - top - r2(m / 2) - m },
|
|
382
437
|
alignX: 1
|
|
383
438
|
};
|
|
384
439
|
}
|
|
385
440
|
function bottom(w, h) {
|
|
386
|
-
const m =
|
|
387
|
-
const top =
|
|
388
|
-
const pic =
|
|
441
|
+
const m = r2(Math.min(w, h) * MARGIN);
|
|
442
|
+
const top = r2(h * BOTTOM_HEADER);
|
|
443
|
+
const pic = r2(h * BOTTOM_PICTURE);
|
|
389
444
|
return {
|
|
390
445
|
header: { x: m, y: m, w: w - 2 * m, h: top - 2 * m },
|
|
391
446
|
body: { x: m, y: top, w: w - 2 * m, h: pic - top - m },
|
|
@@ -393,8 +448,23 @@ function bottom(w, h) {
|
|
|
393
448
|
alignX: 0.5
|
|
394
449
|
};
|
|
395
450
|
}
|
|
451
|
+
function fromSpec(spec, w, h) {
|
|
452
|
+
return {
|
|
453
|
+
header: toRect(spec.header, w, h),
|
|
454
|
+
body: toRect(spec.body, w, h),
|
|
455
|
+
picture: toRect(spec.picture, w, h),
|
|
456
|
+
alignX: spec.alignX,
|
|
457
|
+
flanks: spec.flanks?.map((f) => toRect(f, w, h)),
|
|
458
|
+
logo: spec.logo ? toRect(spec.logo, w, h) : void 0,
|
|
459
|
+
arch: spec.arch
|
|
460
|
+
};
|
|
461
|
+
}
|
|
396
462
|
var mirror = (rect, w) => ({ ...rect, x: w - rect.x - rect.w });
|
|
397
463
|
function geometry(layout, w, h) {
|
|
464
|
+
const oriented = LAYOUT_SPECS[layout];
|
|
465
|
+
if (oriented) {
|
|
466
|
+
return fromSpec(h >= w ? oriented.portrait : oriented.landscape, w, h);
|
|
467
|
+
}
|
|
398
468
|
if (layout === "picture-bottom" /* PictureBottom */) {
|
|
399
469
|
return bottom(w, h);
|
|
400
470
|
}
|
|
@@ -425,12 +495,32 @@ var RULE_WIDTH = 0.9;
|
|
|
425
495
|
var WIDE = 1.6;
|
|
426
496
|
var MIN_K = 0.02;
|
|
427
497
|
var STEPS = 22;
|
|
498
|
+
var ICON_SCALE = 1.6;
|
|
499
|
+
var ICON_GAP = 0.35;
|
|
428
500
|
var fontOf = (line, size, fonts) => `${line.weight} ${size}px ${fonts[line.role]}`;
|
|
429
|
-
var emptyFlow = () => ({ texts: [], cards: [], rules: [], bullets: [] });
|
|
501
|
+
var emptyFlow = () => ({ texts: [], cards: [], rules: [], bullets: [], icons: [] });
|
|
502
|
+
var iconSize = (group, k) => {
|
|
503
|
+
const first = group.lines[0];
|
|
504
|
+
return group.icon && first ? first.size * k * LINE_HEIGHT * ICON_SCALE : 0;
|
|
505
|
+
};
|
|
430
506
|
function lineSize(line, width, k, avail) {
|
|
431
507
|
const indent = line.bullet ? line.size * k * BULLET_INDENT : 0;
|
|
432
508
|
return Math.max(0, Math.min(line.size * k, line.size * (avail - indent) / Math.max(width, 1)));
|
|
433
509
|
}
|
|
510
|
+
function groupRatio(m, k, avail) {
|
|
511
|
+
if (!m.group.uniform) {
|
|
512
|
+
return null;
|
|
513
|
+
}
|
|
514
|
+
return Math.min(...m.group.lines.map((line, i) => lineSize(line, m.widths[i] ?? 1, k, avail) / line.size));
|
|
515
|
+
}
|
|
516
|
+
function placeIcon(group, d, x, textTop, textBottom, out) {
|
|
517
|
+
if (!group.icon || d <= 0) {
|
|
518
|
+
return textBottom;
|
|
519
|
+
}
|
|
520
|
+
const textH = textBottom - textTop;
|
|
521
|
+
out?.icons.push({ rect: { x, y: textTop + Math.max(0, (textH - d) / 2), w: d, h: d }, color: group.icon.color, glyph: group.icon.glyph });
|
|
522
|
+
return textTop + Math.max(textH, d);
|
|
523
|
+
}
|
|
434
524
|
function placeGroup(m, k, col, y0, out) {
|
|
435
525
|
const { group } = m;
|
|
436
526
|
let y = y0;
|
|
@@ -441,17 +531,22 @@ function placeGroup(m, k, col, y0, out) {
|
|
|
441
531
|
const pad = group.card ? group.card.pad * k : 0;
|
|
442
532
|
const top = y;
|
|
443
533
|
y += pad;
|
|
534
|
+
const d = iconSize(group, k);
|
|
535
|
+
const iconIndent = d > 0 ? d * (1 + ICON_GAP) : 0;
|
|
536
|
+
const avail = col.w - 2 * pad - iconIndent;
|
|
537
|
+
const ratio = groupRatio(m, k, avail);
|
|
444
538
|
group.lines.forEach((line, i) => {
|
|
445
|
-
const size = lineSize(line, m.widths[i] ?? 1, k,
|
|
446
|
-
const indent = line.bullet ? line.size * k * BULLET_INDENT : 0;
|
|
447
|
-
const x = col.x + pad + indent;
|
|
539
|
+
const size = ratio === null ? lineSize(line, m.widths[i] ?? 1, k, avail) : line.size * ratio;
|
|
540
|
+
const indent = (line.bullet ? line.size * k * BULLET_INDENT : 0) + iconIndent;
|
|
448
541
|
const w = (m.widths[i] ?? 0) * size / line.size;
|
|
542
|
+
const x = col.right ? col.x + col.w - pad - w : col.x + pad + indent;
|
|
449
543
|
out?.texts.push({ text: line.text, font: "", color: line.color, box: { x, y, w, h: size * LINE_HEIGHT } });
|
|
450
544
|
if (line.bullet && out) {
|
|
451
545
|
out.bullets.push({ x: col.x + pad + size * BULLET_RADIUS, y: y + size * LINE_HEIGHT / 2, r: size * BULLET_RADIUS, color: line.bullet });
|
|
452
546
|
}
|
|
453
547
|
y += size * LINE_HEIGHT;
|
|
454
548
|
});
|
|
549
|
+
y = placeIcon(group, d, col.x + pad, top + pad, y, out);
|
|
455
550
|
y += pad;
|
|
456
551
|
if (group.card) {
|
|
457
552
|
out?.cards.unshift({ rect: { x: col.x, y: top, w: col.w, h: y - top }, color: group.card.color });
|
|
@@ -465,6 +560,27 @@ function placeColumn(groups, k, col, out) {
|
|
|
465
560
|
}
|
|
466
561
|
return h;
|
|
467
562
|
}
|
|
563
|
+
var tallest = (heights, cut) => {
|
|
564
|
+
const left = heights.slice(0, cut).reduce((a, b) => a + b, 0);
|
|
565
|
+
const right = heights.slice(cut).reduce((a, b) => a + b, 0);
|
|
566
|
+
return Math.max(left, right);
|
|
567
|
+
};
|
|
568
|
+
function keepTogether(groups, heights, cut) {
|
|
569
|
+
const breaks = (c) => groups[c - 1]?.group.keepWithNext === true;
|
|
570
|
+
let back = cut;
|
|
571
|
+
while (back > 1 && breaks(back)) {
|
|
572
|
+
back--;
|
|
573
|
+
}
|
|
574
|
+
let fwd = cut;
|
|
575
|
+
while (fwd < groups.length - 1 && breaks(fwd)) {
|
|
576
|
+
fwd++;
|
|
577
|
+
}
|
|
578
|
+
const candidates = [back, fwd].filter((c) => !breaks(c));
|
|
579
|
+
if (candidates.length === 0) {
|
|
580
|
+
return cut;
|
|
581
|
+
}
|
|
582
|
+
return candidates.reduce((best, c) => tallest(heights, c) < tallest(heights, best) ? c : best);
|
|
583
|
+
}
|
|
468
584
|
function splitColumns(groups, zone) {
|
|
469
585
|
if (zone.w / Math.max(zone.h, 1) <= WIDE || groups.length < 2) {
|
|
470
586
|
return [{ col: zone, groups }];
|
|
@@ -479,7 +595,7 @@ function splitColumns(groups, zone) {
|
|
|
479
595
|
}
|
|
480
596
|
acc += h;
|
|
481
597
|
});
|
|
482
|
-
cut = Math.min(cut, groups.length - 1);
|
|
598
|
+
cut = keepTogether(groups, heights, Math.min(cut, groups.length - 1));
|
|
483
599
|
const gap = Math.round(zone.w * 0.04);
|
|
484
600
|
const w = (zone.w - gap) / 2;
|
|
485
601
|
return [
|
|
@@ -524,21 +640,48 @@ function flow(ctx, groups, zone, fonts) {
|
|
|
524
640
|
var BASE = 1080;
|
|
525
641
|
var LOGO_SHARE = 0.26;
|
|
526
642
|
var CARD_RADIUS = 0.035;
|
|
527
|
-
|
|
528
|
-
|
|
643
|
+
var CAP_TOP = 0.15;
|
|
644
|
+
var PIN_HEAD_Y = 0.08;
|
|
645
|
+
var PIN_HEAD_R = 0.19;
|
|
646
|
+
var PIN_TIP_Y = 0.28;
|
|
647
|
+
var PIN_HOLE = 0.42;
|
|
648
|
+
var ARC_START = 0.75;
|
|
649
|
+
var ARC_END = 2.25;
|
|
650
|
+
function drawPin(ctx, r3, color, glyph) {
|
|
651
|
+
const cx = r3.x + r3.w / 2;
|
|
652
|
+
const cy = r3.y + r3.h / 2;
|
|
653
|
+
const hy = cy - r3.w * PIN_HEAD_Y;
|
|
654
|
+
const rad = r3.w * PIN_HEAD_R;
|
|
655
|
+
ctx.fillStyle = color;
|
|
656
|
+
ctx.beginPath();
|
|
657
|
+
ctx.arc(cx, cy, r3.w / 2, 0, Math.PI * 2);
|
|
658
|
+
ctx.fill();
|
|
659
|
+
ctx.fillStyle = glyph;
|
|
660
|
+
ctx.beginPath();
|
|
661
|
+
ctx.moveTo(cx, cy + r3.w * PIN_TIP_Y);
|
|
662
|
+
ctx.arc(cx, hy, rad, Math.PI * ARC_START, Math.PI * ARC_END);
|
|
663
|
+
ctx.closePath();
|
|
664
|
+
ctx.fill();
|
|
665
|
+
ctx.fillStyle = color;
|
|
666
|
+
ctx.beginPath();
|
|
667
|
+
ctx.arc(cx, hy, rad * PIN_HOLE, 0, Math.PI * 2);
|
|
668
|
+
ctx.fill();
|
|
669
|
+
}
|
|
670
|
+
function roundRect(ctx, r3, radius) {
|
|
671
|
+
const rad = Math.min(radius, r3.w / 2, r3.h / 2);
|
|
529
672
|
ctx.beginPath();
|
|
530
|
-
ctx.moveTo(
|
|
531
|
-
ctx.arcTo(
|
|
532
|
-
ctx.arcTo(
|
|
533
|
-
ctx.arcTo(
|
|
534
|
-
ctx.arcTo(
|
|
673
|
+
ctx.moveTo(r3.x + rad, r3.y);
|
|
674
|
+
ctx.arcTo(r3.x + r3.w, r3.y, r3.x + r3.w, r3.y + r3.h, rad);
|
|
675
|
+
ctx.arcTo(r3.x + r3.w, r3.y + r3.h, r3.x, r3.y + r3.h, rad);
|
|
676
|
+
ctx.arcTo(r3.x, r3.y + r3.h, r3.x, r3.y, rad);
|
|
677
|
+
ctx.arcTo(r3.x, r3.y, r3.x + r3.w, r3.y, rad);
|
|
535
678
|
ctx.closePath();
|
|
536
679
|
ctx.fill();
|
|
537
680
|
}
|
|
538
681
|
function drawFlow(ctx, f, radius) {
|
|
539
|
-
f.rules.forEach((
|
|
540
|
-
ctx.fillStyle =
|
|
541
|
-
ctx.fillRect(
|
|
682
|
+
f.rules.forEach((r3) => {
|
|
683
|
+
ctx.fillStyle = r3.color;
|
|
684
|
+
ctx.fillRect(r3.rect.x, r3.rect.y, r3.rect.w, r3.rect.h);
|
|
542
685
|
});
|
|
543
686
|
f.cards.forEach((c) => {
|
|
544
687
|
ctx.fillStyle = c.color;
|
|
@@ -550,6 +693,7 @@ function drawFlow(ctx, f, radius) {
|
|
|
550
693
|
ctx.arc(b.x, b.y, b.r, 0, Math.PI * 2);
|
|
551
694
|
ctx.fill();
|
|
552
695
|
});
|
|
696
|
+
f.icons.forEach((i) => drawPin(ctx, i.rect, i.color, i.glyph));
|
|
553
697
|
ctx.textAlign = "left";
|
|
554
698
|
ctx.textBaseline = "top";
|
|
555
699
|
f.texts.forEach((t) => {
|
|
@@ -569,6 +713,30 @@ function checked(input, assets) {
|
|
|
569
713
|
}
|
|
570
714
|
return result.doc;
|
|
571
715
|
}
|
|
716
|
+
function archRect(geo) {
|
|
717
|
+
if (geo.arch === void 0) {
|
|
718
|
+
return null;
|
|
719
|
+
}
|
|
720
|
+
const top = geo.picture.y + Math.round(geo.picture.h * geo.arch);
|
|
721
|
+
return { x: geo.picture.x, y: top, w: geo.picture.w, h: geo.picture.y + geo.picture.h - top };
|
|
722
|
+
}
|
|
723
|
+
function drawArch(ctx, r3, color) {
|
|
724
|
+
const rad = r3.w / 2;
|
|
725
|
+
ctx.fillStyle = color;
|
|
726
|
+
ctx.fillRect(r3.x, r3.y + rad, r3.w, r3.h - rad);
|
|
727
|
+
ctx.beginPath();
|
|
728
|
+
ctx.arc(r3.x + rad, r3.y + rad, rad, Math.PI, Math.PI * 2);
|
|
729
|
+
ctx.fill();
|
|
730
|
+
}
|
|
731
|
+
function flankFlows(ctx, groups, flanks, fonts) {
|
|
732
|
+
const half = Math.ceil(groups.length / 2);
|
|
733
|
+
return flanks.map((zone, i) => i === 0 ? flow(ctx, groups.slice(0, half), zone, fonts) : flow(ctx, groups.slice(half), { ...zone, right: true }, fonts));
|
|
734
|
+
}
|
|
735
|
+
function capAligned(zone, head) {
|
|
736
|
+
const first = head.texts[0]?.box;
|
|
737
|
+
const shift = first ? Math.min(Math.max(0, Math.round(first.y + first.h * CAP_TOP) - zone.y), Math.round(zone.h / 2)) : 0;
|
|
738
|
+
return { ...zone, y: zone.y + shift, h: zone.h - shift };
|
|
739
|
+
}
|
|
572
740
|
function splitHeader(header, hasLogo) {
|
|
573
741
|
const logoW = hasLogo ? Math.round(header.w * LOGO_SHARE) : 0;
|
|
574
742
|
return { text: { ...header, w: header.w - logoW * 1.1 }, logo: { ...header, x: header.x + header.w - logoW, w: logoW } };
|
|
@@ -591,20 +759,25 @@ function render(input, assets, canvas) {
|
|
|
591
759
|
ctx.fillRect(0, 0, width, height);
|
|
592
760
|
if (doc.background === "tint" /* Tint */) {
|
|
593
761
|
ctx.fillStyle = palette.tint;
|
|
594
|
-
[geo.header, geo.body].forEach((
|
|
762
|
+
[geo.header, geo.body].forEach((r3) => ctx.fillRect(r3.x, r3.y, r3.w, r3.h));
|
|
595
763
|
}
|
|
596
|
-
const zones = splitHeader(geo.header, doc.logo !== void 0);
|
|
764
|
+
const zones = geo.logo ? { text: geo.header, logo: geo.logo } : splitHeader(geo.header, doc.logo !== void 0);
|
|
597
765
|
const head = flow(ctx, headerGroups(doc, palette, u), zones.text, fonts);
|
|
598
|
-
const
|
|
766
|
+
const flanks = geo.flanks ?? [];
|
|
767
|
+
const body = flow(ctx, bodyGroups(doc, palette, u, flanks.length === 0), geo.body, fonts);
|
|
768
|
+
const sides = flanks.length > 0 ? flankFlows(ctx, flankPriceGroups(doc, palette, u), flanks, fonts) : [emptyFlow()];
|
|
599
769
|
const radius = Math.min(width, height) * CARD_RADIUS;
|
|
600
|
-
drawFlow(ctx,
|
|
601
|
-
drawFlow(ctx, body, radius);
|
|
770
|
+
[head, body, ...sides].forEach((f) => drawFlow(ctx, f, radius));
|
|
602
771
|
const logoImage = doc.logo ? assets[doc.logo.imageId] : void 0;
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
ctx.drawImage(logoImage,
|
|
772
|
+
const logo = logoImage ? placeContain(logoImage, geo.logo ? zones.logo : capAligned(zones.logo, head), "top" /* Top */, 1, 1) : null;
|
|
773
|
+
if (logoImage && logo) {
|
|
774
|
+
ctx.drawImage(logoImage, logo.x, logo.y, logo.w, logo.h);
|
|
606
775
|
}
|
|
607
776
|
const image = doc.picture ? assets[doc.picture.imageId] : void 0;
|
|
777
|
+
const backdrop = image ? archRect(geo) : null;
|
|
778
|
+
if (backdrop) {
|
|
779
|
+
drawArch(ctx, backdrop, palette.arch);
|
|
780
|
+
}
|
|
608
781
|
const picture = image && doc.picture ? placeContain(image, geo.picture, doc.picture.anchor, geo.alignX, doc.picture.scale) : null;
|
|
609
782
|
if (image && picture) {
|
|
610
783
|
ctx.drawImage(image, picture.x, picture.y, picture.w, picture.h);
|
|
@@ -614,8 +787,11 @@ function render(input, assets, canvas) {
|
|
|
614
787
|
height,
|
|
615
788
|
picture,
|
|
616
789
|
pictureRegion: geo.picture,
|
|
617
|
-
textBoxes: [
|
|
618
|
-
cardBoxes: body.cards.map((c) => c.rect)
|
|
790
|
+
textBoxes: [head, body, ...sides].flatMap((f) => f.texts.map((t) => t.box)),
|
|
791
|
+
cardBoxes: body.cards.map((c) => c.rect),
|
|
792
|
+
iconBoxes: [head, body, ...sides].flatMap((f) => f.icons.map((i) => i.rect)),
|
|
793
|
+
logo,
|
|
794
|
+
backdrop
|
|
619
795
|
};
|
|
620
796
|
}
|
|
621
797
|
|
|
@@ -634,6 +810,30 @@ function exportPng(canvas) {
|
|
|
634
810
|
return new Blob([bytes], { type: "image/png" });
|
|
635
811
|
}
|
|
636
812
|
|
|
813
|
+
// src/layoutCatalogue.ts
|
|
814
|
+
var POSTERS = ["story" /* Story */, "post" /* Post */, "square" /* Square */];
|
|
815
|
+
var LAYOUT_CATALOGUE = [
|
|
816
|
+
{ id: "picture-right" /* PictureRight */, designId: "E-A", label: "Picture right", description: "Titles across the top, details on the left, the picture on the right standing on the bottom edge.", formats: POSTERS },
|
|
817
|
+
{ id: "picture-left" /* PictureLeft */, designId: "E-B", label: "Picture left", description: "Titles across the top, the picture on the left standing on the bottom edge, details on the right.", formats: POSTERS },
|
|
818
|
+
{ id: "arch" /* Arch */, designId: "E-C", label: "Arch", description: "Titles and details on top, the picture centred in a soft arch at the bottom, prices either side.", formats: POSTERS },
|
|
819
|
+
{ id: "picture-bottom" /* PictureBottom */, designId: "picture-bottom", label: "Picture bottom", description: "Titles and details on top, the picture across the bottom half.", formats: POSTERS },
|
|
820
|
+
{ id: "list" /* List */, designId: "list", label: "Upcoming list", description: "Several dated events as a list beside the picture.", formats: POSTERS },
|
|
821
|
+
{ id: "phone-clock-safe" /* PhoneClockSafe */, designId: "P-A", label: "Phone, clock clear", description: "Lock-screen wallpaper: top kept clear for the clock, details on the left, picture on the right.", formats: ["phone" /* Phone */] },
|
|
822
|
+
{ id: "phone-arch" /* PhoneArch */, designId: "P-B", label: "Phone arch", description: "Lock-screen wallpaper: top kept clear for the clock, details in the middle, the picture in an arch at the bottom.", formats: ["phone" /* Phone */] },
|
|
823
|
+
{ id: "desktop-safe" /* DesktopSafe */, designId: "D-A", label: "Desktop, icons clear", description: "Desktop wallpaper: text on the left clear of desktop icons and the taskbar, the picture on the right.", formats: ["desktop" /* Desktop */] },
|
|
824
|
+
{ id: "desktop-arch" /* DesktopArch */, designId: "D-B", label: "Desktop arch", description: "Desktop wallpaper: titles left, the picture in a centred arch, details right.", formats: ["desktop" /* Desktop */] }
|
|
825
|
+
];
|
|
826
|
+
var DEFAULTS = {
|
|
827
|
+
["story" /* Story */]: "picture-right" /* PictureRight */,
|
|
828
|
+
["post" /* Post */]: "picture-right" /* PictureRight */,
|
|
829
|
+
["square" /* Square */]: "picture-right" /* PictureRight */,
|
|
830
|
+
["phone" /* Phone */]: "phone-clock-safe" /* PhoneClockSafe */,
|
|
831
|
+
["desktop" /* Desktop */]: "desktop-safe" /* DesktopSafe */
|
|
832
|
+
};
|
|
833
|
+
function defaultLayoutFor(format) {
|
|
834
|
+
return DEFAULTS[format];
|
|
835
|
+
}
|
|
836
|
+
|
|
637
837
|
// src/examples/kizombaWorkshop.ts
|
|
638
838
|
var WORKSHOPS = [
|
|
639
839
|
{ script: "Saturday 31 October", date: "Sat 31 Oct", first: "20:00 - 21:00", second: "21:00 - 22:00", party: "20\u20AC" },
|
|
@@ -685,6 +885,6 @@ var kizombaWorkshop = {
|
|
|
685
885
|
};
|
|
686
886
|
var kizombaWorkshopDocs = [...WORKSHOPS.map(workshopDoc), kizombaWorkshop];
|
|
687
887
|
|
|
688
|
-
export { BackgroundStyle, FONT_SETS, FORMAT_SIZES, FlierDocError, FontSetId, FormatId, LayoutId, PALETTES, PaletteId, PictureAnchor, exportPng, flierDocSchema, kizombaWorkshop, kizombaWorkshopDocs, render, validateFlierDoc };
|
|
888
|
+
export { BackgroundStyle, FONT_SETS, FORMAT_SIZES, FlierDocError, FontSetId, FormatId, LAYOUT_CATALOGUE, LayoutId, PALETTES, PaletteId, PictureAnchor, defaultLayoutFor, exportPng, flierDocSchema, kizombaWorkshop, kizombaWorkshopDocs, render, validateFlierDoc };
|
|
689
889
|
//# sourceMappingURL=index.mjs.map
|
|
690
890
|
//# sourceMappingURL=index.mjs.map
|