@vercel/og 0.5.11 → 0.5.12

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.
@@ -18602,7 +18602,86 @@ function loadEmoji(code, type) {
18602
18602
  return fetch(`${api}${code.toUpperCase()}.svg`);
18603
18603
  }
18604
18604
 
18605
- // src/og.ts
18605
+ // src/language/index.ts
18606
+ var FontDetector = class {
18607
+ constructor() {
18608
+ this.rangesByLang = {};
18609
+ }
18610
+ async detect(text, fonts) {
18611
+ await this.load(fonts);
18612
+ const result = {};
18613
+ for (const segment of text) {
18614
+ const lang = this.detectSegment(segment, fonts);
18615
+ if (lang) {
18616
+ result[lang] = result[lang] || "";
18617
+ result[lang] += segment;
18618
+ }
18619
+ }
18620
+ return result;
18621
+ }
18622
+ detectSegment(segment, fonts) {
18623
+ for (const font of fonts) {
18624
+ const range = this.rangesByLang[font];
18625
+ if (range && checkSegmentInRange(segment, range)) {
18626
+ return font;
18627
+ }
18628
+ }
18629
+ return null;
18630
+ }
18631
+ async load(fonts) {
18632
+ let params = "";
18633
+ const existingLang = Object.keys(this.rangesByLang);
18634
+ const langNeedsToLoad = fonts.filter((font) => !existingLang.includes(font));
18635
+ if (langNeedsToLoad.length === 0) {
18636
+ return;
18637
+ }
18638
+ for (const font of langNeedsToLoad) {
18639
+ params += `family=${font}&`;
18640
+ }
18641
+ params += "display=swap";
18642
+ const API = `https://fonts.googleapis.com/css2?${params}`;
18643
+ const fontFace = await (await fetch(API, {
18644
+ headers: {
18645
+ "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Safari/537.36"
18646
+ }
18647
+ })).text();
18648
+ this.addDetectors(fontFace);
18649
+ }
18650
+ addDetectors(input) {
18651
+ const regex = /font-family:\s*'(.+?)';.+?unicode-range:\s*(.+?);/gms;
18652
+ const matches = input.matchAll(regex);
18653
+ for (const [, _lang, range] of matches) {
18654
+ const lang = _lang.replaceAll(" ", "+");
18655
+ if (!this.rangesByLang[lang]) {
18656
+ this.rangesByLang[lang] = [];
18657
+ }
18658
+ this.rangesByLang[lang].push(...convert(range));
18659
+ }
18660
+ }
18661
+ };
18662
+ function convert(input) {
18663
+ return input.split(", ").map((range) => {
18664
+ range = range.replaceAll("U+", "");
18665
+ const [start, end] = range.split("-").map((hex) => parseInt(hex, 16));
18666
+ if (isNaN(end)) {
18667
+ return start;
18668
+ }
18669
+ return [start, end];
18670
+ });
18671
+ }
18672
+ function checkSegmentInRange(segment, range) {
18673
+ const codePoint = segment.codePointAt(0);
18674
+ if (!codePoint)
18675
+ return false;
18676
+ return range.some((val) => {
18677
+ if (typeof val === "number") {
18678
+ return codePoint === val;
18679
+ } else {
18680
+ const [start, end] = val;
18681
+ return start <= codePoint && codePoint <= end;
18682
+ }
18683
+ });
18684
+ }
18606
18685
  var languageFontMap = {
18607
18686
  "ja-JP": "Noto+Sans+JP",
18608
18687
  "ko-KR": "Noto+Sans+KR",
@@ -18622,6 +18701,8 @@ var languageFontMap = {
18622
18701
  math: "Noto+Sans+Math",
18623
18702
  unknown: "Noto+Sans"
18624
18703
  };
18704
+
18705
+ // src/og.ts
18625
18706
  async function loadGoogleFont(font, text) {
18626
18707
  if (!font || !text)
18627
18708
  return;
@@ -18633,27 +18714,35 @@ async function loadGoogleFont(font, text) {
18633
18714
  })).text();
18634
18715
  const resource = css.match(/src: url\((.+)\) format\('(opentype|truetype)'\)/);
18635
18716
  if (!resource)
18636
- throw new Error("Failed to load font");
18637
- return fetch(resource[1]).then((res) => res.arrayBuffer());
18717
+ throw new Error("Failed to download dynamic font");
18718
+ const res = await fetch(resource[1]);
18719
+ if (!res.ok) {
18720
+ throw new Error("Failed to download dynamic font. Status: " + res.status);
18721
+ }
18722
+ return res.arrayBuffer();
18638
18723
  }
18724
+ var detector = new FontDetector();
18639
18725
  var assetCache = /* @__PURE__ */ new Map();
18640
18726
  var loadDynamicAsset = ({ emoji }) => {
18641
18727
  const fn = async (code, text) => {
18642
18728
  if (code === "emoji") {
18643
18729
  return `data:image/svg+xml;base64,` + btoa(await (await loadEmoji(getIconCode(text), emoji)).text());
18644
18730
  }
18645
- if (!languageFontMap[code])
18646
- code = "unknown";
18731
+ const codes = code.split("|");
18732
+ const names = codes.map((code2) => languageFontMap[code2]).filter(Boolean).flat();
18733
+ if (names.length === 0)
18734
+ return [];
18647
18735
  try {
18648
- const data = await loadGoogleFont(languageFontMap[code], text);
18649
- if (data) {
18650
- return {
18651
- name: `satori_${code}_fallback_${text}`,
18652
- data,
18653
- weight: 400,
18654
- style: "normal"
18655
- };
18656
- }
18736
+ const textByFont = await detector.detect(text, names);
18737
+ const fonts = Object.keys(textByFont);
18738
+ const fontData = await Promise.all(fonts.map((font) => loadGoogleFont(font, textByFont[font])));
18739
+ return fontData.map((data, index) => ({
18740
+ name: `satori_${codes[index]}_fallback_${text}`,
18741
+ data,
18742
+ weight: 400,
18743
+ style: "normal",
18744
+ lang: codes[index] === "unknown" ? void 0 : codes[index]
18745
+ }));
18657
18746
  } catch (e) {
18658
18747
  console.error("Failed to load dynamic font for", text, ". Error:", e);
18659
18748
  }