3

I have a Graphics2D object and I want to use its drawString method. I can call that method and pass a String and (x, y) positions, which is very nice. However, I also have the possibility to change the font of my Graphics2D object, using the setFont method, which expects a Font object. This is also very nice. Unfortunately this is not enough for me, because I intend to define multiple font texts to my Font object.

This is the textual representation I would like to transfer to my Font object:

font-family:"Lucida Grande", "Lucida Sans Unicode", Verdana, Arial, Helvetica, sans-serif

I have seen that I have the possibility to use the AttributedCharacterIterator interface to solve my problem, however, I am not sure how should I use that. I have seen that there is actually an implementation called AttributedString, which has a set of Attributes, but I am not sure how could I create an AttributedString object which could be used by the constructor of Font in such a way that my Graphics2D object will recognize the multiple fonts and apply them when I call drawString.

EDIT:

    public static AttributedString getFontByAttributes(String atts, String value)
    {
        String[] attributes = atts.split(",");
        AttributedString attributedString = new AttributedString(value);
        for (int attributeIndex = 0; attributeIndex < attributes.length; attributeIndex++)
        {
            attributedString.addAttribute(TextAttribute.FONT, attributes[attributeIndex].trim());
        }
        return attributedString;
    }
//...
    public void drawTitle(Graphics2D g2d)
{
//...
        g2d.drawString(getFontByAttributes(Settings.titleFontString, Settings.title).getIterator(), Settings.headerWidthOffset, Settings.headerHeightOffset);
//...
}

In the code above I have tried with Andale Mono, which is known on the system where I test this application, however, in the generated graphics, the text is drawn in Times New Roman font. Something is wrong and unfortunately I don't have a clue about what could I be missing.

Lajos Arpad
  • 64,414
  • 37
  • 100
  • 175
  • You are not using the AttributedString API correctly. You want to apply your FONT attributes to specific substrings, using the `addAttribute(String, String, int, int)` method. The method you're using sets the FONT for the whole string, overriding it entirely each time. – torquestomp Apr 17 '13 at 02:31
  • No, I intend to set the font for the whole string to be Lucida Grande. If Lucida Grande is not defined on the computer of the user, then the font of the string should be Lucida Sans Unicode and so on. But this solution overrides the font, so this doesn't solve my problem. I only thought this is a solution, because accidentally my tests didn't show the problems. – Lajos Arpad Apr 17 '13 at 02:43
  • Yes, that is an entirely different problem then. You should refer to this question ( http://stackoverflow.com/questions/2270360/how-to-find-with-java-if-a-certain-font-is-installed-correctly-on-a-machine ) to resolve your problem. – torquestomp Apr 17 '13 at 02:49
  • I cannot use the solution to the referred question. My project generates an SVG on a server, which will be downloaded and viewed by the clients on their machine. So I don't have access to the client computer when the graphics are generated and, as a result java.awt.GraphicsEnvironment.getAvailableFontFamilyNames() will not help me. – Lajos Arpad Apr 17 '13 at 02:54
  • "I intend to define multiple font texts to my Font object." Here I wanted to clarify the fact that I want to use alternative fonts in the same font object. I guess I was not clear enough, I am sorry for that. Is there any solution in Java to my problem, or should I make an ugly hack? – Lajos Arpad Apr 17 '13 at 02:58
  • If your Java code is not going to run on the client machine, and the client machine is where the problem is, then no, there is no solution in Java. You should probably repost the question more accurately. – torquestomp Apr 17 '13 at 03:01
  • I can solve the problem, I was just hoping there is a solution in Java. I don't see why "Unfortunately this is not enough for me, because I intend to define multiple font texts to my Font object." is not clear. I have a Font object and I want to assign multiple fonts to that. If the first is not defined on the client machine, then the second alternative should be used and so on. I believe you it is not clear, but I don't really understand which part is unclear. Also, I already have a solution, but that's not in Java and it is an ugly hack, I wanted to avoid that. – Lajos Arpad Apr 17 '13 at 03:13

2 Answers2

7

Basically, AttributedString provides methods to allow you to apply rendering attributes to a String. Seems obvious really.

After that, you can pass the AttributedString's AttributedCharacterIterator to Graphics2D for rendering...

enter image description here

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.font.LineMetrics;
import java.awt.font.TextAttribute;
import java.text.AttributedString;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;

public class TestAttributedString {

    public static void main(String[] args) {
        new TestAttributedString();
    }

    public TestAttributedString() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
                }

                JFrame frame = new JFrame("Testing");
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.setLayout(new BorderLayout());
                frame.add(new TestPane());
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

    public class TestPane extends JPanel {

        public TestPane() {
        }

        @Override
        public Dimension getPreferredSize() {
            return new Dimension(200, 200);
        }

        @Override
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);
            Graphics2D g2d = (Graphics2D) g.create();
            AttributedString text = new AttributedString("Bunny rabits and flying ponies");
            text.addAttribute(TextAttribute.FONT, new Font("Arial", Font.BOLD, 24), 0, "Bunny rabits".length());
            text.addAttribute(TextAttribute.FOREGROUND, Color.RED, 0, "Bunny rabits".length());

            text.addAttribute(TextAttribute.FONT, new Font("Arial", Font.BOLD & Font.ITALIC, 32), 17, 17 + "flying ponies".length());
            text.addAttribute(TextAttribute.FOREGROUND, Color.BLUE, 17, 17 + "flying ponies".length());

            FontMetrics fm = g2d.getFontMetrics();
            LineMetrics lm = fm.getLineMetrics(text.getIterator(), 0, text.getIterator().getEndIndex(), g);

            g2d.drawString(text.getIterator(), 0, (int)lm.getAscent() + lm.getHeight());
            g2d.dispose();
        }
    }

}

You can check out this for more details

MadProgrammer
  • 343,457
  • 22
  • 230
  • 366
4

Graphics2D has a drawString(AttributedCharacterIterator, int, int) method that you'll want to use.

Simply build up your AttributedString with all the different TextAttribute.FONT properties you want for each specific sub-range of the string, then call:

g2d.drawString(myAttributedString.getIterator(), x, y);
torquestomp
  • 3,304
  • 19
  • 26