@pixldocs/canvas-renderer 0.4.3 → 0.4.5
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.cjs +104 -13
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +3 -0
- package/dist/index.js +104 -13
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -437,6 +437,9 @@ export declare function resolveTemplateData(options: ResolveOptions): Promise<Re
|
|
|
437
437
|
* CRITICAL: Also sets font-weight to "normal" because the weight is already baked into
|
|
438
438
|
* the jsPDF font name (e.g. JosefinSans-SemiBold). Without this, svg2pdf combines
|
|
439
439
|
* font-weight + font-style into "600normal" which won't match fonts registered with style "normal".
|
|
440
|
+
*
|
|
441
|
+
* For Devanagari text: splits <tspan> elements containing mixed scripts into separate
|
|
442
|
+
* <tspan> children — Latin runs use the original font, Devanagari runs use FONT_FALLBACK_DEVANAGARI.
|
|
440
443
|
*/
|
|
441
444
|
export declare function rewriteSvgFontsForJsPDF(svgStr: string): string;
|
|
442
445
|
|
package/dist/index.js
CHANGED
|
@@ -2558,13 +2558,23 @@ const clearFabricCharCache = () => {
|
|
|
2558
2558
|
};
|
|
2559
2559
|
const clearFontCacheAndRerender = (canvas) => {
|
|
2560
2560
|
clearFabricCharCache();
|
|
2561
|
-
|
|
2561
|
+
const fixTextbox = (obj) => {
|
|
2562
|
+
var _a;
|
|
2562
2563
|
if (obj instanceof fabric.Textbox) {
|
|
2564
|
+
const savedWidth = obj.width;
|
|
2563
2565
|
obj.dirty = true;
|
|
2564
2566
|
obj.initDimensions();
|
|
2567
|
+
if (savedWidth != null && Math.abs((obj.width ?? 0) - savedWidth) > 0.01) {
|
|
2568
|
+
obj.width = savedWidth;
|
|
2569
|
+
}
|
|
2570
|
+
obj.setCoords();
|
|
2571
|
+
} else if (obj instanceof fabric.Group) {
|
|
2572
|
+
(_a = obj._objects) == null ? void 0 : _a.forEach(fixTextbox);
|
|
2573
|
+
obj.dirty = true;
|
|
2565
2574
|
obj.setCoords();
|
|
2566
2575
|
}
|
|
2567
|
-
}
|
|
2576
|
+
};
|
|
2577
|
+
canvas.getObjects().forEach(fixTextbox);
|
|
2568
2578
|
canvas.requestRenderAll();
|
|
2569
2579
|
};
|
|
2570
2580
|
const ensureFontLoaded = async (fontFamily) => {
|
|
@@ -11871,8 +11881,47 @@ async function embedFontsForConfig(pdf, config, fontBaseUrl) {
|
|
|
11871
11881
|
console.log(`[pdf-fonts] Embedded ${embedded.size} font variants from config`);
|
|
11872
11882
|
return embedded;
|
|
11873
11883
|
}
|
|
11884
|
+
function isDevanagari(char) {
|
|
11885
|
+
const c = char.codePointAt(0) ?? 0;
|
|
11886
|
+
return c >= 2304 && c <= 2431 || c >= 43232 && c <= 43263 || c >= 7376 && c <= 7423;
|
|
11887
|
+
}
|
|
11888
|
+
function containsDevanagari(text) {
|
|
11889
|
+
if (!text) return false;
|
|
11890
|
+
for (const char of text) {
|
|
11891
|
+
if (isDevanagari(char)) return true;
|
|
11892
|
+
}
|
|
11893
|
+
return false;
|
|
11894
|
+
}
|
|
11895
|
+
function isBasicLatinOrLatin1(char) {
|
|
11896
|
+
const c = char.codePointAt(0) ?? 0;
|
|
11897
|
+
return c <= 591;
|
|
11898
|
+
}
|
|
11899
|
+
function classifyChar(char) {
|
|
11900
|
+
if (isBasicLatinOrLatin1(char)) return "main";
|
|
11901
|
+
if (isDevanagari(char)) return "devanagari";
|
|
11902
|
+
return "symbol";
|
|
11903
|
+
}
|
|
11904
|
+
function splitIntoRuns(text) {
|
|
11905
|
+
if (!text) return [];
|
|
11906
|
+
const runs = [];
|
|
11907
|
+
let currentType = null;
|
|
11908
|
+
let currentText = "";
|
|
11909
|
+
for (const char of text) {
|
|
11910
|
+
const type = classifyChar(char);
|
|
11911
|
+
if (type !== currentType && currentText) {
|
|
11912
|
+
runs.push({ text: currentText, runType: currentType });
|
|
11913
|
+
currentText = "";
|
|
11914
|
+
}
|
|
11915
|
+
currentType = type;
|
|
11916
|
+
currentText += char;
|
|
11917
|
+
}
|
|
11918
|
+
if (currentText && currentType) {
|
|
11919
|
+
runs.push({ text: currentText, runType: currentType });
|
|
11920
|
+
}
|
|
11921
|
+
return runs;
|
|
11922
|
+
}
|
|
11874
11923
|
function rewriteSvgFontsForJsPDF(svgStr) {
|
|
11875
|
-
var _a;
|
|
11924
|
+
var _a, _b;
|
|
11876
11925
|
const parser = new DOMParser();
|
|
11877
11926
|
const doc = parser.parseFromString(svgStr, "image/svg+xml");
|
|
11878
11927
|
const textEls = doc.querySelectorAll("text, tspan, textPath");
|
|
@@ -11893,6 +11942,17 @@ function rewriteSvgFontsForJsPDF(svgStr) {
|
|
|
11893
11942
|
}
|
|
11894
11943
|
return null;
|
|
11895
11944
|
};
|
|
11945
|
+
const resolveWeightNum = (weightRaw) => {
|
|
11946
|
+
const parsedWeight = Number.parseInt(weightRaw, 10);
|
|
11947
|
+
return Number.isFinite(parsedWeight) ? parsedWeight : /bold/i.test(weightRaw) ? 700 : /medium/i.test(weightRaw) ? 500 : /semi/i.test(weightRaw) ? 600 : /light/i.test(weightRaw) ? 300 : 400;
|
|
11948
|
+
};
|
|
11949
|
+
const buildStyleString = (existingStyle, fontName) => {
|
|
11950
|
+
const stylePairs = existingStyle.split(";").map((part) => part.trim()).filter(Boolean).filter((part) => !/^font-family\s*:/i.test(part) && !/^font-weight\s*:/i.test(part) && !/^font-style\s*:/i.test(part));
|
|
11951
|
+
stylePairs.push(`font-family: ${fontName}`);
|
|
11952
|
+
stylePairs.push(`font-weight: normal`);
|
|
11953
|
+
stylePairs.push(`font-style: normal`);
|
|
11954
|
+
return stylePairs.join("; ");
|
|
11955
|
+
};
|
|
11896
11956
|
for (const el of textEls) {
|
|
11897
11957
|
const inlineStyle = el.getAttribute("style") || "";
|
|
11898
11958
|
const rawFf = resolveInheritedValue(el, "font-family");
|
|
@@ -11901,19 +11961,50 @@ function rewriteSvgFontsForJsPDF(svgStr) {
|
|
|
11901
11961
|
if (!isFontAvailable(clean)) continue;
|
|
11902
11962
|
const weightRaw = resolveInheritedValue(el, "font-weight") || "400";
|
|
11903
11963
|
const styleRaw = resolveInheritedValue(el, "font-style") || "normal";
|
|
11904
|
-
const
|
|
11905
|
-
const weight = Number.isFinite(parsedWeight) ? parsedWeight : /bold/i.test(weightRaw) ? 700 : /medium/i.test(weightRaw) ? 500 : /semi/i.test(weightRaw) ? 600 : /light/i.test(weightRaw) ? 300 : 400;
|
|
11964
|
+
const weight = resolveWeightNum(weightRaw);
|
|
11906
11965
|
const resolved = resolveFontWeight(weight);
|
|
11907
11966
|
const isItalic = /italic|oblique/i.test(styleRaw);
|
|
11908
11967
|
const jsPdfName = getEmbeddedJsPDFFontName(clean, resolved, isItalic);
|
|
11909
|
-
el.
|
|
11910
|
-
|
|
11911
|
-
|
|
11912
|
-
|
|
11913
|
-
|
|
11914
|
-
|
|
11915
|
-
|
|
11916
|
-
|
|
11968
|
+
const directText = Array.from(el.childNodes).filter((n) => n.nodeType === 3).map((n) => n.textContent || "").join("");
|
|
11969
|
+
const hasDevanagari = containsDevanagari(directText);
|
|
11970
|
+
if (hasDevanagari && directText.length > 0) {
|
|
11971
|
+
const devanagariWeight = resolveFontWeight(weight);
|
|
11972
|
+
const devanagariJsPdfName = getEmbeddedJsPDFFontName(FONT_FALLBACK_DEVANAGARI, devanagariWeight);
|
|
11973
|
+
const symbolJsPdfName = isFontAvailable(FONT_FALLBACK_SYMBOLS) ? getEmbeddedJsPDFFontName(FONT_FALLBACK_SYMBOLS, 400) : jsPdfName;
|
|
11974
|
+
const childNodes = Array.from(el.childNodes);
|
|
11975
|
+
for (const node of childNodes) {
|
|
11976
|
+
if (node.nodeType !== 3 || !node.textContent) continue;
|
|
11977
|
+
const runs = splitIntoRuns(node.textContent);
|
|
11978
|
+
if (runs.length <= 1 && ((_b = runs[0]) == null ? void 0 : _b.runType) !== "devanagari") continue;
|
|
11979
|
+
const fragment = doc.createDocumentFragment();
|
|
11980
|
+
for (const run of runs) {
|
|
11981
|
+
const tspan = doc.createElementNS("http://www.w3.org/2000/svg", "tspan");
|
|
11982
|
+
let runFont;
|
|
11983
|
+
if (run.runType === "devanagari") {
|
|
11984
|
+
runFont = devanagariJsPdfName;
|
|
11985
|
+
} else if (run.runType === "symbol") {
|
|
11986
|
+
runFont = symbolJsPdfName;
|
|
11987
|
+
} else {
|
|
11988
|
+
runFont = jsPdfName;
|
|
11989
|
+
}
|
|
11990
|
+
tspan.setAttribute("font-family", runFont);
|
|
11991
|
+
tspan.setAttribute("font-weight", "normal");
|
|
11992
|
+
tspan.setAttribute("font-style", "normal");
|
|
11993
|
+
tspan.textContent = run.text;
|
|
11994
|
+
fragment.appendChild(tspan);
|
|
11995
|
+
}
|
|
11996
|
+
el.replaceChild(fragment, node);
|
|
11997
|
+
}
|
|
11998
|
+
el.setAttribute("font-family", jsPdfName);
|
|
11999
|
+
el.setAttribute("font-weight", "normal");
|
|
12000
|
+
el.setAttribute("font-style", "normal");
|
|
12001
|
+
el.setAttribute("style", buildStyleString(inlineStyle, jsPdfName));
|
|
12002
|
+
} else {
|
|
12003
|
+
el.setAttribute("font-family", jsPdfName);
|
|
12004
|
+
el.setAttribute("font-weight", "normal");
|
|
12005
|
+
el.setAttribute("font-style", "normal");
|
|
12006
|
+
el.setAttribute("style", buildStyleString(inlineStyle, jsPdfName));
|
|
12007
|
+
}
|
|
11917
12008
|
}
|
|
11918
12009
|
return new XMLSerializer().serializeToString(doc.documentElement);
|
|
11919
12010
|
}
|