@dloizides/poster-render 0.2.1 → 0.2.3
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 +12 -0
- package/dist/index.js +76 -31
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +76 -31
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -345,7 +345,7 @@ function bodyGroups(doc, p, u, withPrices = true) {
|
|
|
345
345
|
const groups = [];
|
|
346
346
|
const isList = doc.layout === "list" /* List */;
|
|
347
347
|
if (b.venue) {
|
|
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
|
+
groups.push({ lines: b.venue.map((v) => l.venue?.(v, isList ? 48 : 58)), gapAfter: 40 * u, rule: { color: p.accent, height: 8 * u }, ruleBelow: true, icon: { color: p.accent, glyph: p.background } });
|
|
349
349
|
}
|
|
350
350
|
groups.push(...isList ? listGroups(doc, l, u) : eventGroups(doc, l, u, withPrices));
|
|
351
351
|
if (b.includes) {
|
|
@@ -353,7 +353,7 @@ function bodyGroups(doc, p, u, withPrices = true) {
|
|
|
353
353
|
groups.push({ lines, gapAfter: 24 * u, uniform: true });
|
|
354
354
|
}
|
|
355
355
|
if (b.footer) {
|
|
356
|
-
groups.push({ lines: [l.soft?.(b.footer,
|
|
356
|
+
groups.push({ lines: [l.soft?.(b.footer, 38)], gapAfter: 0 });
|
|
357
357
|
}
|
|
358
358
|
return groups;
|
|
359
359
|
}
|
|
@@ -407,8 +407,11 @@ var toRect = (f, w, h) => {
|
|
|
407
407
|
var MARGIN = 0.045;
|
|
408
408
|
var HEADER_PORTRAIT = 0.3;
|
|
409
409
|
var HEADER_LANDSCAPE = 0.36;
|
|
410
|
-
var
|
|
411
|
-
var
|
|
410
|
+
var BOTTOM_PICTURE_W = 0.8;
|
|
411
|
+
var BOTTOM_MIN_TOP = 0.25;
|
|
412
|
+
var BOTTOM_HEADER_SHARE = 0.45;
|
|
413
|
+
var BOTTOM_GAP = 40 / 1080;
|
|
414
|
+
var BOTTOM_ASPECT = 0.57;
|
|
412
415
|
var PICTURE_SIDE = {
|
|
413
416
|
["picture-right" /* PictureRight */]: [0.6, 0.42],
|
|
414
417
|
["picture-left" /* PictureLeft */]: [0.6, 0.42],
|
|
@@ -439,12 +442,15 @@ function side(w, h, fraction) {
|
|
|
439
442
|
}
|
|
440
443
|
function bottom(w, h) {
|
|
441
444
|
const m = r2(Math.min(w, h) * MARGIN);
|
|
442
|
-
const
|
|
443
|
-
const
|
|
445
|
+
const gap = r2(Math.min(w, h) * BOTTOM_GAP);
|
|
446
|
+
const picW = r2(w * BOTTOM_PICTURE_W);
|
|
447
|
+
const picH = Math.min(r2(picW / BOTTOM_ASPECT), h - r2(h * BOTTOM_MIN_TOP));
|
|
448
|
+
const pic = h - picH;
|
|
449
|
+
const top = m + r2((pic - m) * BOTTOM_HEADER_SHARE);
|
|
444
450
|
return {
|
|
445
|
-
header: { x: m, y: m, w: w - 2 * m, h: top -
|
|
446
|
-
body: { x: m, y: top, w: w - 2 * m, h: pic -
|
|
447
|
-
picture: { x:
|
|
451
|
+
header: { x: m, y: m, w: w - 2 * m, h: top - m },
|
|
452
|
+
body: { x: m, y: top, w: w - 2 * m, h: pic - gap - top },
|
|
453
|
+
picture: { x: r2((w - picW) / 2), y: pic, w: picW, h: picH },
|
|
448
454
|
alignX: 0.5
|
|
449
455
|
};
|
|
450
456
|
}
|
|
@@ -548,18 +554,24 @@ function placeGroup(m, k, col, y0, out) {
|
|
|
548
554
|
});
|
|
549
555
|
y = placeIcon(group, d, col.x + pad, top + pad, y, out);
|
|
550
556
|
y += pad;
|
|
557
|
+
if (group.rule && group.ruleBelow) {
|
|
558
|
+
y += group.rule.height * k * (RULE_GAP - 1);
|
|
559
|
+
out?.rules.push({ rect: { x: col.x, y, w: col.w * RULE_WIDTH, h: group.rule.height * k }, color: group.rule.color });
|
|
560
|
+
y += group.rule.height * k;
|
|
561
|
+
}
|
|
551
562
|
if (group.card) {
|
|
552
563
|
out?.cards.unshift({ rect: { x: col.x, y: top, w: col.w, h: y - top }, color: group.card.color });
|
|
553
564
|
}
|
|
554
565
|
return y + group.gapAfter * k - y0;
|
|
555
566
|
}
|
|
556
|
-
function placeColumn(groups, k, col, out) {
|
|
567
|
+
function placeColumn(groups, k, col, out, spread = 0) {
|
|
557
568
|
let h = 0;
|
|
558
569
|
for (const g of groups) {
|
|
559
|
-
h += placeGroup(g, k, col, col.y + h, out);
|
|
570
|
+
h += placeGroup(g, k, col, col.y + h, out) + spread;
|
|
560
571
|
}
|
|
561
|
-
return h;
|
|
572
|
+
return h - spread;
|
|
562
573
|
}
|
|
574
|
+
var columnSum = (heights, from, to) => heights.slice(from, to).reduce((a, b) => a + b, 0);
|
|
563
575
|
var tallest = (heights, cut) => {
|
|
564
576
|
const left = heights.slice(0, cut).reduce((a, b) => a + b, 0);
|
|
565
577
|
const right = heights.slice(cut).reduce((a, b) => a + b, 0);
|
|
@@ -585,19 +597,17 @@ function splitColumns(groups, zone) {
|
|
|
585
597
|
if (zone.w / Math.max(zone.h, 1) <= WIDE || groups.length < 2) {
|
|
586
598
|
return [{ col: zone, groups }];
|
|
587
599
|
}
|
|
588
|
-
const heights = groups.map((g) => placeGroup(g, 1, zone, 0, null));
|
|
589
|
-
const half = heights.reduce((a, b) => a + b, 0) / 2;
|
|
590
|
-
let acc = 0;
|
|
591
|
-
let cut = 1;
|
|
592
|
-
heights.forEach((h, i) => {
|
|
593
|
-
if (acc < half) {
|
|
594
|
-
cut = i + 1;
|
|
595
|
-
}
|
|
596
|
-
acc += h;
|
|
597
|
-
});
|
|
598
|
-
cut = keepTogether(groups, heights, Math.min(cut, groups.length - 1));
|
|
599
600
|
const gap = Math.round(zone.w * 0.04);
|
|
600
601
|
const w = (zone.w - gap) / 2;
|
|
602
|
+
const heights = groups.map((g) => placeGroup(g, 1, { ...zone, w }, 0, null));
|
|
603
|
+
const total = columnSum(heights, 0, heights.length);
|
|
604
|
+
let cut = 1;
|
|
605
|
+
for (let c = 2; c < groups.length; c++) {
|
|
606
|
+
if (Math.abs(total - 2 * columnSum(heights, 0, c)) < Math.abs(total - 2 * columnSum(heights, 0, cut))) {
|
|
607
|
+
cut = c;
|
|
608
|
+
}
|
|
609
|
+
}
|
|
610
|
+
cut = keepTogether(groups, heights, cut);
|
|
601
611
|
return [
|
|
602
612
|
{ col: { ...zone, w }, groups: groups.slice(0, cut) },
|
|
603
613
|
{ col: { ...zone, x: zone.x + w + gap, w }, groups: groups.slice(cut) }
|
|
@@ -627,7 +637,9 @@ function flow(ctx, groups, zone, fonts) {
|
|
|
627
637
|
}
|
|
628
638
|
}
|
|
629
639
|
const out = emptyFlow();
|
|
630
|
-
columns.
|
|
640
|
+
const heights = columns.map((c) => placeColumn(c.groups, lo, c.col, null));
|
|
641
|
+
const end = Math.max(...heights);
|
|
642
|
+
columns.forEach((c, i) => placeColumn(c.groups, lo, c.col, out, c.groups.length > 1 && columns.length > 1 ? (end - (heights[i] ?? end)) / (c.groups.length - 1) : 0));
|
|
631
643
|
const lines = groups.flatMap((g) => g.lines);
|
|
632
644
|
out.texts.forEach((t, i) => {
|
|
633
645
|
const line = lines[i];
|
|
@@ -636,6 +648,42 @@ function flow(ctx, groups, zone, fonts) {
|
|
|
636
648
|
return out;
|
|
637
649
|
}
|
|
638
650
|
|
|
651
|
+
// src/render/regions.ts
|
|
652
|
+
var LINE_HEIGHT2 = 1.2;
|
|
653
|
+
var BODY_GAP = 40;
|
|
654
|
+
function flowFlanks(ctx, groups, flanks, fonts) {
|
|
655
|
+
const half = Math.ceil(groups.length / 2);
|
|
656
|
+
return flanks.map((zone, i) => i === 0 ? flow(ctx, groups.slice(0, half), zone, fonts) : flow(ctx, groups.slice(half), { ...zone, right: true }, fonts));
|
|
657
|
+
}
|
|
658
|
+
function smallestPerLine(groups, results) {
|
|
659
|
+
const sizes = results.flatMap((r3) => r3.texts.map((t) => t.box.h / LINE_HEIGHT2));
|
|
660
|
+
const smallest = [];
|
|
661
|
+
let i = 0;
|
|
662
|
+
groups.forEach(
|
|
663
|
+
(g) => g.lines.forEach((_line, j) => {
|
|
664
|
+
smallest[j] = Math.min(smallest[j] ?? Infinity, sizes[i] ?? Infinity);
|
|
665
|
+
i++;
|
|
666
|
+
})
|
|
667
|
+
);
|
|
668
|
+
return smallest;
|
|
669
|
+
}
|
|
670
|
+
function equalFlanks(ctx, groups, flanks, fonts) {
|
|
671
|
+
const smallest = smallestPerLine(groups, flowFlanks(ctx, groups, flanks, fonts));
|
|
672
|
+
const sized = groups.map((g) => ({ ...g, lines: g.lines.map((line, j) => ({ ...line, size: Math.min(line.size, smallest[j] ?? line.size) })) }));
|
|
673
|
+
return flowFlanks(ctx, sized, flanks, fonts);
|
|
674
|
+
}
|
|
675
|
+
function measuredEnd(f, fallback) {
|
|
676
|
+
const bottoms = [...f.texts.map((t) => t.box), ...f.rules.map((r3) => r3.rect), ...f.icons.map((i) => i.rect)].map((r3) => r3.y + r3.h);
|
|
677
|
+
return bottoms.length > 0 ? Math.max(...bottoms) : fallback;
|
|
678
|
+
}
|
|
679
|
+
function pictureRegion(layout, geo, body, u, frame) {
|
|
680
|
+
if (layout !== "picture-bottom" /* PictureBottom */) {
|
|
681
|
+
return geo.picture;
|
|
682
|
+
}
|
|
683
|
+
const y = Math.min(geo.picture.y, Math.ceil(measuredEnd(body, geo.body.y) + BODY_GAP * u));
|
|
684
|
+
return { x: geo.picture.x, y, w: geo.picture.w, h: frame.height - y };
|
|
685
|
+
}
|
|
686
|
+
|
|
639
687
|
// src/render/render.ts
|
|
640
688
|
var BASE = 1080;
|
|
641
689
|
var LOGO_SHARE = 0.26;
|
|
@@ -728,10 +776,6 @@ function drawArch(ctx, r3, color) {
|
|
|
728
776
|
ctx.arc(r3.x + rad, r3.y + rad, rad, Math.PI, Math.PI * 2);
|
|
729
777
|
ctx.fill();
|
|
730
778
|
}
|
|
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
779
|
function capAligned(zone, head) {
|
|
736
780
|
const first = head.texts[0]?.box;
|
|
737
781
|
const shift = first ? Math.min(Math.max(0, Math.round(first.y + first.h * CAP_TOP) - zone.y), Math.round(zone.h / 2)) : 0;
|
|
@@ -765,7 +809,7 @@ function render(input, assets, canvas) {
|
|
|
765
809
|
const head = flow(ctx, headerGroups(doc, palette, u), zones.text, fonts);
|
|
766
810
|
const flanks = geo.flanks ?? [];
|
|
767
811
|
const body = flow(ctx, bodyGroups(doc, palette, u, flanks.length === 0), geo.body, fonts);
|
|
768
|
-
const sides = flanks.length > 0 ?
|
|
812
|
+
const sides = flanks.length > 0 ? equalFlanks(ctx, flankPriceGroups(doc, palette, u), flanks, fonts) : [emptyFlow()];
|
|
769
813
|
const radius = Math.min(width, height) * CARD_RADIUS;
|
|
770
814
|
[head, body, ...sides].forEach((f) => drawFlow(ctx, f, radius));
|
|
771
815
|
const logoImage = doc.logo ? assets[doc.logo.imageId] : void 0;
|
|
@@ -778,7 +822,8 @@ function render(input, assets, canvas) {
|
|
|
778
822
|
if (backdrop) {
|
|
779
823
|
drawArch(ctx, backdrop, palette.arch);
|
|
780
824
|
}
|
|
781
|
-
const
|
|
825
|
+
const region = pictureRegion(doc.layout, geo, body, u, { height });
|
|
826
|
+
const picture = image && doc.picture ? placeContain(image, region, doc.picture.anchor, geo.alignX, doc.picture.scale) : null;
|
|
782
827
|
if (image && picture) {
|
|
783
828
|
ctx.drawImage(image, picture.x, picture.y, picture.w, picture.h);
|
|
784
829
|
}
|
|
@@ -786,7 +831,7 @@ function render(input, assets, canvas) {
|
|
|
786
831
|
width,
|
|
787
832
|
height,
|
|
788
833
|
picture,
|
|
789
|
-
pictureRegion:
|
|
834
|
+
pictureRegion: region,
|
|
790
835
|
textBoxes: [head, body, ...sides].flatMap((f) => f.texts.map((t) => t.box)),
|
|
791
836
|
cardBoxes: body.cards.map((c) => c.rect),
|
|
792
837
|
iconBoxes: [head, body, ...sides].flatMap((f) => f.icons.map((i) => i.rect)),
|