以下代码使用batik-svggen生成SVG文件;目标是一个非常经典的目标,但似乎不可能第一次正确...正确计算一些渲染文本的高度和宽度,以便它可以被一个形状包围 - 这里是一个矩形。 / p>
使用的类如下:
public final class SvgParseNode
{
private static final int LEFTMARGIN = 5;
private static final int TOPMARGIN = 5;
private static final int RIGHTMARGIN = 5;
private static final int BOTTOMARGIN = 5;
private final Graphics2D graphics;
private final String text;
private final int xStart;
private final int yStart;
private final int rwidth;
private final int rheight;
private final int textXOffset;
private final int textYOffset;
@SuppressWarnings("ObjectToString")
public SvgParseNode(final Graphics2D graphics, final ParseNode node,
final int xStart, final int yStart)
{
this.xStart = xStart;
this.yStart = yStart;
this.graphics = Objects.requireNonNull(graphics);
text = Objects.requireNonNull(node).toString();
/*
* First, get the bounds of the text
*/
final Font font = graphics.getFont();
final FontMetrics metrics = graphics.getFontMetrics(font);
final Rectangle2D bounds = metrics.getStringBounds(text, graphics);
final int boundsHeight = (int) bounds.getHeight();
/*
* The width of the target rectangle is that of the bounds width, plus
* both left and right margin on the x axis
*/
// PROBLEM HERE
//rwidth = (int) bounds.getWidth() + LEFTMARGIN + RIGHTMARGIN;
rwidth = metrics.stringWidth(text) + LEFTMARGIN + RIGHTMARGIN;
/*
* The height is that of the bounds height, plus both up and down
* margins on the y avis
*/
rheight = boundsHeight + TOPMARGIN + BOTTOMARGIN;
/*
* The x offset of the text is that of the left x margin
*/
textXOffset = LEFTMARGIN;
/*
* The y offset is that of half of half the bounds height plus the y
* up margin
*/
textYOffset = rheight / 2 + TOPMARGIN;
}
public void render()
{
final Shape rect = new Rectangle(xStart, yStart, rwidth, rheight);
graphics.draw(rect);
graphics.drawString(text, xStart + textXOffset, yStart + textYOffset);
}
public Point getTopAttachPoint()
{
return new Point(xStart + rwidth / 2, yStart);
}
public Point getDownAttachPoint()
{
return new Point(xStart + rwidth / 2, yStart + rheight);
}
}
在上面标有PROBLEM HERE
的行上,我尝试使用.getStringBounds()
给出的宽度作为文本的宽度。运气不好,它没有返回正确的结果......但.stringWidth()
有效。
我用它来编写渲染(代码改编自here):
public final class SvgWriting
{
private static final int XSTART = 10;
private static final int YSTART = 10;
private static final Path OUTFILE;
static {
final String s = System.getProperty("java.io.tmpdir");
if (s == null)
throw new ExceptionInInitializerError("java.io.tmpdir not defined");
OUTFILE = Paths.get(s, "t2.svg");
}
private SvgWriting()
{
throw new Error("no instantiation is permitted");
}
public static void main(final String... args)
throws IOException
{
// Get a DOMImplementation.
final DOMImplementation domImpl =
GenericDOMImplementation.getDOMImplementation();
// Create an instance of org.w3c.dom.Document.
final String svgNS = "http://www.w3.org/2000/svg";
final Document document = domImpl.createDocument(svgNS, "svg", null);
// Create an instance of the SVG Generator.
final SVGGraphics2D svgGenerator = new SVGGraphics2D(document);
final SvgParseNode node = new SvgParseNode(svgGenerator,
new DummyNode(), XSTART, YSTART);
node.render();
try (
final Writer out = Files.newBufferedWriter(OUTFILE);
) {
svgGenerator.stream(out, false);
}
}
private static final class DummyNode
extends ParseNode
{
private DummyNode()
{
super("foo", Collections.emptyList());
}
@Override
public String toString()
{
return "I suck at graphics, I said!";
}
}
}
我注意到的是,文本越长,“更不正确”.getStringBounds()
似乎就越多;这让我说,不知怎的,它没有考虑每个渲染字形之间的空间,但这只是一个假设。
在上面的示例中,metrics.getStringBounds(...).getWidth()
返回~105,metrics.stringWidth()
返回111。
无论如何,这真的很奇怪;为什么.getStringBounds()
没有返回“正确”的宽度?我错过了什么?