@pixldocs/canvas-renderer 0.5.155 → 0.5.157
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-BwHwQrnS.cjs → index-NR05ndTM.cjs} +9 -18
- package/dist/index-NR05ndTM.cjs.map +1 -0
- package/dist/{index-BQA8_wgq.js → index-xn-L7QHB.js} +9 -18
- package/dist/index-xn-L7QHB.js.map +1 -0
- package/dist/index.cjs +1 -1
- package/dist/index.js +1 -1
- package/dist/{pdfFonts-BTEVnYX8.cjs → svgTextToPath-7fhL08qs.cjs} +796 -31
- package/dist/svgTextToPath-7fhL08qs.cjs.map +1 -0
- package/dist/{pdfFonts-b3_bv7F0.js → svgTextToPath-V1vC0SQt.js} +747 -20
- package/dist/svgTextToPath-V1vC0SQt.js.map +1 -0
- package/dist/{vectorPdfExport-C5NmVmxB.cjs → vectorPdfExport-CTktki-M.cjs} +50 -51
- package/dist/vectorPdfExport-CTktki-M.cjs.map +1 -0
- package/dist/{vectorPdfExport-vCax992V.js → vectorPdfExport-CZyyQbwm.js} +50 -51
- package/dist/vectorPdfExport-CZyyQbwm.js.map +1 -0
- package/package.json +1 -1
- package/dist/index-BQA8_wgq.js.map +0 -1
- package/dist/index-BwHwQrnS.cjs.map +0 -1
- package/dist/pdfFonts-BTEVnYX8.cjs.map +0 -1
- package/dist/pdfFonts-b3_bv7F0.js.map +0 -1
- package/dist/svgTextToPath-BXAzwaaR.js +0 -706
- package/dist/svgTextToPath-BXAzwaaR.js.map +0 -1
- package/dist/svgTextToPath-IM1f6F-f.cjs +0 -745
- package/dist/svgTextToPath-IM1f6F-f.cjs.map +0 -1
- package/dist/vectorPdfExport-C5NmVmxB.cjs.map +0 -1
- package/dist/vectorPdfExport-vCax992V.js.map +0 -1
|
@@ -1,6 +1,7 @@
|
|
|
1
|
+
import * as opentype from "opentype.js";
|
|
1
2
|
const __vite_import_meta_env__ = { "BASE_URL": "/", "DEV": false, "MODE": "production", "PROD": true, "SSR": false };
|
|
2
|
-
const fontCache = /* @__PURE__ */ new Map();
|
|
3
|
-
const fontBytesCache = /* @__PURE__ */ new Map();
|
|
3
|
+
const fontCache$1 = /* @__PURE__ */ new Map();
|
|
4
|
+
const fontBytesCache$1 = /* @__PURE__ */ new Map();
|
|
4
5
|
const googleFontUrlCache = /* @__PURE__ */ new Map();
|
|
5
6
|
const googleFontNotFound = /* @__PURE__ */ new Set();
|
|
6
7
|
const proxyNotFound = /* @__PURE__ */ new Set();
|
|
@@ -799,7 +800,7 @@ const embedFont = async (pdf, fontName, weight = 400, isItalic = false) => {
|
|
|
799
800
|
const cacheKey = `${fontName}-${resolvedWeight}${isItalic ? "-italic" : ""}`;
|
|
800
801
|
const jsPdfFontName = getEmbeddedJsPDFFontName(fontName, weight, isItalic);
|
|
801
802
|
try {
|
|
802
|
-
let base64Font = fontCache.get(cacheKey);
|
|
803
|
+
let base64Font = fontCache$1.get(cacheKey);
|
|
803
804
|
if (!base64Font) {
|
|
804
805
|
const response = await fetch(fontPath);
|
|
805
806
|
if (!response.ok) throw new Error(`Failed to fetch font: ${response.statusText}`);
|
|
@@ -810,7 +811,7 @@ const embedFont = async (pdf, fontName, weight = 400, isItalic = false) => {
|
|
|
810
811
|
let binary = "";
|
|
811
812
|
for (let i = 0; i < bytes.length; i++) binary += String.fromCharCode(bytes[i]);
|
|
812
813
|
base64Font = btoa(binary);
|
|
813
|
-
fontCache.set(cacheKey, base64Font);
|
|
814
|
+
fontCache$1.set(cacheKey, base64Font);
|
|
814
815
|
} else if (!registeredVariantCoverage.has(variantKey(fontName, resolvedWeight, isItalic))) {
|
|
815
816
|
registeredVariantCoverage.set(variantKey(fontName, resolvedWeight, isItalic), extractSupportedCodePointsFromTtf(base64ToBytes(base64Font)));
|
|
816
817
|
}
|
|
@@ -836,16 +837,16 @@ const isFontAvailable = (fontName) => {
|
|
|
836
837
|
};
|
|
837
838
|
async function fetchGoogleFontTTF(fontFamily, weight = 400, isItalic = false) {
|
|
838
839
|
const cacheKey = `gf:${fontFamily}:${weight}:${isItalic ? "i" : "n"}`;
|
|
839
|
-
if (fontCache.has(cacheKey)) return fontCache.get(cacheKey);
|
|
840
|
+
if (fontCache$1.has(cacheKey)) return fontCache$1.get(cacheKey);
|
|
840
841
|
const notFoundKey = remoteVariantKey(fontFamily, weight, isItalic);
|
|
841
842
|
if (googleFontNotFound.has(notFoundKey)) return null;
|
|
842
843
|
const proxyBytes = await fetchTtfViaProxy(fontFamily, weight, isItalic, "google");
|
|
843
844
|
if (proxyBytes && isJsPdfEmbeddableTrueType(proxyBytes)) {
|
|
844
|
-
fontBytesCache.set(cacheKey, proxyBytes);
|
|
845
|
+
fontBytesCache$1.set(cacheKey, proxyBytes);
|
|
845
846
|
let binary = "";
|
|
846
847
|
for (let i = 0; i < proxyBytes.length; i++) binary += String.fromCharCode(proxyBytes[i]);
|
|
847
848
|
const b64 = btoa(binary);
|
|
848
|
-
fontCache.set(cacheKey, b64);
|
|
849
|
+
fontCache$1.set(cacheKey, b64);
|
|
849
850
|
return b64;
|
|
850
851
|
}
|
|
851
852
|
if (resolveFontProxyUrl()) return null;
|
|
@@ -876,11 +877,11 @@ async function fetchGoogleFontTTF(fontFamily, weight = 400, isItalic = false) {
|
|
|
876
877
|
console.warn(`[pdfFonts] Google Fonts returned a non-TTF font for ${fontFamily} (${weight}); skipping jsPDF embed`);
|
|
877
878
|
return null;
|
|
878
879
|
}
|
|
879
|
-
fontBytesCache.set(cacheKey, bytes);
|
|
880
|
+
fontBytesCache$1.set(cacheKey, bytes);
|
|
880
881
|
let binary = "";
|
|
881
882
|
for (let i = 0; i < bytes.length; i++) binary += String.fromCharCode(bytes[i]);
|
|
882
883
|
const b64 = btoa(binary);
|
|
883
|
-
fontCache.set(cacheKey, b64);
|
|
884
|
+
fontCache$1.set(cacheKey, b64);
|
|
884
885
|
return b64;
|
|
885
886
|
} catch (err) {
|
|
886
887
|
console.warn(`[pdfFonts] fetchGoogleFontTTF failed for ${fontFamily} (${weight}):`, err);
|
|
@@ -889,9 +890,9 @@ async function fetchGoogleFontTTF(fontFamily, weight = 400, isItalic = false) {
|
|
|
889
890
|
}
|
|
890
891
|
async function getGoogleFontBytes(fontFamily, weight = 400, isItalic = false) {
|
|
891
892
|
const cacheKey = `gf:${fontFamily}:${weight}:${isItalic ? "i" : "n"}`;
|
|
892
|
-
if (fontBytesCache.has(cacheKey)) return fontBytesCache.get(cacheKey);
|
|
893
|
+
if (fontBytesCache$1.has(cacheKey)) return fontBytesCache$1.get(cacheKey);
|
|
893
894
|
await fetchGoogleFontTTF(fontFamily, weight, isItalic);
|
|
894
|
-
return fontBytesCache.get(cacheKey) || null;
|
|
895
|
+
return fontBytesCache$1.get(cacheKey) || null;
|
|
895
896
|
}
|
|
896
897
|
const fontshareNotFound = /* @__PURE__ */ new Set();
|
|
897
898
|
function toFontshareSlug(family) {
|
|
@@ -899,16 +900,16 @@ function toFontshareSlug(family) {
|
|
|
899
900
|
}
|
|
900
901
|
async function fetchFontshareTTF(fontFamily, weight = 400, isItalic = false, slug) {
|
|
901
902
|
const cacheKey = `gf:${fontFamily}:${weight}:${isItalic ? "i" : "n"}`;
|
|
902
|
-
if (fontCache.has(cacheKey)) return fontCache.get(cacheKey);
|
|
903
|
+
if (fontCache$1.has(cacheKey)) return fontCache$1.get(cacheKey);
|
|
903
904
|
const notFoundKey = remoteVariantKey(fontFamily, weight, isItalic);
|
|
904
905
|
if (fontshareNotFound.has(notFoundKey)) return null;
|
|
905
906
|
const proxyBytes = await fetchTtfViaProxy(fontFamily, weight, isItalic, "fontshare");
|
|
906
907
|
if (proxyBytes && isJsPdfEmbeddableTrueType(proxyBytes)) {
|
|
907
|
-
fontBytesCache.set(cacheKey, proxyBytes);
|
|
908
|
+
fontBytesCache$1.set(cacheKey, proxyBytes);
|
|
908
909
|
let binary = "";
|
|
909
910
|
for (let i = 0; i < proxyBytes.length; i++) binary += String.fromCharCode(proxyBytes[i]);
|
|
910
911
|
const b64 = btoa(binary);
|
|
911
|
-
fontCache.set(cacheKey, b64);
|
|
912
|
+
fontCache$1.set(cacheKey, b64);
|
|
912
913
|
return b64;
|
|
913
914
|
}
|
|
914
915
|
if (resolveFontProxyUrl()) return null;
|
|
@@ -937,11 +938,11 @@ async function fetchFontshareTTF(fontFamily, weight = 400, isItalic = false, slu
|
|
|
937
938
|
console.warn(`[pdfFonts] Fontshare returned a non-TTF font for ${fontFamily} (${weight}); skipping jsPDF embed`);
|
|
938
939
|
return null;
|
|
939
940
|
}
|
|
940
|
-
fontBytesCache.set(cacheKey, bytes);
|
|
941
|
+
fontBytesCache$1.set(cacheKey, bytes);
|
|
941
942
|
let binary = "";
|
|
942
943
|
for (let i = 0; i < bytes.length; i++) binary += String.fromCharCode(bytes[i]);
|
|
943
944
|
const b64 = btoa(binary);
|
|
944
|
-
fontCache.set(cacheKey, b64);
|
|
945
|
+
fontCache$1.set(cacheKey, b64);
|
|
945
946
|
return b64;
|
|
946
947
|
} catch (err) {
|
|
947
948
|
console.warn(`[pdfFonts] fetchFontshareTTF failed for ${fontFamily} (${weight}):`, err);
|
|
@@ -950,9 +951,9 @@ async function fetchFontshareTTF(fontFamily, weight = 400, isItalic = false, slu
|
|
|
950
951
|
}
|
|
951
952
|
async function getFontshareFontBytes(fontFamily, weight = 400, isItalic = false, slug) {
|
|
952
953
|
const cacheKey = `gf:${fontFamily}:${weight}:${isItalic ? "i" : "n"}`;
|
|
953
|
-
if (fontBytesCache.has(cacheKey)) return fontBytesCache.get(cacheKey);
|
|
954
|
+
if (fontBytesCache$1.has(cacheKey)) return fontBytesCache$1.get(cacheKey);
|
|
954
955
|
await fetchFontshareTTF(fontFamily, weight, isItalic);
|
|
955
|
-
return fontBytesCache.get(cacheKey) || null;
|
|
956
|
+
return fontBytesCache$1.get(cacheKey) || null;
|
|
956
957
|
}
|
|
957
958
|
const embedFontWithGoogleFallback = async (pdf, fontName, weight = 400, isItalic = false) => {
|
|
958
959
|
if (FONT_FILES[fontName]) {
|
|
@@ -1103,7 +1104,8 @@ function resolveDevanagariSibling(latinFont) {
|
|
|
1103
1104
|
if (!latinFont) return FONT_FALLBACK_DEVANAGARI;
|
|
1104
1105
|
return LATIN_TO_DEVANAGARI[latinFont] || FONT_FALLBACK_DEVANAGARI;
|
|
1105
1106
|
}
|
|
1106
|
-
|
|
1107
|
+
const pdfFonts = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
|
|
1108
|
+
__proto__: null,
|
|
1107
1109
|
FONT_FALLBACK_DEVANAGARI,
|
|
1108
1110
|
FONT_FALLBACK_MATH,
|
|
1109
1111
|
FONT_FALLBACK_SYMBOLS,
|
|
@@ -1127,5 +1129,730 @@ export {
|
|
|
1127
1129
|
resolveBestRegisteredVariant,
|
|
1128
1130
|
resolveDevanagariSibling,
|
|
1129
1131
|
resolveFontWeight
|
|
1132
|
+
}, Symbol.toStringTag, { value: "Module" }));
|
|
1133
|
+
const HB_WASM_URL = "/wasm/hb.wasm";
|
|
1134
|
+
let hbInstancePromise = null;
|
|
1135
|
+
async function getHB() {
|
|
1136
|
+
if (hbInstancePromise) return hbInstancePromise;
|
|
1137
|
+
hbInstancePromise = (async () => {
|
|
1138
|
+
const [{ default: createHarfBuzz }, { default: hbjs }] = await Promise.all([
|
|
1139
|
+
import("harfbuzzjs/hb.js"),
|
|
1140
|
+
import("harfbuzzjs/hbjs.js")
|
|
1141
|
+
]);
|
|
1142
|
+
const moduleInstance = await createHarfBuzz({
|
|
1143
|
+
locateFile: (path) => {
|
|
1144
|
+
if (path.endsWith(".wasm")) return HB_WASM_URL;
|
|
1145
|
+
return path;
|
|
1146
|
+
}
|
|
1147
|
+
});
|
|
1148
|
+
return hbjs(moduleInstance);
|
|
1149
|
+
})();
|
|
1150
|
+
return hbInstancePromise;
|
|
1151
|
+
}
|
|
1152
|
+
const hbFontCache = /* @__PURE__ */ new Map();
|
|
1153
|
+
async function getHBFont(cacheKey, fontDataLoader) {
|
|
1154
|
+
const cached = hbFontCache.get(cacheKey);
|
|
1155
|
+
if (cached) return { font: cached.font, upem: cached.upem };
|
|
1156
|
+
const hb = await getHB();
|
|
1157
|
+
const fontData = await fontDataLoader();
|
|
1158
|
+
const blob = hb.createBlob(fontData);
|
|
1159
|
+
const face = hb.createFace(blob, 0);
|
|
1160
|
+
const font = hb.createFont(face);
|
|
1161
|
+
const upem = (face.getUpem ? face.getUpem() : face.upem ?? 1e3) || 1e3;
|
|
1162
|
+
font.setScale(upem, upem);
|
|
1163
|
+
blob.destroy();
|
|
1164
|
+
hbFontCache.set(cacheKey, { face, font, upem });
|
|
1165
|
+
return { font, upem };
|
|
1166
|
+
}
|
|
1167
|
+
async function shapeRunToSvgPath(fontData, cacheKey, text, x, y, fontSize, opts) {
|
|
1168
|
+
const hb = await getHB();
|
|
1169
|
+
const loader = typeof fontData === "function" ? fontData : async () => fontData;
|
|
1170
|
+
const { font, upem } = await getHBFont(cacheKey, loader);
|
|
1171
|
+
const buffer = hb.createBuffer();
|
|
1172
|
+
buffer.addText(text);
|
|
1173
|
+
if (opts == null ? void 0 : opts.direction) buffer.setDirection(opts.direction);
|
|
1174
|
+
if (opts == null ? void 0 : opts.script) buffer.setScript(opts.script);
|
|
1175
|
+
if (opts == null ? void 0 : opts.language) buffer.setLanguage(opts.language);
|
|
1176
|
+
buffer.guessSegmentProperties();
|
|
1177
|
+
hb.shape(font, buffer);
|
|
1178
|
+
const glyphs = buffer.json();
|
|
1179
|
+
const scale = fontSize / upem;
|
|
1180
|
+
let penX = 0;
|
|
1181
|
+
let penY = 0;
|
|
1182
|
+
const pieces = [];
|
|
1183
|
+
for (const g of glyphs) {
|
|
1184
|
+
const rawPath = font.glyphToPath(g.g);
|
|
1185
|
+
if (rawPath) {
|
|
1186
|
+
const ox = (penX + g.dx) * scale + x;
|
|
1187
|
+
const oy = (penY + g.dy) * -scale + y;
|
|
1188
|
+
pieces.push(transformPathData(rawPath, scale, -scale, ox, oy));
|
|
1189
|
+
}
|
|
1190
|
+
penX += g.ax;
|
|
1191
|
+
penY += g.ay;
|
|
1192
|
+
}
|
|
1193
|
+
buffer.destroy();
|
|
1194
|
+
return {
|
|
1195
|
+
pathData: pieces.join(""),
|
|
1196
|
+
width: penX * scale
|
|
1197
|
+
};
|
|
1198
|
+
}
|
|
1199
|
+
function transformPathData(d, sx, sy, tx, ty) {
|
|
1200
|
+
let out = "";
|
|
1201
|
+
let i = 0;
|
|
1202
|
+
const n = d.length;
|
|
1203
|
+
while (i < n) {
|
|
1204
|
+
const c = d[i];
|
|
1205
|
+
if (c === "M" || c === "L" || c === "C" || c === "Q") {
|
|
1206
|
+
out += c;
|
|
1207
|
+
i++;
|
|
1208
|
+
let pairsBuf = "";
|
|
1209
|
+
while (i < n && d[i] !== "M" && d[i] !== "L" && d[i] !== "C" && d[i] !== "Q" && d[i] !== "Z") {
|
|
1210
|
+
pairsBuf += d[i];
|
|
1211
|
+
i++;
|
|
1212
|
+
}
|
|
1213
|
+
const nums = pairsBuf.trim().split(/[ ,]+/).filter(Boolean).map(Number);
|
|
1214
|
+
for (let k = 0; k < nums.length; k += 2) {
|
|
1215
|
+
const px = nums[k] * sx + tx;
|
|
1216
|
+
const py = nums[k + 1] * sy + ty;
|
|
1217
|
+
if (k > 0) out += " ";
|
|
1218
|
+
out += `${round(px)},${round(py)}`;
|
|
1219
|
+
}
|
|
1220
|
+
} else if (c === "Z") {
|
|
1221
|
+
out += "Z";
|
|
1222
|
+
i++;
|
|
1223
|
+
} else {
|
|
1224
|
+
i++;
|
|
1225
|
+
}
|
|
1226
|
+
}
|
|
1227
|
+
return out;
|
|
1228
|
+
}
|
|
1229
|
+
function round(n) {
|
|
1230
|
+
return (Math.round(n * 100) / 100).toString();
|
|
1231
|
+
}
|
|
1232
|
+
const fontCache = /* @__PURE__ */ new Map();
|
|
1233
|
+
const fontBytesCache = /* @__PURE__ */ new Map();
|
|
1234
|
+
function isDevanagari(char) {
|
|
1235
|
+
const c = char.codePointAt(0) ?? 0;
|
|
1236
|
+
return c >= 2304 && c <= 2431 || c >= 43232 && c <= 43263 || c >= 7376 && c <= 7423;
|
|
1237
|
+
}
|
|
1238
|
+
function containsDevanagari(text) {
|
|
1239
|
+
if (!text) return false;
|
|
1240
|
+
for (const char of text) {
|
|
1241
|
+
if (isDevanagari(char)) return true;
|
|
1242
|
+
}
|
|
1243
|
+
return false;
|
|
1244
|
+
}
|
|
1245
|
+
function isBasicLatinOrLatinExtended(char) {
|
|
1246
|
+
const c = char.codePointAt(0) ?? 0;
|
|
1247
|
+
return c <= 591;
|
|
1248
|
+
}
|
|
1249
|
+
function isMathOperatorChar(char) {
|
|
1250
|
+
const c = char.codePointAt(0) ?? 0;
|
|
1251
|
+
if (c >= 8592 && c <= 8703) return true;
|
|
1252
|
+
if (c >= 8704 && c <= 8959) return true;
|
|
1253
|
+
if (c >= 8960 && c <= 9215) return true;
|
|
1254
|
+
if (c >= 10176 && c <= 10223) return true;
|
|
1255
|
+
if (c >= 10624 && c <= 10751) return true;
|
|
1256
|
+
if (c >= 10752 && c <= 11007) return true;
|
|
1257
|
+
if (c >= 11008 && c <= 11097) return true;
|
|
1258
|
+
return false;
|
|
1259
|
+
}
|
|
1260
|
+
function classifyCharForFontRun(char) {
|
|
1261
|
+
if (isBasicLatinOrLatinExtended(char)) return "main";
|
|
1262
|
+
if (isDevanagari(char)) return "devanagari";
|
|
1263
|
+
if (isMathOperatorChar(char)) return "math";
|
|
1264
|
+
return "symbol";
|
|
1265
|
+
}
|
|
1266
|
+
function isIgnorableForCoverage(char) {
|
|
1267
|
+
return /\s/.test(char) || /[\u0964\u0965\u200c\u200d]/u.test(char);
|
|
1268
|
+
}
|
|
1269
|
+
function fontSupportsRun(font, text, runType) {
|
|
1270
|
+
if (!font) return false;
|
|
1271
|
+
for (const char of text) {
|
|
1272
|
+
if (isIgnorableForCoverage(char)) continue;
|
|
1273
|
+
if (runType === "devanagari" && !isDevanagari(char)) continue;
|
|
1274
|
+
if (runType === "symbol" && classifyCharForFontRun(char) !== "symbol") continue;
|
|
1275
|
+
if (runType === "math" && classifyCharForFontRun(char) !== "math") continue;
|
|
1276
|
+
const glyph = font.charToGlyph(char);
|
|
1277
|
+
if (!glyph || glyph.index === 0) return false;
|
|
1278
|
+
}
|
|
1279
|
+
return true;
|
|
1280
|
+
}
|
|
1281
|
+
const browserMeasureCanvas = typeof document !== "undefined" ? document.createElement("canvas") : null;
|
|
1282
|
+
function measureBrowserWidth(fontFamily, weight, fontSize, text) {
|
|
1283
|
+
const ctx = browserMeasureCanvas == null ? void 0 : browserMeasureCanvas.getContext("2d");
|
|
1284
|
+
if (!ctx) return null;
|
|
1285
|
+
ctx.font = `normal normal ${weight} ${fontSize}px "${fontFamily}"`;
|
|
1286
|
+
return ctx.measureText(text).width;
|
|
1287
|
+
}
|
|
1288
|
+
function uniqueFamilies(families) {
|
|
1289
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1290
|
+
const out = [];
|
|
1291
|
+
for (const family of families) {
|
|
1292
|
+
const clean = family == null ? void 0 : family.trim();
|
|
1293
|
+
if (!clean || seen.has(clean)) continue;
|
|
1294
|
+
seen.add(clean);
|
|
1295
|
+
out.push(clean);
|
|
1296
|
+
}
|
|
1297
|
+
return out;
|
|
1298
|
+
}
|
|
1299
|
+
function fontLooksItalic(font) {
|
|
1300
|
+
var _a, _b, _c, _d;
|
|
1301
|
+
if (!font) return false;
|
|
1302
|
+
const names = font.names;
|
|
1303
|
+
const candidates = [
|
|
1304
|
+
(_a = names.fontSubfamily) == null ? void 0 : _a.en,
|
|
1305
|
+
(_b = names.typographicSubfamily) == null ? void 0 : _b.en,
|
|
1306
|
+
(_c = names.fullName) == null ? void 0 : _c.en,
|
|
1307
|
+
(_d = names.postScriptName) == null ? void 0 : _d.en
|
|
1308
|
+
].filter(Boolean).join(" ");
|
|
1309
|
+
return /italic|oblique/i.test(candidates);
|
|
1310
|
+
}
|
|
1311
|
+
function syntheticItalicTransform(anchorX, baselineY) {
|
|
1312
|
+
return `translate(${anchorX} ${baselineY}) skewX(-12) translate(${-anchorX} ${-baselineY})`;
|
|
1313
|
+
}
|
|
1314
|
+
function getFontPath(fontFiles, weight, isItalic = false) {
|
|
1315
|
+
const resolved = resolveFontWeight(weight);
|
|
1316
|
+
const uprightMap = {
|
|
1317
|
+
300: ["light", "regular"],
|
|
1318
|
+
400: ["regular"],
|
|
1319
|
+
500: ["medium", "regular"],
|
|
1320
|
+
600: ["semibold", "bold"],
|
|
1321
|
+
700: ["bold", "semibold", "regular"]
|
|
1322
|
+
};
|
|
1323
|
+
const italicMap = {
|
|
1324
|
+
300: ["lightItalic", "italic", "light", "regular"],
|
|
1325
|
+
400: ["italic", "regular"],
|
|
1326
|
+
500: ["mediumItalic", "italic", "medium", "regular"],
|
|
1327
|
+
600: ["semiboldItalic", "boldItalic", "italic", "semibold", "bold", "regular"],
|
|
1328
|
+
700: ["boldItalic", "semiboldItalic", "italic", "bold", "semibold", "regular"]
|
|
1329
|
+
};
|
|
1330
|
+
const map = isItalic ? italicMap : uprightMap;
|
|
1331
|
+
for (const key of map[resolved] || ["regular"]) {
|
|
1332
|
+
if (fontFiles[key]) return fontFiles[key];
|
|
1333
|
+
}
|
|
1334
|
+
return fontFiles.regular || null;
|
|
1335
|
+
}
|
|
1336
|
+
function resolveFontUrl(fileName, fontBaseUrl) {
|
|
1337
|
+
if (/^https?:\/\//i.test(fileName) || fileName.startsWith("data:")) return fileName;
|
|
1338
|
+
if (fileName.startsWith("/")) {
|
|
1339
|
+
return typeof window !== "undefined" ? new URL(fileName, window.location.origin).toString() : fileName;
|
|
1340
|
+
}
|
|
1341
|
+
const baseUrl = fontBaseUrl.endsWith("/") ? fontBaseUrl : fontBaseUrl + "/";
|
|
1342
|
+
return new URL(fileName, baseUrl).toString();
|
|
1343
|
+
}
|
|
1344
|
+
async function loadFont(fontFamily, weight, fontBaseUrl, isItalic = false) {
|
|
1345
|
+
const cacheKey = `${fontFamily}__${weight}__${isItalic ? "i" : "n"}`;
|
|
1346
|
+
if (fontCache.has(cacheKey)) return fontCache.get(cacheKey);
|
|
1347
|
+
const fontFiles = FONT_FILES[fontFamily];
|
|
1348
|
+
if (fontFiles) {
|
|
1349
|
+
const fileName = getFontPath(fontFiles, weight, isItalic);
|
|
1350
|
+
if (!fileName) return null;
|
|
1351
|
+
const url = resolveFontUrl(fileName, fontBaseUrl);
|
|
1352
|
+
try {
|
|
1353
|
+
const response = await fetch(url);
|
|
1354
|
+
if (!response.ok) {
|
|
1355
|
+
console.warn(`[text-to-path] Failed to fetch font ${url}: ${response.status}`);
|
|
1356
|
+
return null;
|
|
1357
|
+
}
|
|
1358
|
+
const buffer = await response.arrayBuffer();
|
|
1359
|
+
const bytes = new Uint8Array(buffer);
|
|
1360
|
+
fontBytesCache.set(cacheKey, bytes);
|
|
1361
|
+
const font = opentype.parse(buffer);
|
|
1362
|
+
fontCache.set(cacheKey, font);
|
|
1363
|
+
return font;
|
|
1364
|
+
} catch (err) {
|
|
1365
|
+
console.warn(`[text-to-path] Failed to load local font ${fontFamily}:`, err);
|
|
1366
|
+
return null;
|
|
1367
|
+
}
|
|
1368
|
+
}
|
|
1369
|
+
try {
|
|
1370
|
+
const resolvedWeight = resolveFontWeight(weight);
|
|
1371
|
+
let bytes = await getGoogleFontBytes(fontFamily, resolvedWeight, isItalic);
|
|
1372
|
+
if (!bytes) {
|
|
1373
|
+
bytes = await getFontshareFontBytes(fontFamily, resolvedWeight, isItalic);
|
|
1374
|
+
}
|
|
1375
|
+
if (!bytes && isItalic) {
|
|
1376
|
+
bytes = await getGoogleFontBytes(fontFamily, resolvedWeight, false);
|
|
1377
|
+
if (!bytes) bytes = await getFontshareFontBytes(fontFamily, resolvedWeight, false);
|
|
1378
|
+
}
|
|
1379
|
+
if (!bytes) {
|
|
1380
|
+
console.warn(`[text-to-path] No TTF available for ${fontFamily} (${weight}) on Google Fonts or Fontshare`);
|
|
1381
|
+
return null;
|
|
1382
|
+
}
|
|
1383
|
+
fontBytesCache.set(cacheKey, bytes);
|
|
1384
|
+
const ab = bytes.buffer.slice(bytes.byteOffset, bytes.byteOffset + bytes.byteLength);
|
|
1385
|
+
const font = opentype.parse(ab);
|
|
1386
|
+
fontCache.set(cacheKey, font);
|
|
1387
|
+
return font;
|
|
1388
|
+
} catch (err) {
|
|
1389
|
+
console.warn(`[text-to-path] Failed to load Google font ${fontFamily}:`, err);
|
|
1390
|
+
return null;
|
|
1391
|
+
}
|
|
1392
|
+
}
|
|
1393
|
+
async function getFontBytes(fontFamily, weight, fontBaseUrl, isItalic = false) {
|
|
1394
|
+
const cacheKey = `${fontFamily}__${weight}__${isItalic ? "i" : "n"}`;
|
|
1395
|
+
if (!fontBytesCache.has(cacheKey)) {
|
|
1396
|
+
await loadFont(fontFamily, weight, fontBaseUrl, isItalic);
|
|
1397
|
+
}
|
|
1398
|
+
const bytes = fontBytesCache.get(cacheKey);
|
|
1399
|
+
return bytes ? { bytes, cacheKey } : null;
|
|
1400
|
+
}
|
|
1401
|
+
function measureRunWidth(font, text, fontSize) {
|
|
1402
|
+
try {
|
|
1403
|
+
return font.getAdvanceWidth(text, fontSize, { kerning: true });
|
|
1404
|
+
} catch {
|
|
1405
|
+
try {
|
|
1406
|
+
return font.getAdvanceWidth(text, fontSize);
|
|
1407
|
+
} catch {
|
|
1408
|
+
return text.length * fontSize * 0.5;
|
|
1409
|
+
}
|
|
1410
|
+
}
|
|
1411
|
+
}
|
|
1412
|
+
function splitByFontRun(text) {
|
|
1413
|
+
if (!text) return [];
|
|
1414
|
+
const runs = [];
|
|
1415
|
+
let buf = "";
|
|
1416
|
+
let bufType = classifyCharForFontRun(text[0]);
|
|
1417
|
+
for (const ch of text) {
|
|
1418
|
+
const chType = classifyCharForFontRun(ch);
|
|
1419
|
+
const isNeutral = /\s/.test(ch);
|
|
1420
|
+
if (chType === bufType || isNeutral) {
|
|
1421
|
+
buf += ch;
|
|
1422
|
+
} else {
|
|
1423
|
+
if (buf) runs.push({ text: buf, runType: bufType });
|
|
1424
|
+
buf = ch;
|
|
1425
|
+
bufType = chType;
|
|
1426
|
+
}
|
|
1427
|
+
}
|
|
1428
|
+
if (buf) runs.push({ text: buf, runType: bufType });
|
|
1429
|
+
return runs;
|
|
1430
|
+
}
|
|
1431
|
+
async function shapeRunToPath(run, isDeva, primaryFont, primaryBytes, devaFont, devaBytes, x, y, fontSize) {
|
|
1432
|
+
if (isDeva && devaBytes && devaFont) {
|
|
1433
|
+
try {
|
|
1434
|
+
const shaped = await shapeRunToSvgPath(
|
|
1435
|
+
devaBytes.bytes,
|
|
1436
|
+
devaBytes.cacheKey,
|
|
1437
|
+
run,
|
|
1438
|
+
x,
|
|
1439
|
+
y,
|
|
1440
|
+
fontSize,
|
|
1441
|
+
{ script: "deva", language: "hin", direction: "ltr" }
|
|
1442
|
+
);
|
|
1443
|
+
if (shaped.pathData) {
|
|
1444
|
+
const advance = measureRunWidth(devaFont, run, fontSize);
|
|
1445
|
+
return { pathData: shaped.pathData, advance };
|
|
1446
|
+
}
|
|
1447
|
+
} catch (err) {
|
|
1448
|
+
console.warn(`[text-to-path] HarfBuzz failed for "${run}":`, err);
|
|
1449
|
+
}
|
|
1450
|
+
try {
|
|
1451
|
+
const path = devaFont.getPath(run, x, y, fontSize);
|
|
1452
|
+
const d = path.toPathData(2);
|
|
1453
|
+
if (d && d !== "M0 0") return { pathData: d, advance: measureRunWidth(devaFont, run, fontSize) };
|
|
1454
|
+
} catch {
|
|
1455
|
+
}
|
|
1456
|
+
return null;
|
|
1457
|
+
}
|
|
1458
|
+
try {
|
|
1459
|
+
const path = primaryFont.getPath(run, x, y, fontSize, { kerning: true });
|
|
1460
|
+
const d = path.toPathData(2);
|
|
1461
|
+
if (d && d !== "M0 0") return { pathData: d, advance: measureRunWidth(primaryFont, run, fontSize) };
|
|
1462
|
+
return { pathData: "", advance: measureRunWidth(primaryFont, run, fontSize) };
|
|
1463
|
+
} catch (err) {
|
|
1464
|
+
console.warn(`[text-to-path] opentype getPath failed for "${run}":`, err);
|
|
1465
|
+
return null;
|
|
1466
|
+
}
|
|
1467
|
+
}
|
|
1468
|
+
function parseTranslate(transform) {
|
|
1469
|
+
if (!transform) return { tx: 0, ty: 0 };
|
|
1470
|
+
const m = transform.match(/translate\(\s*([-\d.]+)[ \s,]+([-\d.]+)\s*\)/);
|
|
1471
|
+
if (m) return { tx: parseFloat(m[1]), ty: parseFloat(m[2]) };
|
|
1472
|
+
const mm = transform.match(/matrix\(\s*([-\d.]+)[ \s,]+([-\d.]+)[ \s,]+([-\d.]+)[ \s,]+([-\d.]+)[ \s,]+([-\d.]+)[ \s,]+([-\d.]+)\s*\)/);
|
|
1473
|
+
if (mm) return { tx: parseFloat(mm[5]), ty: parseFloat(mm[6]) };
|
|
1474
|
+
return { tx: 0, ty: 0 };
|
|
1475
|
+
}
|
|
1476
|
+
function needsComplexShaping(text) {
|
|
1477
|
+
if (!text) return false;
|
|
1478
|
+
for (const ch of text) {
|
|
1479
|
+
const c = ch.codePointAt(0) ?? 0;
|
|
1480
|
+
if (c >= 2304 && c <= 2431 || c >= 43232 && c <= 43263 || c >= 7376 && c <= 7423) return true;
|
|
1481
|
+
if (c >= 2432 && c <= 3583) return true;
|
|
1482
|
+
if (c >= 3584 && c <= 4095) return true;
|
|
1483
|
+
if (c >= 4096 && c <= 4255) return true;
|
|
1484
|
+
if (c >= 6016 && c <= 6143) return true;
|
|
1485
|
+
if (c >= 1424 && c <= 1791) return true;
|
|
1486
|
+
if (c >= 1792 && c <= 1871) return true;
|
|
1487
|
+
if (c >= 12288 && c <= 40959) return true;
|
|
1488
|
+
if (c >= 44032 && c <= 55215) return true;
|
|
1489
|
+
if (c >= 126976) return true;
|
|
1490
|
+
if (c >= 8592 && c <= 8703) return true;
|
|
1491
|
+
if (c >= 8704 && c <= 8959) return true;
|
|
1492
|
+
if (c >= 8960 && c <= 9215) return true;
|
|
1493
|
+
if (c >= 10176 && c <= 10223) return true;
|
|
1494
|
+
if (c >= 10624 && c <= 10751) return true;
|
|
1495
|
+
if (c >= 10752 && c <= 11007) return true;
|
|
1496
|
+
if (c >= 11008 && c <= 11097) return true;
|
|
1497
|
+
}
|
|
1498
|
+
return false;
|
|
1499
|
+
}
|
|
1500
|
+
const DECORATIVE_OUTLINE_FAMILIES = /* @__PURE__ */ new Set([
|
|
1501
|
+
"greatvibes",
|
|
1502
|
+
"great vibes",
|
|
1503
|
+
"cinzel",
|
|
1504
|
+
"cinzeldecorative",
|
|
1505
|
+
"cinzel decorative",
|
|
1506
|
+
"pacifico",
|
|
1507
|
+
"allura",
|
|
1508
|
+
"alexbrush",
|
|
1509
|
+
"alex brush",
|
|
1510
|
+
"abrilfatface",
|
|
1511
|
+
"abril fatface",
|
|
1512
|
+
"cormorant",
|
|
1513
|
+
"cormorantgaramond",
|
|
1514
|
+
"cormorant garamond",
|
|
1515
|
+
"dancingscript",
|
|
1516
|
+
"dancing script",
|
|
1517
|
+
"sacramento",
|
|
1518
|
+
"lobster",
|
|
1519
|
+
"satisfy",
|
|
1520
|
+
"kaushanscript",
|
|
1521
|
+
"kaushan script",
|
|
1522
|
+
"parisienne",
|
|
1523
|
+
"tangerine",
|
|
1524
|
+
"yellowtail",
|
|
1525
|
+
"shadowsintolight",
|
|
1526
|
+
"shadows into light",
|
|
1527
|
+
"amaticsc",
|
|
1528
|
+
"amatic sc",
|
|
1529
|
+
"caveat",
|
|
1530
|
+
"playball",
|
|
1531
|
+
"marckscript",
|
|
1532
|
+
"marck script",
|
|
1533
|
+
"courgette"
|
|
1534
|
+
]);
|
|
1535
|
+
function isDecorativeFamily(family) {
|
|
1536
|
+
if (!family) return false;
|
|
1537
|
+
const k = family.replace(/['"]/g, "").replace(/\s+/g, " ").trim().toLowerCase();
|
|
1538
|
+
if (DECORATIVE_OUTLINE_FAMILIES.has(k)) return true;
|
|
1539
|
+
return DECORATIVE_OUTLINE_FAMILIES.has(k.replace(/\s+/g, ""));
|
|
1540
|
+
}
|
|
1541
|
+
function readResolvedFontFamily(textEl) {
|
|
1542
|
+
var _a, _b, _c;
|
|
1543
|
+
const readStyleToken = (style, prop) => {
|
|
1544
|
+
var _a2;
|
|
1545
|
+
const m = style.match(new RegExp(`${prop}\\s*:\\s*([^;]+)`, "i"));
|
|
1546
|
+
return ((_a2 = m == null ? void 0 : m[1]) == null ? void 0 : _a2.trim()) || null;
|
|
1547
|
+
};
|
|
1548
|
+
let current = textEl;
|
|
1549
|
+
while (current) {
|
|
1550
|
+
const a = (_a = current.getAttribute("font-family")) == null ? void 0 : _a.trim();
|
|
1551
|
+
if (a) return ((_b = a.split(",")[0]) == null ? void 0 : _b.replace(/['"]/g, "").trim()) || null;
|
|
1552
|
+
const s = readStyleToken(current.getAttribute("style") || "", "font-family");
|
|
1553
|
+
if (s) return ((_c = s.split(",")[0]) == null ? void 0 : _c.replace(/['"]/g, "").trim()) || null;
|
|
1554
|
+
current = current.parentElement;
|
|
1555
|
+
}
|
|
1556
|
+
return null;
|
|
1557
|
+
}
|
|
1558
|
+
async function convertDevanagariTextToPath(svgStr, fontBaseUrl, options = {}) {
|
|
1559
|
+
var _a, _b, _c, _d;
|
|
1560
|
+
const baseUrl = fontBaseUrl ?? (typeof window !== "undefined" ? window.location.origin + "/fonts/" : "/fonts/");
|
|
1561
|
+
const mode = options.mode ?? "all";
|
|
1562
|
+
const parser = new DOMParser();
|
|
1563
|
+
const doc = parser.parseFromString(svgStr, "image/svg+xml");
|
|
1564
|
+
const textEls = doc.querySelectorAll("text");
|
|
1565
|
+
let convertedCount = 0;
|
|
1566
|
+
let skippedCount = 0;
|
|
1567
|
+
for (const textEl of textEls) {
|
|
1568
|
+
if ((_a = textEl.closest) == null ? void 0 : _a.call(textEl, '[data-pd-selectable-layer="1"]')) {
|
|
1569
|
+
skippedCount++;
|
|
1570
|
+
continue;
|
|
1571
|
+
}
|
|
1572
|
+
const fullText = textEl.textContent || "";
|
|
1573
|
+
if (!fullText.trim()) continue;
|
|
1574
|
+
if (mode === "shadow-bound") {
|
|
1575
|
+
const inShadowMarker = !!textEl.closest && !!textEl.closest("g.__pdShadowRaster");
|
|
1576
|
+
let inTaggedTextbox = false;
|
|
1577
|
+
let cur = textEl.parentElement;
|
|
1578
|
+
while (cur) {
|
|
1579
|
+
if (cur.tagName.toLowerCase() === "g" && (cur.hasAttribute("data-pd-shadow-blur") || cur.hasAttribute("data-pd-text-bg"))) {
|
|
1580
|
+
inTaggedTextbox = true;
|
|
1581
|
+
break;
|
|
1582
|
+
}
|
|
1583
|
+
cur = cur.parentElement;
|
|
1584
|
+
}
|
|
1585
|
+
if (!inShadowMarker && !inTaggedTextbox) {
|
|
1586
|
+
skippedCount++;
|
|
1587
|
+
continue;
|
|
1588
|
+
}
|
|
1589
|
+
}
|
|
1590
|
+
const hasMixedStyleTspans = () => {
|
|
1591
|
+
const tspansAll = textEl.querySelectorAll("tspan");
|
|
1592
|
+
const readProp = (el, prop) => {
|
|
1593
|
+
const attr = (el.getAttribute(prop) || "").trim();
|
|
1594
|
+
if (attr) return attr;
|
|
1595
|
+
const style = el.getAttribute("style") || "";
|
|
1596
|
+
const m = style.match(new RegExp(`(?:^|;)\\s*${prop}\\s*:\\s*([^;]+)`, "i"));
|
|
1597
|
+
return m ? m[1].trim() : "";
|
|
1598
|
+
};
|
|
1599
|
+
const baseWeight = readProp(textEl, "font-weight");
|
|
1600
|
+
const baseStyle = readProp(textEl, "font-style");
|
|
1601
|
+
const baseFill = readProp(textEl, "fill");
|
|
1602
|
+
const baseDeco = readProp(textEl, "text-decoration");
|
|
1603
|
+
for (const ts of tspansAll) {
|
|
1604
|
+
const tw = readProp(ts, "font-weight");
|
|
1605
|
+
const tst = readProp(ts, "font-style");
|
|
1606
|
+
const tFill = readProp(ts, "fill");
|
|
1607
|
+
const tDeco = readProp(ts, "text-decoration");
|
|
1608
|
+
const tBg = (ts.getAttribute("style") || "").match(/background|text-background/i);
|
|
1609
|
+
if (tw && tw !== baseWeight || tst && tst !== baseStyle || tFill && tFill !== baseFill || tDeco && tDeco !== baseDeco || tBg) {
|
|
1610
|
+
return true;
|
|
1611
|
+
}
|
|
1612
|
+
}
|
|
1613
|
+
return false;
|
|
1614
|
+
};
|
|
1615
|
+
if (mode === "complex-only") {
|
|
1616
|
+
const resolvedFamily = readResolvedFontFamily(textEl);
|
|
1617
|
+
const decorative = isDecorativeFamily(resolvedFamily);
|
|
1618
|
+
if (!needsComplexShaping(fullText) && !hasMixedStyleTspans() && !decorative) {
|
|
1619
|
+
skippedCount++;
|
|
1620
|
+
continue;
|
|
1621
|
+
}
|
|
1622
|
+
}
|
|
1623
|
+
if (mode === "mixed-style-only" && !hasMixedStyleTspans()) {
|
|
1624
|
+
skippedCount++;
|
|
1625
|
+
continue;
|
|
1626
|
+
}
|
|
1627
|
+
const readStyleToken = (style, prop) => {
|
|
1628
|
+
var _a2;
|
|
1629
|
+
const m = style.match(new RegExp(`${prop}\\s*:\\s*([^;]+)`, "i"));
|
|
1630
|
+
return ((_a2 = m == null ? void 0 : m[1]) == null ? void 0 : _a2.trim()) || null;
|
|
1631
|
+
};
|
|
1632
|
+
const getAttrOrStyle = (el, attr) => {
|
|
1633
|
+
var _a2;
|
|
1634
|
+
let current = el;
|
|
1635
|
+
while (current) {
|
|
1636
|
+
const attrVal = (_a2 = current.getAttribute(attr)) == null ? void 0 : _a2.trim();
|
|
1637
|
+
if (attrVal) return attrVal;
|
|
1638
|
+
const styleVal = readStyleToken(current.getAttribute("style") || "", attr);
|
|
1639
|
+
if (styleVal) return styleVal;
|
|
1640
|
+
current = current.parentElement;
|
|
1641
|
+
}
|
|
1642
|
+
return null;
|
|
1643
|
+
};
|
|
1644
|
+
const fontFamily = (_c = (_b = getAttrOrStyle(textEl, "font-family")) == null ? void 0 : _b.split(",")[0]) == null ? void 0 : _c.replace(/['"]/g, "").trim();
|
|
1645
|
+
const fontSizeStr = getAttrOrStyle(textEl, "font-size") || "16";
|
|
1646
|
+
const fontSize = parseFloat(fontSizeStr);
|
|
1647
|
+
const fontWeightStr = getAttrOrStyle(textEl, "font-weight") || "400";
|
|
1648
|
+
const fontWeight = Number.parseInt(fontWeightStr, 10) || 400;
|
|
1649
|
+
const fillColor = getAttrOrStyle(textEl, "fill") || "#000000";
|
|
1650
|
+
const fillOpacity = getAttrOrStyle(textEl, "fill-opacity") || "1";
|
|
1651
|
+
if (!fontFamily) {
|
|
1652
|
+
skippedCount++;
|
|
1653
|
+
continue;
|
|
1654
|
+
}
|
|
1655
|
+
const primaryFont = await loadFont(fontFamily, fontWeight, baseUrl);
|
|
1656
|
+
const primaryBytes = await getFontBytes(fontFamily, fontWeight, baseUrl);
|
|
1657
|
+
const hasDeva = containsDevanagari(fullText);
|
|
1658
|
+
const hasSymbol = [...fullText].some((char) => classifyCharForFontRun(char) === "symbol");
|
|
1659
|
+
const hasMath = [...fullText].some((char) => classifyCharForFontRun(char) === "math");
|
|
1660
|
+
const devaCandidateFamilies = hasDeva ? uniqueFamilies([fontFamily, FONT_FALLBACK_DEVANAGARI, resolveDevanagariSibling(fontFamily)]) : [];
|
|
1661
|
+
const devaRunFontCache = /* @__PURE__ */ new Map();
|
|
1662
|
+
const resolveDevaFontForRun = (runText, runFontSize) => {
|
|
1663
|
+
const cacheKey = `${runText}__${runFontSize}`;
|
|
1664
|
+
const cached = devaRunFontCache.get(cacheKey);
|
|
1665
|
+
if (cached) return cached;
|
|
1666
|
+
const promise = (async () => {
|
|
1667
|
+
const browserWidth = measureBrowserWidth(fontFamily, fontWeight, runFontSize, runText);
|
|
1668
|
+
let best = null;
|
|
1669
|
+
for (const family of devaCandidateFamilies) {
|
|
1670
|
+
const font = family === fontFamily ? primaryFont : await loadFont(family, fontWeight, baseUrl);
|
|
1671
|
+
const bytes = family === fontFamily ? primaryBytes : await getFontBytes(family, fontWeight, baseUrl);
|
|
1672
|
+
if (!font || !bytes || !fontSupportsRun(font, runText, "devanagari")) continue;
|
|
1673
|
+
const width = measureRunWidth(font, runText, runFontSize);
|
|
1674
|
+
const diff = browserWidth == null ? 0 : Math.abs(width - browserWidth);
|
|
1675
|
+
if (!best || diff < best.diff) best = { family, font, bytes, diff };
|
|
1676
|
+
if (browserWidth != null && diff <= 0.25) break;
|
|
1677
|
+
}
|
|
1678
|
+
if (best) {
|
|
1679
|
+
console.log("[text-to-path] Devanagari font resolved", {
|
|
1680
|
+
requestedFamily: fontFamily,
|
|
1681
|
+
resolvedFamily: best.family,
|
|
1682
|
+
browserWidth,
|
|
1683
|
+
diff: Math.round(best.diff * 100) / 100
|
|
1684
|
+
});
|
|
1685
|
+
}
|
|
1686
|
+
return best;
|
|
1687
|
+
})();
|
|
1688
|
+
devaRunFontCache.set(cacheKey, promise);
|
|
1689
|
+
return promise;
|
|
1690
|
+
};
|
|
1691
|
+
const symbolRunFontPromise = hasSymbol ? (async () => {
|
|
1692
|
+
const symbolFont = await loadFont(FONT_FALLBACK_SYMBOLS, 400, baseUrl);
|
|
1693
|
+
const symbolBytes = await getFontBytes(FONT_FALLBACK_SYMBOLS, 400, baseUrl);
|
|
1694
|
+
return symbolFont && symbolBytes ? { family: FONT_FALLBACK_SYMBOLS, font: symbolFont, bytes: symbolBytes } : null;
|
|
1695
|
+
})() : null;
|
|
1696
|
+
const mathRunFontPromise = hasMath ? (async () => {
|
|
1697
|
+
const mathFont = await loadFont(FONT_FALLBACK_MATH, 400, baseUrl);
|
|
1698
|
+
const mathBytes = await getFontBytes(FONT_FALLBACK_MATH, 400, baseUrl);
|
|
1699
|
+
return mathFont && mathBytes ? { family: FONT_FALLBACK_MATH, font: mathFont, bytes: mathBytes } : null;
|
|
1700
|
+
})() : null;
|
|
1701
|
+
if (!primaryFont && !hasDeva && !hasSymbol && !hasMath) {
|
|
1702
|
+
console.warn(`[text-to-path] No font available for "${fontFamily}", leaving as <text>`);
|
|
1703
|
+
skippedCount++;
|
|
1704
|
+
continue;
|
|
1705
|
+
}
|
|
1706
|
+
const textAnchorRaw = (getAttrOrStyle(textEl, "text-anchor") || "start").toLowerCase();
|
|
1707
|
+
const baseTextAnchor = textAnchorRaw === "middle" ? "middle" : textAnchorRaw === "end" ? "end" : "start";
|
|
1708
|
+
const tspans = textEl.querySelectorAll("tspan");
|
|
1709
|
+
const elementsToProcess = tspans.length > 0 ? Array.from(tspans) : [textEl];
|
|
1710
|
+
const group = doc.createElementNS("http://www.w3.org/2000/svg", "g");
|
|
1711
|
+
const textTransform = textEl.getAttribute("transform");
|
|
1712
|
+
if (textTransform) group.setAttribute("transform", textTransform);
|
|
1713
|
+
const baseX = parseFloat(textEl.getAttribute("x") || "0");
|
|
1714
|
+
const baseY = parseFloat(textEl.getAttribute("y") || "0");
|
|
1715
|
+
for (const elem of elementsToProcess) {
|
|
1716
|
+
const text = elem.textContent || "";
|
|
1717
|
+
if (!text.trim()) continue;
|
|
1718
|
+
const elemX = parseFloat(elem.getAttribute("x") || String(baseX));
|
|
1719
|
+
const elemY = parseFloat(elem.getAttribute("y") || String(baseY));
|
|
1720
|
+
const elemAnchorRaw = (getAttrOrStyle(elem, "text-anchor") || baseTextAnchor).toLowerCase();
|
|
1721
|
+
const elemTextAnchor = elemAnchorRaw === "middle" ? "middle" : elemAnchorRaw === "end" ? "end" : "start";
|
|
1722
|
+
const elemTransform = elem !== textEl ? parseTranslate(elem.getAttribute("transform")) : { tx: 0, ty: 0 };
|
|
1723
|
+
let x = elemX + elemTransform.tx;
|
|
1724
|
+
const y = elemY + elemTransform.ty;
|
|
1725
|
+
const elemFill = getAttrOrStyle(elem, "fill") || fillColor;
|
|
1726
|
+
const elemFillOpacity = getAttrOrStyle(elem, "fill-opacity") || fillOpacity;
|
|
1727
|
+
const elemFontSizeStr = getAttrOrStyle(elem, "font-size") || String(fontSize);
|
|
1728
|
+
const elemFontSize = parseFloat(elemFontSizeStr);
|
|
1729
|
+
const elemWeightStr = getAttrOrStyle(elem, "font-weight") || String(fontWeight);
|
|
1730
|
+
const elemWeight = Number.parseInt(elemWeightStr, 10) || (/bold/i.test(elemWeightStr) ? 700 : fontWeight);
|
|
1731
|
+
const elemStyleRaw = (getAttrOrStyle(elem, "font-style") || "normal").toLowerCase();
|
|
1732
|
+
const elemItalic = /italic|oblique/i.test(elemStyleRaw);
|
|
1733
|
+
const sameAsPrimary = elemWeight === fontWeight && !elemItalic;
|
|
1734
|
+
const elemFont = sameAsPrimary ? primaryFont : await loadFont(fontFamily, elemWeight, baseUrl, elemItalic);
|
|
1735
|
+
const elemBytes = sameAsPrimary ? primaryBytes : await getFontBytes(fontFamily, elemWeight, baseUrl, elemItalic);
|
|
1736
|
+
const fontForElem = elemFont || primaryFont;
|
|
1737
|
+
const bytesForElem = elemBytes || primaryBytes;
|
|
1738
|
+
const runs = splitByFontRun(text);
|
|
1739
|
+
let totalAdvance = 0;
|
|
1740
|
+
let canMeasureAll = true;
|
|
1741
|
+
for (const r of runs) {
|
|
1742
|
+
if (r.runType === "devanagari") {
|
|
1743
|
+
const resolved = await resolveDevaFontForRun(r.text, elemFontSize);
|
|
1744
|
+
if (resolved) totalAdvance += measureRunWidth(resolved.font, r.text, elemFontSize);
|
|
1745
|
+
else canMeasureAll = false;
|
|
1746
|
+
} else if (r.runType === "symbol") {
|
|
1747
|
+
const resolved = await symbolRunFontPromise;
|
|
1748
|
+
if (resolved && fontSupportsRun(resolved.font, r.text, "symbol")) totalAdvance += measureRunWidth(resolved.font, r.text, elemFontSize);
|
|
1749
|
+
else canMeasureAll = false;
|
|
1750
|
+
} else if (r.runType === "math") {
|
|
1751
|
+
const resolved = await mathRunFontPromise;
|
|
1752
|
+
if (resolved && fontSupportsRun(resolved.font, r.text, "math")) totalAdvance += measureRunWidth(resolved.font, r.text, elemFontSize);
|
|
1753
|
+
else canMeasureAll = false;
|
|
1754
|
+
} else {
|
|
1755
|
+
if (fontForElem) totalAdvance += measureRunWidth(fontForElem, r.text, elemFontSize);
|
|
1756
|
+
else canMeasureAll = false;
|
|
1757
|
+
}
|
|
1758
|
+
}
|
|
1759
|
+
if (canMeasureAll) {
|
|
1760
|
+
if (elemTextAnchor === "middle") x -= totalAdvance / 2;
|
|
1761
|
+
else if (elemTextAnchor === "end") x -= totalAdvance;
|
|
1762
|
+
}
|
|
1763
|
+
let cursor = x;
|
|
1764
|
+
let elemConverted = false;
|
|
1765
|
+
for (const r of runs) {
|
|
1766
|
+
const resolvedDeva = r.runType === "devanagari" ? await resolveDevaFontForRun(r.text, elemFontSize) : null;
|
|
1767
|
+
const resolvedSymbol = r.runType === "symbol" ? await symbolRunFontPromise : null;
|
|
1768
|
+
const resolvedMath = r.runType === "math" ? await mathRunFontPromise : null;
|
|
1769
|
+
const useDeva = !!resolvedDeva;
|
|
1770
|
+
const fontForRun = (resolvedDeva == null ? void 0 : resolvedDeva.font) ?? (resolvedSymbol == null ? void 0 : resolvedSymbol.font) ?? (resolvedMath == null ? void 0 : resolvedMath.font) ?? fontForElem;
|
|
1771
|
+
const bytesForRun = (resolvedDeva == null ? void 0 : resolvedDeva.bytes) ?? (resolvedSymbol == null ? void 0 : resolvedSymbol.bytes) ?? (resolvedMath == null ? void 0 : resolvedMath.bytes) ?? bytesForElem;
|
|
1772
|
+
if (!fontForRun) continue;
|
|
1773
|
+
const result = await shapeRunToPath(
|
|
1774
|
+
r.text,
|
|
1775
|
+
!!useDeva,
|
|
1776
|
+
fontForRun,
|
|
1777
|
+
bytesForRun,
|
|
1778
|
+
(resolvedDeva == null ? void 0 : resolvedDeva.font) ?? null,
|
|
1779
|
+
(resolvedDeva == null ? void 0 : resolvedDeva.bytes) ?? null,
|
|
1780
|
+
cursor,
|
|
1781
|
+
y,
|
|
1782
|
+
elemFontSize
|
|
1783
|
+
);
|
|
1784
|
+
if (result && result.pathData) {
|
|
1785
|
+
const pathEl = doc.createElementNS("http://www.w3.org/2000/svg", "path");
|
|
1786
|
+
pathEl.setAttribute("d", result.pathData);
|
|
1787
|
+
pathEl.setAttribute("fill", elemFill);
|
|
1788
|
+
if (elemItalic && !fontLooksItalic(fontForRun)) {
|
|
1789
|
+
pathEl.setAttribute("transform", syntheticItalicTransform(cursor, y));
|
|
1790
|
+
}
|
|
1791
|
+
if (elemFillOpacity !== "1") {
|
|
1792
|
+
pathEl.setAttribute("fill-opacity", elemFillOpacity);
|
|
1793
|
+
}
|
|
1794
|
+
group.appendChild(pathEl);
|
|
1795
|
+
elemConverted = true;
|
|
1796
|
+
}
|
|
1797
|
+
if (result) cursor += result.advance;
|
|
1798
|
+
}
|
|
1799
|
+
if (elemConverted) {
|
|
1800
|
+
convertedCount++;
|
|
1801
|
+
} else {
|
|
1802
|
+
if (elem === textEl) {
|
|
1803
|
+
const clone = elem.cloneNode(true);
|
|
1804
|
+
if (textTransform) clone.removeAttribute("transform");
|
|
1805
|
+
group.appendChild(clone);
|
|
1806
|
+
} else {
|
|
1807
|
+
const newText = doc.createElementNS("http://www.w3.org/2000/svg", "text");
|
|
1808
|
+
newText.setAttribute("x", String(elemX));
|
|
1809
|
+
newText.setAttribute("y", String(elemY));
|
|
1810
|
+
if (elem.getAttribute("style")) newText.setAttribute("style", elem.getAttribute("style"));
|
|
1811
|
+
if (elem.getAttribute("font-family")) newText.setAttribute("font-family", elem.getAttribute("font-family"));
|
|
1812
|
+
if (elem.getAttribute("font-size")) newText.setAttribute("font-size", elem.getAttribute("font-size"));
|
|
1813
|
+
if (elem.getAttribute("font-weight")) newText.setAttribute("font-weight", elem.getAttribute("font-weight"));
|
|
1814
|
+
newText.textContent = text;
|
|
1815
|
+
group.appendChild(newText);
|
|
1816
|
+
}
|
|
1817
|
+
}
|
|
1818
|
+
}
|
|
1819
|
+
if (group.childNodes.length > 0) {
|
|
1820
|
+
(_d = textEl.parentNode) == null ? void 0 : _d.replaceChild(group, textEl);
|
|
1821
|
+
}
|
|
1822
|
+
}
|
|
1823
|
+
console.log(
|
|
1824
|
+
`[text-to-path] Universal outline complete: converted=${convertedCount} skipped=${skippedCount}`
|
|
1825
|
+
);
|
|
1826
|
+
return new XMLSerializer().serializeToString(doc.documentElement);
|
|
1827
|
+
}
|
|
1828
|
+
const convertAllTextToPath = convertDevanagariTextToPath;
|
|
1829
|
+
async function preloadDevanagariFont(fontBaseUrl) {
|
|
1830
|
+
const baseUrl = fontBaseUrl ?? (typeof window !== "undefined" ? window.location.origin + "/fonts/" : "/fonts/");
|
|
1831
|
+
for (const weight of [300, 400, 500, 600, 700]) {
|
|
1832
|
+
await loadFont(FONT_FALLBACK_DEVANAGARI, weight, baseUrl);
|
|
1833
|
+
}
|
|
1834
|
+
}
|
|
1835
|
+
const svgTextToPath = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
|
|
1836
|
+
__proto__: null,
|
|
1837
|
+
convertAllTextToPath,
|
|
1838
|
+
convertDevanagariTextToPath,
|
|
1839
|
+
preloadDevanagariFont
|
|
1840
|
+
}, Symbol.toStringTag, { value: "Module" }));
|
|
1841
|
+
export {
|
|
1842
|
+
FONT_FALLBACK_SYMBOLS as F,
|
|
1843
|
+
FONT_FALLBACK_MATH as a,
|
|
1844
|
+
FONT_FALLBACK_DEVANAGARI as b,
|
|
1845
|
+
isFamilyEmbedded as c,
|
|
1846
|
+
resolveBestRegisteredVariant as d,
|
|
1847
|
+
embedFontWithGoogleFallback as e,
|
|
1848
|
+
getEmbeddedJsPDFFontName as f,
|
|
1849
|
+
getEmbeddedVariantsList as g,
|
|
1850
|
+
convertAllTextToPath as h,
|
|
1851
|
+
isFontAvailable as i,
|
|
1852
|
+
resolveFontWeight as j,
|
|
1853
|
+
doesVariantSupportChar as k,
|
|
1854
|
+
pdfFonts as p,
|
|
1855
|
+
resetPdfFontRegistry as r,
|
|
1856
|
+
svgTextToPath as s
|
|
1130
1857
|
};
|
|
1131
|
-
//# sourceMappingURL=
|
|
1858
|
+
//# sourceMappingURL=svgTextToPath-V1vC0SQt.js.map
|