@dloizides/poster-render 0.2.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 +8 -0
- package/dist/index.d.mts +2 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +84 -15
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +84 -15
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -312,18 +312,19 @@ function headerGroups(doc, p, u) {
|
|
|
312
312
|
return [{ lines: lines.filter((x) => x !== void 0), gapAfter: 0 }];
|
|
313
313
|
}
|
|
314
314
|
function itemGroup(item, l, u) {
|
|
315
|
-
const lines = [l.script?.(item.date,
|
|
315
|
+
const lines = [l.script?.(item.date, 67)];
|
|
316
316
|
item.schedule.forEach((row) => lines.push(l.strong?.(`${row.label} ${row.time}`, 42)));
|
|
317
317
|
if (item.price) {
|
|
318
318
|
lines.push(l.accent?.(item.price, 42));
|
|
319
319
|
}
|
|
320
|
-
return { lines, gapAfter:
|
|
320
|
+
return { lines, gapAfter: 48 * u };
|
|
321
321
|
}
|
|
322
322
|
function eventGroups(doc, l, u, withPrices) {
|
|
323
323
|
const b = doc.blocks;
|
|
324
324
|
const groups = [];
|
|
325
325
|
b.schedule?.forEach((row) => groups.push({ lines: [l.strong?.(row.label, 66), l.accent?.(row.time, 60)], gapAfter: 30 * u }));
|
|
326
|
-
(withPrices ? b.prices : void 0)
|
|
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 }));
|
|
327
328
|
return groups;
|
|
328
329
|
}
|
|
329
330
|
function listGroups(doc, l, u) {
|
|
@@ -344,12 +345,12 @@ function bodyGroups(doc, p, u, withPrices = true) {
|
|
|
344
345
|
const groups = [];
|
|
345
346
|
const isList = doc.layout === "list" /* List */;
|
|
346
347
|
if (b.venue) {
|
|
347
|
-
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 } });
|
|
348
349
|
}
|
|
349
350
|
groups.push(...isList ? listGroups(doc, l, u) : eventGroups(doc, l, u, withPrices));
|
|
350
351
|
if (b.includes) {
|
|
351
352
|
const lines = [l.accent?.(b.includes.heading, 38), ...b.includes.items.map((i) => l.item?.(i, 38))];
|
|
352
|
-
groups.push({ lines, gapAfter: 24 * u,
|
|
353
|
+
groups.push({ lines, gapAfter: 24 * u, uniform: true });
|
|
353
354
|
}
|
|
354
355
|
if (b.footer) {
|
|
355
356
|
groups.push({ lines: [l.soft?.(b.footer, 34)], gapAfter: 0 });
|
|
@@ -388,11 +389,11 @@ var LAYOUT_SPECS = {
|
|
|
388
389
|
},
|
|
389
390
|
["desktop-safe" /* DesktopSafe */]: {
|
|
390
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 },
|
|
391
|
-
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 }
|
|
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] }
|
|
392
393
|
},
|
|
393
394
|
["desktop-arch" /* DesktopArch */]: {
|
|
394
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 },
|
|
395
|
-
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.
|
|
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 }
|
|
396
397
|
}
|
|
397
398
|
};
|
|
398
399
|
var r = Math.round;
|
|
@@ -494,8 +495,14 @@ var RULE_WIDTH = 0.9;
|
|
|
494
495
|
var WIDE = 1.6;
|
|
495
496
|
var MIN_K = 0.02;
|
|
496
497
|
var STEPS = 22;
|
|
498
|
+
var ICON_SCALE = 1.6;
|
|
499
|
+
var ICON_GAP = 0.35;
|
|
497
500
|
var fontOf = (line, size, fonts) => `${line.weight} ${size}px ${fonts[line.role]}`;
|
|
498
|
-
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
|
+
};
|
|
499
506
|
function lineSize(line, width, k, avail) {
|
|
500
507
|
const indent = line.bullet ? line.size * k * BULLET_INDENT : 0;
|
|
501
508
|
return Math.max(0, Math.min(line.size * k, line.size * (avail - indent) / Math.max(width, 1)));
|
|
@@ -506,6 +513,14 @@ function groupRatio(m, k, avail) {
|
|
|
506
513
|
}
|
|
507
514
|
return Math.min(...m.group.lines.map((line, i) => lineSize(line, m.widths[i] ?? 1, k, avail) / line.size));
|
|
508
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
|
+
}
|
|
509
524
|
function placeGroup(m, k, col, y0, out) {
|
|
510
525
|
const { group } = m;
|
|
511
526
|
let y = y0;
|
|
@@ -516,10 +531,13 @@ function placeGroup(m, k, col, y0, out) {
|
|
|
516
531
|
const pad = group.card ? group.card.pad * k : 0;
|
|
517
532
|
const top = y;
|
|
518
533
|
y += pad;
|
|
519
|
-
const
|
|
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);
|
|
520
538
|
group.lines.forEach((line, i) => {
|
|
521
|
-
const size = ratio === null ? lineSize(line, m.widths[i] ?? 1, k,
|
|
522
|
-
const indent = line.bullet ? line.size * k * BULLET_INDENT : 0;
|
|
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;
|
|
523
541
|
const w = (m.widths[i] ?? 0) * size / line.size;
|
|
524
542
|
const x = col.right ? col.x + col.w - pad - w : col.x + pad + indent;
|
|
525
543
|
out?.texts.push({ text: line.text, font: "", color: line.color, box: { x, y, w, h: size * LINE_HEIGHT } });
|
|
@@ -528,6 +546,7 @@ function placeGroup(m, k, col, y0, out) {
|
|
|
528
546
|
}
|
|
529
547
|
y += size * LINE_HEIGHT;
|
|
530
548
|
});
|
|
549
|
+
y = placeIcon(group, d, col.x + pad, top + pad, y, out);
|
|
531
550
|
y += pad;
|
|
532
551
|
if (group.card) {
|
|
533
552
|
out?.cards.unshift({ rect: { x: col.x, y: top, w: col.w, h: y - top }, color: group.card.color });
|
|
@@ -541,6 +560,27 @@ function placeColumn(groups, k, col, out) {
|
|
|
541
560
|
}
|
|
542
561
|
return h;
|
|
543
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
|
+
}
|
|
544
584
|
function splitColumns(groups, zone) {
|
|
545
585
|
if (zone.w / Math.max(zone.h, 1) <= WIDE || groups.length < 2) {
|
|
546
586
|
return [{ col: zone, groups }];
|
|
@@ -555,7 +595,7 @@ function splitColumns(groups, zone) {
|
|
|
555
595
|
}
|
|
556
596
|
acc += h;
|
|
557
597
|
});
|
|
558
|
-
cut = Math.min(cut, groups.length - 1);
|
|
598
|
+
cut = keepTogether(groups, heights, Math.min(cut, groups.length - 1));
|
|
559
599
|
const gap = Math.round(zone.w * 0.04);
|
|
560
600
|
const w = (zone.w - gap) / 2;
|
|
561
601
|
return [
|
|
@@ -601,6 +641,32 @@ var BASE = 1080;
|
|
|
601
641
|
var LOGO_SHARE = 0.26;
|
|
602
642
|
var CARD_RADIUS = 0.035;
|
|
603
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
|
+
}
|
|
604
670
|
function roundRect(ctx, r3, radius) {
|
|
605
671
|
const rad = Math.min(radius, r3.w / 2, r3.h / 2);
|
|
606
672
|
ctx.beginPath();
|
|
@@ -627,6 +693,7 @@ function drawFlow(ctx, f, radius) {
|
|
|
627
693
|
ctx.arc(b.x, b.y, b.r, 0, Math.PI * 2);
|
|
628
694
|
ctx.fill();
|
|
629
695
|
});
|
|
696
|
+
f.icons.forEach((i) => drawPin(ctx, i.rect, i.color, i.glyph));
|
|
630
697
|
ctx.textAlign = "left";
|
|
631
698
|
ctx.textBaseline = "top";
|
|
632
699
|
f.texts.forEach((t) => {
|
|
@@ -702,9 +769,9 @@ function render(input, assets, canvas) {
|
|
|
702
769
|
const radius = Math.min(width, height) * CARD_RADIUS;
|
|
703
770
|
[head, body, ...sides].forEach((f) => drawFlow(ctx, f, radius));
|
|
704
771
|
const logoImage = doc.logo ? assets[doc.logo.imageId] : void 0;
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
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);
|
|
708
775
|
}
|
|
709
776
|
const image = doc.picture ? assets[doc.picture.imageId] : void 0;
|
|
710
777
|
const backdrop = image ? archRect(geo) : null;
|
|
@@ -722,6 +789,8 @@ function render(input, assets, canvas) {
|
|
|
722
789
|
pictureRegion: geo.picture,
|
|
723
790
|
textBoxes: [head, body, ...sides].flatMap((f) => f.texts.map((t) => t.box)),
|
|
724
791
|
cardBoxes: body.cards.map((c) => c.rect),
|
|
792
|
+
iconBoxes: [head, body, ...sides].flatMap((f) => f.icons.map((i) => i.rect)),
|
|
793
|
+
logo,
|
|
725
794
|
backdrop
|
|
726
795
|
};
|
|
727
796
|
}
|