使用 iText 创建超过 10000 页的 PDF
Create 10000+ pages PDF with iText
我需要在 iText (2.1.7) 中生成非常长的 PDF,其中页面由 Graphics2D
绘制。问题是,每页需要约 2MB,因此创建 10.000 页的 pdf 需要约 20GB RAM。是否有可能在 HDD 上每约 100 页刷新 com.lowagie.Text.Document
的一部分?我试过 PdfWriter.flush() 但没有用。
这是一个小代码。 运行 它与 -Xmx200m 结果 OutOfMemoryError
.
import java.awt.Color;
import java.awt.Graphics2D;
import java.io.File;
import java.io.FileOutputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import com.lowagie.text.Document;
import com.lowagie.text.Rectangle;
import com.lowagie.text.pdf.PdfContentByte;
import com.lowagie.text.pdf.PdfTemplate;
import com.lowagie.text.pdf.PdfWriter;
public class Main {
static String FILENAME = "/home/username/tmp/out.pdf";
public static void main(String[] args) {
try {
System.out.println(printMem());
Rectangle rectPage = new Rectangle(0, 0, 210, 297);
FileOutputStream fos = new FileOutputStream(new File(FILENAME));
Document d = new Document(rectPage, 0, 0, 0, 0);
PdfWriter writer = PdfWriter.getInstance(d, fos);
d.open();
int pageNo = 200;
for (int i = 0; i < pageNo; i++) {
d.newPage();
PdfContentByte cb;
PdfTemplate tp;
Graphics2D g2;
cb = writer.getDirectContent();
tp = cb.createTemplate(rectPage.getWidth(),
rectPage.getHeight());
g2 = tp.createGraphicsShapes(rectPage.getWidth(),
rectPage.getHeight());
paintRand(g2);
g2.dispose();
cb.addTemplate(tp, 0, 0);
System.out.println(i + ") " + printMem());
writer.flush();
}
d.close();
System.out.println(printMem());
System.out.println("done");
} catch (Exception e) {
System.err.println(e.getMessage());
e.printStackTrace();
} catch (OutOfMemoryError mem) {
System.err.println("OUT OF MEMORY!!!!!!! " + printMem());
mem.printStackTrace();
}
}
private static String printMem() {
final long usedMem = Runtime.getRuntime().totalMemory()
- Runtime.getRuntime().freeMemory();
String ram = "RAM: " + (usedMem / 1024 / 1024) + "MB / "
+ (Runtime.getRuntime().totalMemory() / 1024 / 1024) + "MB";
if (Runtime.getRuntime().maxMemory() != Long.MAX_VALUE) {
ram += " (max limit: " + Runtime.getRuntime().maxMemory() / 1024
/ 1024 + "MB)";
} else {
ram += " (no max limit)";
}
return ram;
}
private static Random rand = new Random();
private static List<Color> colors = new ArrayList<Color>()
{
{
add(new Color(33, 33, 33));
add(new Color(33, 33, 36));
add(new Color(33, 33, 44));
add(new Color(33, 33, 38));
}
};
private static void paintRand(Graphics2D g2) {
int count = 10000;
for (int i = 0; i < count; i++) {
g2.setColor(colors.get(rand.nextInt(colors.size())));
g2.fillOval(rand.nextInt(210), rand.nextInt(210), rand.nextInt(55), rand.nextInt(55));
}
}
}
所以我得到
117) RAM: 239MB / 271MB (max limit: 271MB)
118) RAM: 246MB / 271MB (max limit: 271MB)
119) RAM: 244MB / 271MB (max limit: 271MB)
120) RAM: 245MB / 271MB (max limit: 271MB)
121) RAM: 247MB / 271MB (max limit: 271MB)
OUT OF MEMORY!!!!!!! RAM: 242MB / 271MB (max limit: 271MB)
java.lang.OutOfMemoryError: Java heap space
at com.lowagie.text.pdf.ByteBuffer.append_i(Unknown Source)
at com.lowagie.text.pdf.ByteBuffer.append(Unknown Source)
at com.lowagie.text.pdf.ByteBuffer.formatDouble(Unknown Source)
at com.lowagie.text.pdf.ByteBuffer.append(Unknown Source)
at com.lowagie.text.pdf.ByteBuffer.append(Unknown Source)
at com.lowagie.text.pdf.PdfContentByte.HelperRGB(Unknown Source)
at com.lowagie.text.pdf.PdfContentByte.setRGBColorFill(Unknown Source)
at com.lowagie.text.pdf.PdfContentByte.setColorFill(Unknown Source)
at com.lowagie.text.pdf.PdfGraphics2D.setPaint(Unknown Source)
at com.lowagie.text.pdf.PdfGraphics2D.setFillPaint(Unknown Source)
at com.lowagie.text.pdf.PdfGraphics2D.followPath(Unknown Source)
at com.lowagie.text.pdf.PdfGraphics2D.fill(Unknown Source)
at com.lowagie.text.pdf.PdfGraphics2D.fillOval(Unknown Source)
at Main.paintRand(Main.java:121)
at Main.main(Main.java:54)
是否有像 BufferedWriter 那样在 peaces 中写入 Document 的选项?
很难理解为什么您仍然使用 iText 2.1.7。 问题的答案是"No!" 所以请运动起来升级吧
关于你的问题,你应该区分不同的问题。
请检查以下问题的答案:What is the size limit of pdf file when generated using c# code with images?
您会注意到 iText 2.1.7 不能用于创建大于 2GB 的文件。知道每个页面需要 2MB 并且您希望有 10K 页,您应该意识到您不能使用 iText 2.1.7 来实现您的目标。
您甚至尝试创建大于 PDF 1.4 固有限制的文件,因此您必须确保使用压缩的交叉引用 table,这是在 PDF 1.4 中引入的功能PDF 1.5.
此外:如果您阅读了文档,您应该意识到 iText 会在页面准备好后立即将每个页面的内容刷新到 OutputStream
。因此你的问题"Is there any possibility to flush a part of Document
each ~100 pages on HDD?"很奇怪。
您对问题的诊断是错误的:您正在创建大量 PdfTemplate
对象并将它们保存在内存中!您应该使用 releaseTemplate() 方法写入 PdfWriter
内存中保存的所有 tp
实例。这将释放大量内存。
但在你这样做之前:请做正确的事并升级!
我需要在 iText (2.1.7) 中生成非常长的 PDF,其中页面由 Graphics2D
绘制。问题是,每页需要约 2MB,因此创建 10.000 页的 pdf 需要约 20GB RAM。是否有可能在 HDD 上每约 100 页刷新 com.lowagie.Text.Document
的一部分?我试过 PdfWriter.flush() 但没有用。
这是一个小代码。 运行 它与 -Xmx200m 结果 OutOfMemoryError
.
import java.awt.Color;
import java.awt.Graphics2D;
import java.io.File;
import java.io.FileOutputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import com.lowagie.text.Document;
import com.lowagie.text.Rectangle;
import com.lowagie.text.pdf.PdfContentByte;
import com.lowagie.text.pdf.PdfTemplate;
import com.lowagie.text.pdf.PdfWriter;
public class Main {
static String FILENAME = "/home/username/tmp/out.pdf";
public static void main(String[] args) {
try {
System.out.println(printMem());
Rectangle rectPage = new Rectangle(0, 0, 210, 297);
FileOutputStream fos = new FileOutputStream(new File(FILENAME));
Document d = new Document(rectPage, 0, 0, 0, 0);
PdfWriter writer = PdfWriter.getInstance(d, fos);
d.open();
int pageNo = 200;
for (int i = 0; i < pageNo; i++) {
d.newPage();
PdfContentByte cb;
PdfTemplate tp;
Graphics2D g2;
cb = writer.getDirectContent();
tp = cb.createTemplate(rectPage.getWidth(),
rectPage.getHeight());
g2 = tp.createGraphicsShapes(rectPage.getWidth(),
rectPage.getHeight());
paintRand(g2);
g2.dispose();
cb.addTemplate(tp, 0, 0);
System.out.println(i + ") " + printMem());
writer.flush();
}
d.close();
System.out.println(printMem());
System.out.println("done");
} catch (Exception e) {
System.err.println(e.getMessage());
e.printStackTrace();
} catch (OutOfMemoryError mem) {
System.err.println("OUT OF MEMORY!!!!!!! " + printMem());
mem.printStackTrace();
}
}
private static String printMem() {
final long usedMem = Runtime.getRuntime().totalMemory()
- Runtime.getRuntime().freeMemory();
String ram = "RAM: " + (usedMem / 1024 / 1024) + "MB / "
+ (Runtime.getRuntime().totalMemory() / 1024 / 1024) + "MB";
if (Runtime.getRuntime().maxMemory() != Long.MAX_VALUE) {
ram += " (max limit: " + Runtime.getRuntime().maxMemory() / 1024
/ 1024 + "MB)";
} else {
ram += " (no max limit)";
}
return ram;
}
private static Random rand = new Random();
private static List<Color> colors = new ArrayList<Color>()
{
{
add(new Color(33, 33, 33));
add(new Color(33, 33, 36));
add(new Color(33, 33, 44));
add(new Color(33, 33, 38));
}
};
private static void paintRand(Graphics2D g2) {
int count = 10000;
for (int i = 0; i < count; i++) {
g2.setColor(colors.get(rand.nextInt(colors.size())));
g2.fillOval(rand.nextInt(210), rand.nextInt(210), rand.nextInt(55), rand.nextInt(55));
}
}
}
所以我得到
117) RAM: 239MB / 271MB (max limit: 271MB)
118) RAM: 246MB / 271MB (max limit: 271MB)
119) RAM: 244MB / 271MB (max limit: 271MB)
120) RAM: 245MB / 271MB (max limit: 271MB)
121) RAM: 247MB / 271MB (max limit: 271MB)
OUT OF MEMORY!!!!!!! RAM: 242MB / 271MB (max limit: 271MB)
java.lang.OutOfMemoryError: Java heap space
at com.lowagie.text.pdf.ByteBuffer.append_i(Unknown Source)
at com.lowagie.text.pdf.ByteBuffer.append(Unknown Source)
at com.lowagie.text.pdf.ByteBuffer.formatDouble(Unknown Source)
at com.lowagie.text.pdf.ByteBuffer.append(Unknown Source)
at com.lowagie.text.pdf.ByteBuffer.append(Unknown Source)
at com.lowagie.text.pdf.PdfContentByte.HelperRGB(Unknown Source)
at com.lowagie.text.pdf.PdfContentByte.setRGBColorFill(Unknown Source)
at com.lowagie.text.pdf.PdfContentByte.setColorFill(Unknown Source)
at com.lowagie.text.pdf.PdfGraphics2D.setPaint(Unknown Source)
at com.lowagie.text.pdf.PdfGraphics2D.setFillPaint(Unknown Source)
at com.lowagie.text.pdf.PdfGraphics2D.followPath(Unknown Source)
at com.lowagie.text.pdf.PdfGraphics2D.fill(Unknown Source)
at com.lowagie.text.pdf.PdfGraphics2D.fillOval(Unknown Source)
at Main.paintRand(Main.java:121)
at Main.main(Main.java:54)
是否有像 BufferedWriter 那样在 peaces 中写入 Document 的选项?
很难理解为什么您仍然使用 iText 2.1.7。 问题的答案是"No!" 所以请运动起来升级吧
关于你的问题,你应该区分不同的问题。
请检查以下问题的答案:What is the size limit of pdf file when generated using c# code with images?
您会注意到 iText 2.1.7 不能用于创建大于 2GB 的文件。知道每个页面需要 2MB 并且您希望有 10K 页,您应该意识到您不能使用 iText 2.1.7 来实现您的目标。
您甚至尝试创建大于 PDF 1.4 固有限制的文件,因此您必须确保使用压缩的交叉引用 table,这是在 PDF 1.4 中引入的功能PDF 1.5.
此外:如果您阅读了文档,您应该意识到 iText 会在页面准备好后立即将每个页面的内容刷新到 OutputStream
。因此你的问题"Is there any possibility to flush a part of Document
each ~100 pages on HDD?"很奇怪。
您对问题的诊断是错误的:您正在创建大量 PdfTemplate
对象并将它们保存在内存中!您应该使用 releaseTemplate() 方法写入 PdfWriter
内存中保存的所有 tp
实例。这将释放大量内存。
但在你这样做之前:请做正确的事并升级!