Mass guide editor changes #18
+29
@@ -102,6 +102,35 @@ class PdfBuilder {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Render a line made of mixed-font/color segments (e.g. bold/italic runs)
|
||||||
|
* as one continuous text object: consecutive Tj operators without an
|
||||||
|
* intervening Tm advance the cursor by each font's real glyph widths, so
|
||||||
|
* spacing matches the viewer's metrics exactly (unlike our approximate
|
||||||
|
* textWidth(), which is only used for word-wrap decisions).
|
||||||
|
*/
|
||||||
|
textRun(parts, x, yTop, size) {
|
||||||
|
const y = this.pageHeight - yTop - size;
|
||||||
|
const ops = [`BT 1 0 0 1 ${x.toFixed(2)} ${y.toFixed(2)} Tm`];
|
||||||
|
let curFont = null;
|
||||||
|
let curColor = null;
|
||||||
|
for (const part of parts) {
|
||||||
|
if (!part.text) continue;
|
||||||
|
if (part.font !== curFont) {
|
||||||
|
ops.push(`/${part.font} ${size} Tf`);
|
||||||
|
curFont = part.font;
|
||||||
|
}
|
||||||
|
if (!curColor || part.color[0] !== curColor[0] || part.color[1] !== curColor[1] || part.color[2] !== curColor[2]) {
|
||||||
|
ops.push(`${col(part.color)} rg`);
|
||||||
|
curColor = part.color;
|
||||||
|
}
|
||||||
|
const clean = String(part.text).replace(/\t/g, ' ');
|
||||||
|
ops.push(`(${esc(toLatin1(clean))}) Tj`);
|
||||||
|
}
|
||||||
|
ops.push('ET');
|
||||||
|
this.currentPage.ops.push(ops.join(' '));
|
||||||
|
}
|
||||||
|
|
||||||
rect(x, yTop, w, h, { fill = null, stroke = null, lineWidth = 1 } = {}) {
|
rect(x, yTop, w, h, { fill = null, stroke = null, lineWidth = 1 } = {}) {
|
||||||
const y = this.pageHeight - yTop - h;
|
const y = this.pageHeight - yTop - h;
|
||||||
const ops = [];
|
const ops = [];
|
||||||
|
|||||||
+12
-12
@@ -148,12 +148,12 @@ function exportPdf(ast, outDir, template = {}) {
|
|||||||
ensure(item.lineHeight);
|
ensure(item.lineHeight);
|
||||||
const textX = M + indentBase + item.indent;
|
const textX = M + indentBase + item.indent;
|
||||||
if (idx === 0 && item.prefix) pdf.text(item.prefix, textX - LIST_INDENT, y, { size: item.size, font: 'F1', color });
|
if (idx === 0 && item.prefix) pdf.text(item.prefix, textX - LIST_INDENT, y, { size: item.size, font: 'F1', color });
|
||||||
let x = textX;
|
const parts = line.map((word) => ({
|
||||||
for (const word of line) {
|
text: word.text,
|
||||||
const c = word.href ? tpl.accentColor : (item.muted ? [100, 100, 100] : color);
|
font: word.font,
|
||||||
pdf.text(word.text, x, y, { size: item.size, font: word.font, color: c });
|
color: word.href ? tpl.accentColor : (item.muted ? [100, 100, 100] : color),
|
||||||
x += pdf.textWidth(word.text, item.size, word.font);
|
}));
|
||||||
}
|
pdf.textRun(parts, textX, y, item.size);
|
||||||
y += item.lineHeight;
|
y += item.lineHeight;
|
||||||
});
|
});
|
||||||
y += 4;
|
y += 4;
|
||||||
@@ -271,12 +271,12 @@ function exportPdf(ast, outDir, template = {}) {
|
|||||||
item.lines.forEach((line, idx) => {
|
item.lines.forEach((line, idx) => {
|
||||||
const textX = M + 10 + item.indent;
|
const textX = M + 10 + item.indent;
|
||||||
if (idx === 0 && item.prefix) pdf.text(item.prefix, textX - LIST_INDENT, by, { size: item.size, font: 'F1' });
|
if (idx === 0 && item.prefix) pdf.text(item.prefix, textX - LIST_INDENT, by, { size: item.size, font: 'F1' });
|
||||||
let x = textX;
|
const parts = line.map((word) => ({
|
||||||
for (const word of line) {
|
text: word.text,
|
||||||
const c = word.href ? tpl.accentColor : (item.muted ? [100, 100, 100] : [0, 0, 0]);
|
font: word.font,
|
||||||
pdf.text(word.text, x, by, { size: item.size, font: word.font, color: c });
|
color: word.href ? tpl.accentColor : (item.muted ? [100, 100, 100] : [0, 0, 0]),
|
||||||
x += pdf.textWidth(word.text, item.size, word.font);
|
}));
|
||||||
}
|
pdf.textRun(parts, textX, by, item.size);
|
||||||
by += item.lineHeight;
|
by += item.lineHeight;
|
||||||
});
|
});
|
||||||
by += 4;
|
by += 4;
|
||||||
|
|||||||
Reference in New Issue
Block a user