PDFKit 在使用 for 循环时将文本拆分为两个相等的列
PDFKit split text into two equal columns while using for loop
我正在尝试使用 PDFKit
来生成一个简单的 pdf,在大多数情况下,pdf 可以工作,但尽管以一种非常无用的方式,但我拥有的是一个甲板建筑 API在许多卡片中,我想将这些对象中的每一个导出为 pdf,就像显示它们的名称一样简单,但事实上,pdf 一次只呈现一张卡片,而且只在一行上,我喜欢什么发生的事情是让它把文本分成几列,这样它看起来就和这个类似。
column 1 | column 2
c1 c8
c2 c9
c3 c10
c4 c(n)
到目前为止,这是我的代码,
module.exports = asyncHandler(async (req, res, next) => {
try {
// find the deck
const deck = await Deck.findById(req.params.deckId);
// need to sort cards by name
await deck.cards.sort((a, b) => {
if (a.name < b.name) {
return -1;
} else if (a.name > b.name) {
return 1;
} else {
return 0;
}
});
// Create a new PDF document
const doc = new PDFDocument();
// Pipe its output somewhere, like to a file or HTTP response
doc.pipe(
fs.createWriteStream(
`${__dirname}/../../public/pdf/${deck.deck_name}.pdf`
)
);
// Embed a font, set the font size, and render some text
doc.fontSize(25).text(`${deck.deck_name} Deck List`, {
align: "center",
underline: true,
underlineColor: "#000000",
underlineThickness: 2,
});
// We need to create two columns for the cards
// The first column will be the card name
// The second column will continue the cards listed
const section = doc.struct("P");
doc.addStructure(section);
for (const card of deck.cards) {
doc.text(`${card.name}`, {
color: "#000000",
fontSize: 10,
columns: 2,
columnGap: 10,
continued: true,
});
}
section.end();
// finalize the PDF and end the response
doc.end();
res.status(200).json({ message: "PDF generated successfully" });
} catch (error) {
console.error(error);
res.status(500).json({
success: false,
message: `Server Error - ${error.message}`,
});
}
});
目前这确实会生成我想要的列顺序,但是这个解决方案有一个极端的警告,那就是,如果卡片文本不是很长,下一张卡片将在同一行开始,它会如果我能找到一种方法让文本占据该行的整个宽度,那将很有用,但我还没有看到任何相关的东西。
我认为问题在于您依赖于 PDFKit 的文本“流动”API/logic,并且当两张卡片不够大而无法流动 你的列,你在一列中得到两张卡片。
我会说你真正想要的是创建一个 table——基于你的初始文本样本。
PDFKit 还没有 table API,所以你必须自己补一个。
这是一种计算事物维度的方法:
- 页面大小
- 文本单元格的大小(您自己手动选择,或使用 PDFKit 告诉您某段文本有多大)
- 边距
然后您使用这些大小来计算您的文本的行数和列数可以适合您的页面。
最后你遍历每一页的列然后行,将文本写入那些column-by-row“坐标”(我通过“偏移量”跟踪并用于计算最终“位置”)。
const PDFDocument = require('pdfkit');
const fs = require('fs');
// Create mock-up Cards for OP
const cards = [];
for (let i = 0; i < 100; i++) {
cards.push(`Card ${i + 1}`);
}
// Set a sensible starting point for each page
const originX = 50;
const originY = 50;
const doc = new PDFDocument({ size: 'LETTER' });
// Define row height and column widths, based on font size; either manually,
// or use commented-out heightOf and widthOf methods to dynamically pick sizes
doc.fontSize(24);
const rowH = 50; // doc.heightOfString(cards[cards.length - 1]);
const colW = 150; // doc.widthOfString(cards[cards.length - 1]); // because the last card is the "longest" piece of text
// Margins aren't really discussed in the documentation; I can ignore the top and left margin by
// placing the text at (0,0), but I cannot write below the bottom margin
const pageH = doc.page.height;
const rowsPerPage = parseInt((pageH - originY - doc.page.margins.bottom) / rowH);
const colsPerPage = 2;
var cardIdx = 0;
while (cardIdx < cards.length) {
var colOffset = 0;
while (colOffset < colsPerPage) {
const posX = originX + (colOffset * colW);
var rowOffset = 0;
while (rowOffset < rowsPerPage) {
const posY = originY + (rowOffset * rowH);
doc.text(cards[cardIdx], posX, posY);
cardIdx += 1;
rowOffset += 1;
}
colOffset += 1;
}
// This is hacky, but PDFKit adds a page by default so the loop doesn't 100% control when a page is added;
// this prevents an empty trailing page from being added
if (cardIdx < cards.length) {
doc.addPage();
}
}
// Finalize PDF file
doc.pipe(fs.createWriteStream('output.pdf'));
doc.end();
当我 运行 得到一个 4 页的 PDF,如下所示:
改变 colW = 250
和 colsPerPage = 3
:
我正在尝试使用 PDFKit
来生成一个简单的 pdf,在大多数情况下,pdf 可以工作,但尽管以一种非常无用的方式,但我拥有的是一个甲板建筑 API在许多卡片中,我想将这些对象中的每一个导出为 pdf,就像显示它们的名称一样简单,但事实上,pdf 一次只呈现一张卡片,而且只在一行上,我喜欢什么发生的事情是让它把文本分成几列,这样它看起来就和这个类似。
column 1 | column 2
c1 c8
c2 c9
c3 c10
c4 c(n)
到目前为止,这是我的代码,
module.exports = asyncHandler(async (req, res, next) => {
try {
// find the deck
const deck = await Deck.findById(req.params.deckId);
// need to sort cards by name
await deck.cards.sort((a, b) => {
if (a.name < b.name) {
return -1;
} else if (a.name > b.name) {
return 1;
} else {
return 0;
}
});
// Create a new PDF document
const doc = new PDFDocument();
// Pipe its output somewhere, like to a file or HTTP response
doc.pipe(
fs.createWriteStream(
`${__dirname}/../../public/pdf/${deck.deck_name}.pdf`
)
);
// Embed a font, set the font size, and render some text
doc.fontSize(25).text(`${deck.deck_name} Deck List`, {
align: "center",
underline: true,
underlineColor: "#000000",
underlineThickness: 2,
});
// We need to create two columns for the cards
// The first column will be the card name
// The second column will continue the cards listed
const section = doc.struct("P");
doc.addStructure(section);
for (const card of deck.cards) {
doc.text(`${card.name}`, {
color: "#000000",
fontSize: 10,
columns: 2,
columnGap: 10,
continued: true,
});
}
section.end();
// finalize the PDF and end the response
doc.end();
res.status(200).json({ message: "PDF generated successfully" });
} catch (error) {
console.error(error);
res.status(500).json({
success: false,
message: `Server Error - ${error.message}`,
});
}
});
目前这确实会生成我想要的列顺序,但是这个解决方案有一个极端的警告,那就是,如果卡片文本不是很长,下一张卡片将在同一行开始,它会如果我能找到一种方法让文本占据该行的整个宽度,那将很有用,但我还没有看到任何相关的东西。
我认为问题在于您依赖于 PDFKit 的文本“流动”API/logic,并且当两张卡片不够大而无法流动 你的列,你在一列中得到两张卡片。
我会说你真正想要的是创建一个 table——基于你的初始文本样本。
PDFKit 还没有 table API,所以你必须自己补一个。
这是一种计算事物维度的方法:
- 页面大小
- 文本单元格的大小(您自己手动选择,或使用 PDFKit 告诉您某段文本有多大)
- 边距
然后您使用这些大小来计算您的文本的行数和列数可以适合您的页面。
最后你遍历每一页的列然后行,将文本写入那些column-by-row“坐标”(我通过“偏移量”跟踪并用于计算最终“位置”)。
const PDFDocument = require('pdfkit');
const fs = require('fs');
// Create mock-up Cards for OP
const cards = [];
for (let i = 0; i < 100; i++) {
cards.push(`Card ${i + 1}`);
}
// Set a sensible starting point for each page
const originX = 50;
const originY = 50;
const doc = new PDFDocument({ size: 'LETTER' });
// Define row height and column widths, based on font size; either manually,
// or use commented-out heightOf and widthOf methods to dynamically pick sizes
doc.fontSize(24);
const rowH = 50; // doc.heightOfString(cards[cards.length - 1]);
const colW = 150; // doc.widthOfString(cards[cards.length - 1]); // because the last card is the "longest" piece of text
// Margins aren't really discussed in the documentation; I can ignore the top and left margin by
// placing the text at (0,0), but I cannot write below the bottom margin
const pageH = doc.page.height;
const rowsPerPage = parseInt((pageH - originY - doc.page.margins.bottom) / rowH);
const colsPerPage = 2;
var cardIdx = 0;
while (cardIdx < cards.length) {
var colOffset = 0;
while (colOffset < colsPerPage) {
const posX = originX + (colOffset * colW);
var rowOffset = 0;
while (rowOffset < rowsPerPage) {
const posY = originY + (rowOffset * rowH);
doc.text(cards[cardIdx], posX, posY);
cardIdx += 1;
rowOffset += 1;
}
colOffset += 1;
}
// This is hacky, but PDFKit adds a page by default so the loop doesn't 100% control when a page is added;
// this prevents an empty trailing page from being added
if (cardIdx < cards.length) {
doc.addPage();
}
}
// Finalize PDF file
doc.pipe(fs.createWriteStream('output.pdf'));
doc.end();
当我 运行 得到一个 4 页的 PDF,如下所示:
改变 colW = 250
和 colsPerPage = 3
: