@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/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, 34)], gapAfter: 0 });
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 BOTTOM_HEADER = 0.2;
411
- var BOTTOM_PICTURE = 0.52;
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 top = r2(h * BOTTOM_HEADER);
443
- const pic = r2(h * BOTTOM_PICTURE);
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 - 2 * m },
446
- body: { x: m, y: top, w: w - 2 * m, h: pic - top - m },
447
- picture: { x: 0, y: pic, w, h: h - pic },
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.forEach((c) => placeColumn(c.groups, lo, c.col, out));
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 ? flankFlows(ctx, flankPriceGroups(doc, palette, u), flanks, fonts) : [emptyFlow()];
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 picture = image && doc.picture ? placeContain(image, geo.picture, doc.picture.anchor, geo.alignX, doc.picture.scale) : null;
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: geo.picture,
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)),