@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/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, 96)];
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: 26 * u };
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)?.forEach((row) => groups.push({ lines: [l.strong?.(row.amount, 66), l.accent?.(row.label, 52)], gapAfter: 30 * u }));
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, card: { color: p.card, pad: 28 * u }, uniform: true });
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.88, 0.04, 0.09, 0.14], 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 }
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 ratio = groupRatio(m, k, col.w - 2 * 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);
520
538
  group.lines.forEach((line, i) => {
521
- const size = ratio === null ? lineSize(line, m.widths[i] ?? 1, k, col.w - 2 * pad) : line.size * ratio;
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
- if (logoImage) {
706
- const r3 = placeContain(logoImage, capAligned(zones.logo, head), "top" /* Top */, 1, 1);
707
- ctx.drawImage(logoImage, r3.x, r3.y, r3.w, r3.h);
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
  }