fix: logic to construct SVG (#2)
Fixes #1 <img width="682" alt="image.png" src="attachments/6109d59a-ba6c-4614-a248-5abf5a4c2122"> Reviewed-on: #2 Co-authored-by: Aditya <adithya18062000@gmail.com> Co-committed-by: Aditya <adithya18062000@gmail.com>
This commit was merged in pull request #2.
This commit is contained in:
107
static/share.js
107
static/share.js
@@ -25,11 +25,110 @@ async function screenshotDOM(el, opts = {}) {
|
|||||||
|
|
||||||
if (background !== 'transparent') clone.style.background = background;
|
if (background !== 'transparent') clone.style.background = background;
|
||||||
if (!clone.getAttribute('xmlns')) clone.setAttribute('xmlns', 'http://www.w3.org/1999/xhtml');
|
if (!clone.getAttribute('xmlns')) clone.setAttribute('xmlns', 'http://www.w3.org/1999/xhtml');
|
||||||
|
const word = el.querySelector("h3").textContent.trim();
|
||||||
|
const phonetic = el.querySelector(".pronun").textContent.trim();
|
||||||
|
const [types, ...defs] = [...el.querySelectorAll("ol.defs li")].map(node => node.textContent.trim());
|
||||||
|
|
||||||
const xhtml = new XMLSerializer().serializeToString(clone);
|
function charsPerLine(text, maxWidth) {
|
||||||
const svg = `<svg xmlns="http://www.w3.org/2000/svg" width="${width}" height="${height}">
|
const fontSize = Math.round(width * 0.025);
|
||||||
<foreignObject x="0" y="0" width="100%" height="100%">${xhtml}</foreignObject>
|
const canvas = document.createElement("canvas");
|
||||||
</svg>`;
|
const ctx = canvas.getContext("2d");
|
||||||
|
ctx.font = `${fontSize}px Helvetica Neue, Segoe UI, Helvetica, sans-serif`;
|
||||||
|
|
||||||
|
return Math.floor(maxWidth / (ctx.measureText(text).width / text.length));
|
||||||
|
}
|
||||||
|
|
||||||
|
const renderDefs = (defs, x, y) => {
|
||||||
|
let yOffset = y;
|
||||||
|
const offset = 20;
|
||||||
|
const maxLength = charsPerLine(defs[0], width - 60);
|
||||||
|
|
||||||
|
return defs.map((def, idx) => {
|
||||||
|
if (idx > 0) {
|
||||||
|
yOffset += vOffset;
|
||||||
|
}
|
||||||
|
|
||||||
|
let part = '';
|
||||||
|
const parts = [];
|
||||||
|
const words = def.split(' ');
|
||||||
|
|
||||||
|
for (const word of words) {
|
||||||
|
if ((part + ' ' + word).trim().length <= maxLength) {
|
||||||
|
part = (part + ' ' + word).trim();
|
||||||
|
} else {
|
||||||
|
if (part) parts.push(part);
|
||||||
|
part = word;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (part) parts.push(part);
|
||||||
|
|
||||||
|
return `
|
||||||
|
<text class="num" x="0" y="${yOffset}">${idx + 1}.</text>
|
||||||
|
<text class="def" x="${x}" y="${yOffset}">
|
||||||
|
${parts.map((part, idx) => {
|
||||||
|
if (idx > 0) {
|
||||||
|
yOffset += offset;
|
||||||
|
}
|
||||||
|
return `<tspan x="${x}" y="${yOffset}">${part}</tspan>`
|
||||||
|
}).join('')}
|
||||||
|
</text>
|
||||||
|
`;
|
||||||
|
}).join('');
|
||||||
|
}
|
||||||
|
|
||||||
|
const vOffset = 25;
|
||||||
|
const svg = `
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="${width}" height="${height}" viewBox="0 0 ${width} ${height}">
|
||||||
|
<style>
|
||||||
|
.card { fill: #ffffff; stroke: #e5e7eb; stroke-width: 1.2; rx: 5; ry: 5; }
|
||||||
|
.headword {
|
||||||
|
font-size: ${Math.round(width * 0.03)}px;
|
||||||
|
font-weight: 700;
|
||||||
|
fill: #111827;
|
||||||
|
}
|
||||||
|
.pron {
|
||||||
|
font-size: ${Math.round(width * 0.025)}px;
|
||||||
|
fill: #6b7280;
|
||||||
|
}
|
||||||
|
.pos {
|
||||||
|
font-size: ${Math.round(width * 0.017)}px;
|
||||||
|
font-weight: bold;
|
||||||
|
fill: #666;
|
||||||
|
}
|
||||||
|
.def {
|
||||||
|
font-size: ${Math.round(width * 0.025)}px;
|
||||||
|
fill: #111827;
|
||||||
|
}
|
||||||
|
.num {
|
||||||
|
font-size: ${Math.round(width * 0.025)}px;
|
||||||
|
fill: #6b7280;
|
||||||
|
}
|
||||||
|
|
||||||
|
text {
|
||||||
|
font-family: "Helvetica Neue", "Segoe UI", Helvetica, sans-serif;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<!-- Card background -->
|
||||||
|
<rect x="8" y="8" width="${width - 16}" height="${height - 16}" rx="5" ry="5" class="card"/>
|
||||||
|
|
||||||
|
<!-- Content -->
|
||||||
|
<g transform="translate(32,36)">
|
||||||
|
<!-- Headword -->
|
||||||
|
<text class="headword" x="0" y="0">${word}</text>
|
||||||
|
|
||||||
|
<!-- Pronunciation -->
|
||||||
|
<text class="pron" x="0" y="20">${phonetic}</text>
|
||||||
|
|
||||||
|
<!-- POS -->
|
||||||
|
<text class="pos" x="13" y="45">${types}</text>
|
||||||
|
|
||||||
|
<!-- Definition 1 -->
|
||||||
|
${renderDefs(defs, 24, 75)}
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
`;
|
||||||
|
|
||||||
const svgBlob = new Blob([svg], { type: 'image/svg+xml;charset=utf-8' });
|
const svgBlob = new Blob([svg], { type: 'image/svg+xml;charset=utf-8' });
|
||||||
const url = URL.createObjectURL(svgBlob);
|
const url = URL.createObjectURL(svgBlob);
|
||||||
|
|||||||
Reference in New Issue
Block a user