为什么FontMetrics的.getStringBounds()没有返回正确的文本宽度?

时间:2016-03-13 20:02:02

标签: java svg graphics2d batik

以下代码使用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()没有返回“正确”的宽度?我错过了什么?

0 个答案:

没有答案