使用 iText 生成 PDF 时,如果我需要多次切换字体,文件会变得太大
When using iText to generate a PDF, if I need to switch fonts many times the file size becomes too large
我的 PDF 有一部分需要为其 unicode 符号使用一种字体,而该段落的其余部分应该使用不同的字体。 (类似于“1.a 2.b 3.c”,其中“1.”是 unicode symbol/font,"a" 是另一种字体)我遵循了 Bruno 在这里描述的方法: 并且可以很好地生成 PDF。问题在于,与仅使用一种字体和一个文本元素相比,PDF 的文件大小从大约 20MB 增加到大约 100MB。此部分在文档中重复使用了数千次。我想知道是否有办法减少切换字体的影响或以某种方式减小整个文档的文件大小。
样式创建伪代码:
Style style1 = new Style();
Style style2 = new Style();
PdfFont font1 = PdfFontFactory.createFont(FontProgramFactory.createFont(fontFile1), PdfEncodings.IDENTITY_H, true);
style1.setFont(font1).setFontSize(8f).setFontColor(Color.DARK_GRAY);
PdfFont font2 = PdfFontFactory.createFont(FontProgramFactory.createFont(fontFile2), "", false);
style2.setFont(font2).setFontSize(8f).setFontColor(Color.DARK_GRAY);
编写text/paragraph伪代码:
Div div = new Div().setPaddingLeft(3).setMarginBottom(0).setKeepTogether(true);
Paragraph paragraph = new Paragraph();
loop up to 25 times: {
Text unicodeText = new Text(unicodeSymbol + " ").addStyle(style1);
paragraph.add(unicodeText);
Text plainText = new Text(plainText + " ").addStyle(style2);
paragraph.add(plainText);
}
div.add(paragraph);
text/paragraph 的这篇文章被写了数千遍,构成了文档的大部分内容。基本上,该文档由数千个具有相应代码的 "buildings" 组成,并且代码具有类别。我需要将类别的索引作为 unicode 符号,然后是建筑物段落中的所有相应代码。
这是可重现的代码:
float offSet = 50;
Integer leading = 10;
DateFormat format = new SimpleDateFormat("yyyy_MM_dd_kkmmss");
String formattedDate = format.format(new Date());
String path = "/tmp/testing_pdf_"+formattedDate + ".pdf";
File targetPdfFile = new File(path);
PdfWriter writer = new PdfWriter(path, new WriterProperties().addXmpMetadata());
PdfDocument pdf = new PdfDocument(writer);
pdf.setTagged();
PageSize pageSize = PageSize.LETTER;
Document document = new Document(pdf, pageSize);
document.setMargins(offSet, offSet, offSet, offSet);
byte[] font1file = IOUtils.toByteArray(FileUtility.getInputStreamFromClassPath("fonts/Garamond-Premier-Pro-Regular.ttf"));
byte[] font2file = IOUtils.toByteArray(FileUtility.getInputStreamFromClassPath("fonts/Quivira.otf"));
PdfFont font1 = PdfFontFactory.createFont(FontProgramFactory.createFont(font1file), "", true);
PdfFont font2 = PdfFontFactory.createFont(FontProgramFactory.createFont(font2file), PdfEncodings.IDENTITY_H, true);
Style style1 = new Style().setFont(font1).setFontSize(8f).setFontColor(Color.DARK_GRAY);
Style style2 = new Style().setFont(font2).setFontSize(8f).setFontColor(Color.DARK_GRAY);
float columnGap = 5;
float columnWidth = (pageSize.getWidth() - offSet * 2 - columnGap * 2) / 3;
float columnHeight = pageSize.getHeight() - offSet * 2;
Rectangle[] columns = {
new Rectangle(offSet, offSet, columnWidth, columnHeight),
new Rectangle(offSet + columnWidth + columnGap, offSet, columnWidth, columnHeight),
new Rectangle(offSet + columnWidth * 2 + columnGap * 2, offSet, columnWidth, columnHeight)};
document.setRenderer(new ColumnDocumentRenderer(document, columns));
for (int j = 0; j < 5000; j++) {
Div div = new Div().setPaddingLeft(3).setMarginBottom(0).setKeepTogether(true);
Paragraph paragraph = new Paragraph().setFixedLeading(leading);
// StringBuilder stringBuilder = new StringBuilder();
for (int i = 0; i < 26; i++) {
paragraph.add(new Text("\u3255 ").addStyle(style2));
paragraph.add(new Text("test ").addStyle(style1));
// stringBuilder.append("\u3255 ").append(" test ");
}
// paragraph.add(stringBuilder.toString()).addStyle(style2);
div.add(paragraph);
document.add(div);
}
document.close();
在创建可重现代码时,我发现这与被标记的文档有关。如果删除将其标记为已标记的行,则会大大减小文件大小。
您还可以通过使用一种字体而不是两种字体的注释掉的字符串生成器来减小文件大小。 (注释掉 for 循环中的两个 "paragraph.add")这反映了我在代码中遇到的问题。
问题不在字体本身。问题来自于您正在创建带标签的 PDF。标记文档中有很多 PDF 对象,文件中需要很多 space。
我无法重现您的 20MB 与 100MB 结果。在我的机器上,无论是使用一种字体还是两种字体,但使用两个 Text
元素,生成的文件大小约为 44MB。
要在创建大型标记文档时压缩文件,您应该使用压缩所有 PDF 对象的完全压缩模式,而不仅仅是流。
要激活完全压缩模式,请使用 WriterProperties
创建一个 PdfWriter
实例:
PdfWriter writer = new PdfWriter(outFileName,
new WriterProperties().setFullCompressionMode(true));
此设置将我的文件大小从 >40MB 减少到 ~5MB。
请注意,您使用的是iText 7.0.x
,而7.1.x
行已经发布,现在是iText的主线,所以我建议您更新到最新版本。
我的 PDF 有一部分需要为其 unicode 符号使用一种字体,而该段落的其余部分应该使用不同的字体。 (类似于“1.a 2.b 3.c”,其中“1.”是 unicode symbol/font,"a" 是另一种字体)我遵循了 Bruno 在这里描述的方法:
样式创建伪代码:
Style style1 = new Style();
Style style2 = new Style();
PdfFont font1 = PdfFontFactory.createFont(FontProgramFactory.createFont(fontFile1), PdfEncodings.IDENTITY_H, true);
style1.setFont(font1).setFontSize(8f).setFontColor(Color.DARK_GRAY);
PdfFont font2 = PdfFontFactory.createFont(FontProgramFactory.createFont(fontFile2), "", false);
style2.setFont(font2).setFontSize(8f).setFontColor(Color.DARK_GRAY);
编写text/paragraph伪代码:
Div div = new Div().setPaddingLeft(3).setMarginBottom(0).setKeepTogether(true);
Paragraph paragraph = new Paragraph();
loop up to 25 times: {
Text unicodeText = new Text(unicodeSymbol + " ").addStyle(style1);
paragraph.add(unicodeText);
Text plainText = new Text(plainText + " ").addStyle(style2);
paragraph.add(plainText);
}
div.add(paragraph);
text/paragraph 的这篇文章被写了数千遍,构成了文档的大部分内容。基本上,该文档由数千个具有相应代码的 "buildings" 组成,并且代码具有类别。我需要将类别的索引作为 unicode 符号,然后是建筑物段落中的所有相应代码。
这是可重现的代码:
float offSet = 50;
Integer leading = 10;
DateFormat format = new SimpleDateFormat("yyyy_MM_dd_kkmmss");
String formattedDate = format.format(new Date());
String path = "/tmp/testing_pdf_"+formattedDate + ".pdf";
File targetPdfFile = new File(path);
PdfWriter writer = new PdfWriter(path, new WriterProperties().addXmpMetadata());
PdfDocument pdf = new PdfDocument(writer);
pdf.setTagged();
PageSize pageSize = PageSize.LETTER;
Document document = new Document(pdf, pageSize);
document.setMargins(offSet, offSet, offSet, offSet);
byte[] font1file = IOUtils.toByteArray(FileUtility.getInputStreamFromClassPath("fonts/Garamond-Premier-Pro-Regular.ttf"));
byte[] font2file = IOUtils.toByteArray(FileUtility.getInputStreamFromClassPath("fonts/Quivira.otf"));
PdfFont font1 = PdfFontFactory.createFont(FontProgramFactory.createFont(font1file), "", true);
PdfFont font2 = PdfFontFactory.createFont(FontProgramFactory.createFont(font2file), PdfEncodings.IDENTITY_H, true);
Style style1 = new Style().setFont(font1).setFontSize(8f).setFontColor(Color.DARK_GRAY);
Style style2 = new Style().setFont(font2).setFontSize(8f).setFontColor(Color.DARK_GRAY);
float columnGap = 5;
float columnWidth = (pageSize.getWidth() - offSet * 2 - columnGap * 2) / 3;
float columnHeight = pageSize.getHeight() - offSet * 2;
Rectangle[] columns = {
new Rectangle(offSet, offSet, columnWidth, columnHeight),
new Rectangle(offSet + columnWidth + columnGap, offSet, columnWidth, columnHeight),
new Rectangle(offSet + columnWidth * 2 + columnGap * 2, offSet, columnWidth, columnHeight)};
document.setRenderer(new ColumnDocumentRenderer(document, columns));
for (int j = 0; j < 5000; j++) {
Div div = new Div().setPaddingLeft(3).setMarginBottom(0).setKeepTogether(true);
Paragraph paragraph = new Paragraph().setFixedLeading(leading);
// StringBuilder stringBuilder = new StringBuilder();
for (int i = 0; i < 26; i++) {
paragraph.add(new Text("\u3255 ").addStyle(style2));
paragraph.add(new Text("test ").addStyle(style1));
// stringBuilder.append("\u3255 ").append(" test ");
}
// paragraph.add(stringBuilder.toString()).addStyle(style2);
div.add(paragraph);
document.add(div);
}
document.close();
在创建可重现代码时,我发现这与被标记的文档有关。如果删除将其标记为已标记的行,则会大大减小文件大小。
您还可以通过使用一种字体而不是两种字体的注释掉的字符串生成器来减小文件大小。 (注释掉 for 循环中的两个 "paragraph.add")这反映了我在代码中遇到的问题。
问题不在字体本身。问题来自于您正在创建带标签的 PDF。标记文档中有很多 PDF 对象,文件中需要很多 space。
我无法重现您的 20MB 与 100MB 结果。在我的机器上,无论是使用一种字体还是两种字体,但使用两个 Text
元素,生成的文件大小约为 44MB。
要在创建大型标记文档时压缩文件,您应该使用压缩所有 PDF 对象的完全压缩模式,而不仅仅是流。
要激活完全压缩模式,请使用 WriterProperties
创建一个 PdfWriter
实例:
PdfWriter writer = new PdfWriter(outFileName,
new WriterProperties().setFullCompressionMode(true));
此设置将我的文件大小从 >40MB 减少到 ~5MB。
请注意,您使用的是iText 7.0.x
,而7.1.x
行已经发布,现在是iText的主线,所以我建议您更新到最新版本。