问题
I've just had to implement the creation of a PNG Image, rendering out the contents of a text file. Searching online I found a few implementations using Android but no complete example for multi-line text using standard Java only so thought it would be worth posting my solution here.
The requirements were:
Take a String of potentially any size and render it out with properly formatted paragraphs to fit into PNG images, splitting the String into lines and paragraphs properly. If the rendered document does not fit into one page then generate multiple BufferedImages, one for each page.
回答1:
I found some sample code in the Java documentation for rendering out a paragraph, building on that I made the following:
private static final Font FONT = new Font("Serif", Font.PLAIN, 14);
private static final float PARAGRAPH_BREAK = 10;
private static final float MARGIN = 20;
private Graphics2D setupGraphics(BufferedImage img) {
Graphics2D g2d = img.createGraphics();
g2d.setFont(FONT);
g2d.fillRect(0, 0, img.getWidth(), img.getHeight());
g2d.setColor(Color.BLACK);
return g2d;
}
private List<BufferedImage> renderText(String str, int width, int height) {
String[] paragraphs = str.split("\n");
List<BufferedImage> images = new ArrayList<>();
BufferedImage img = new BufferedImage(width,
height,
BufferedImage.TYPE_3BYTE_BGR);
images.add(img);
Graphics2D g2d = setupGraphics(img);
float drawPosY = 0;
for (int paragraph=0;paragraph<paragraphs.length;paragraph++) {
drawPosY += PARAGRAPH_BREAK;
AttributedString attStr = new AttributedString(paragraphs[paragraph]);
AttributedCharacterIterator it = attStr.getIterator();
LineBreakMeasurer measurer = new LineBreakMeasurer(it, g2d.getFontRenderContext());
measurer.setPosition(it.getBeginIndex());
while (measurer.getPosition() < it.getEndIndex()) {
TextLayout layout = measurer.nextLayout(img.getWidth()-MARGIN*2);
if (drawPosY > img.getHeight() - layout.getAscent() - layout.getDescent() - layout.getLeading()) {
drawPosY = 0;
img = new BufferedImage((int)(
width,
height,
BufferedImage.TYPE_3BYTE_BGR);
images.add(img);
g2d.dispose();
g2d = setupGraphics(img);
}
drawPosY += layout.getAscent();
layout.draw(g2d, MARGIN, drawPosY);
drawPosY += layout.getDescent()+layout.getLeading();
}
}
g2d.dispose();
return images;
}
In my case I needed the generated PNG in memory so I created it as follows:
try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
ImageIO.write(output, "png", baos);
ret.setImageData(baos.toByteArray());
} catch (IOException ex) {
Logger.getLogger(ImageGenerationService.class.getName()).log(Level.SEVERE, null, ex);
return null;
}
Almost identical code in this section would cause ImageIO to write out the file different formats (for example "jpg" instead of "png") or write the image to a file (using FileOutputStream instead of ByteArrayOutputStream).
I hope this helps anyone else with the same problem.
来源:https://stackoverflow.com/questions/20702201/rendering-multi-line-multi-page-formatted-text-to-a-bufferedimage-not-in-androi