2

I am inquiring about two things. First I am having an ttf file and it is located in my macair drive. I do not want to add that file into my project structure. How can i import the True_type font from it. I have tried various ways to import it my Java program. e.g. public class TextFixer {
private static String[] names = { "iksswgrg.ttf" }; //this exists on my macair drive and i want to create font from it.

  private static Map<String, Font> cache = new ConcurrentHashMap<String, Font>(names.length);           
  static {                                                                                              
    for (String name : names) {                                                                         
      cache.put(name, getFont(name));                                                                   
    }                                                                                                   
  }                                                                                                     
  public static Font getFont(String name) {                                                             
    Font font = null;                                                                                   
    if (cache != null) {                                                                                
      if ((font = cache.get(name)) != null) {                                                           
        return font;                                                                                    
      }                                                                                                 
    }                                                                                                   
    String fName = "/fonts/" + name;                                                                    
    try {                                                                                               
      InputStream is = TextFixer.class.getResourceAsStream(fName);                                      
      font = new Font("ttf", 0, 16);                                                                    
      //font = Font.createFont(Font.TRUETYPE_FONT, is);                                                 
    } catch (Exception ex) {                                                                            
      ex.printStackTrace();                                                                             
      System.err.println(fName + " not loaded.  Using serif font.");                                    
      font = new Font("serif", Font.PLAIN, 24);                                                         
    }                                                                                                   
    return font;                                                                                        
  }

2nd part is I want to create a String by using Graphics. First I need to have width that is of 130mm. The height of the displayed box will be the tallest character in the provided string. The font size is between 8 and 16. I have an enterprise project which take care of the height and size of the ttf. The problem i face is: I do not want to use swing/javafx libraries. I want to use Graphics library of Java, use Graphics2D to have a rectangle. How can i set its width to be precisely 130mm? Then I want to make that width flexible with according to the Font. I want to draw a string and the string should get adjusted/being flexible in the provided width. I am able to draw a string through g.drawString() but I am unable to see it on console. As I do not want to use Jframe or any Swing/javaFX libraries. I know this seems a bit long but I hope I have explained it well enough. I desperately need help. Please let me know if you guys can help me out here. Thanks in advance

  • Well, you should be able to load the `Font` directly from a `File` reference, [see this previous question/answer](https://stackoverflow.com/questions/5652344/how-can-i-use-a-custom-font-in-java) for examples - You will require need to know the path to the font file based on the working location of your program – MadProgrammer Jun 08 '17 at 08:11
  • You next problem is an issue between the differences between pixels and physical measurements - how long is a piece of string? You will need to know the DPI of what ever you are rendering to, only from there can you calculate the physical pixel size you will need. [This example](https://stackoverflow.com/questions/14450012/using-printerjob-to-print-an-image-graphics2d/14450329#14450329) demonstrates how to convert between cm/inches and pixels based on the DPI of the output – MadProgrammer Jun 08 '17 at 08:17
  • Once you know how many pixels that 130mm actually takes, you make use of `FontMetrics` to measure the text width (and height) - See [Measuring Text](http://docs.oracle.com/javase/tutorial/2d/text/measuringtext.html) for more details. What I would do from there is choose a "base" font size, calculate the width of the text and determine if you need to move up or down from there, adjusting the font size till you hit your target size – MadProgrammer Jun 08 '17 at 08:18
  • how long that pathe could be e.g. try { GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment(); ge.registerFont(Font.createFont(Font.TRUETYPE_FONT, new File("User/Abc/DEF/Template/A.ttf"))); // will that work? } catch (IOException|FontFormatException e) { //Handle exception } – Code_and_Coder Jun 08 '17 at 08:21
  • The path can as long as you need to be, either an absolute or relative reference will you do. You problem is about how to load the Font, it's about how to reference a file on the drive - which has many, many answers – MadProgrammer Jun 08 '17 at 08:22
  • its 72DPI. The width has to be flexible and it should not pass the provided width. If the string could be accommodated in the provided width it should be displayed. that is max width per line (I do not want to print more than one line). – Code_and_Coder Jun 08 '17 at 08:25
  • Thanks MadProgrammer. How can i display it on Console to test the various strings with accordance to font and width flexibility? I do not want to use a Swing frame to get it displayed. – Code_and_Coder Jun 08 '17 at 08:34

1 Answers1

1

First I am having an ttf file and it is located in my macair drive. I do not want to add that file into my project structure. How can i import the True_type font from it

This is more of a problem to do with "How do you reference a file on the file system" then "How do I load a font", because if you can solve the first, you can solve the second.

File fontFile = new File("some/relative/path/to/your/Font.tff");

or

File fontFile = new File("/some/absolute/path/to/your/Font.tff");

Personally, I like neither, as it it causes too much trouble (working directories, other systems, etc), I prefer to use embedded resources where possible or put the files in a common location.

For example {user.home}/AppData/Local/{application name} on Windows or on Mac you could use {user.home}/Library/Application Support/{application name}, then it doesn't matter where the program is executed from

Loading the font is relatively simple. For my example, I placed the font file in the working directory of the program

System.out.println(new File("Pacifico.ttf").exists());
Font font = Font.createFont(Font.TRUETYPE_FONT, new File("Pacifico.ttf"));

2nd part is I want to create a String by using Graphics. First I need to have width that is of 130mm. The height of the displayed box will be the tallest character in the provided string

This is much more complicated, as images are measured in pixels. In order to know how many pixels make up a given distance, we need to know the DPI of the image.

its 72DPI

Okay then, from that, we can calculate the number of pixels we need

public static double cmToPixel(double cm, double dpi) {
    return (dpi / 2.54) * cm;
}

130mm (13 cm) comes out to be 368.503937007874 @ 72dpi.

From this, we need to find the font point size for a given piece of text to fit within this range.

Now, there are a number of was you can do this, you could simply start at point 1 and perform a linear progression till you pass the range you're after. It's not exactly fast and, as you increase the size, it can become a little error prone.

I've opted for more of a divide and conquer approach

protected static int widthOfText(String text, Font font, float fontSize, Graphics2D g2d) {
    font = font.deriveFont(fontSize);
    FontMetrics fm = g2d.getFontMetrics(font);
    int textWidth = fm.stringWidth(text);
    return textWidth;
}

public static Float pointToFit(double width, String text, Font font, Graphics2D g2d, float min, float max) {
    float fontSize = min + ((max - min) / 2f);
    font = font.deriveFont(fontSize);
    FontMetrics fm = g2d.getFontMetrics(font);
    int textWidth = fm.stringWidth(text);

    if (fontSize == min || fontSize == max) {
        return fontSize;
    }

    if (textWidth < width) {
        return pointToFit(width, text, font, g2d, fontSize, max);
    } else if (textWidth > width) {
        return pointToFit(width, text, font, g2d, min, fontSize);
    }
    return fontSize;
}

Important to note, it's not perfect, but it betters a linear progression :P

With this in hand, we can start calculating the required properties we need...

String text = "Happy, Happy, Joy, Joy";
double width = cmToPixel(13.0, 72.0);

BufferedImage img = new BufferedImage(1, 1, BufferedImage.TYPE_INT_ARGB);
Graphics2D g2d = img.createGraphics();
float fontSize = pointToFit(width, text, font, g2d, 0, (float)width);

font = font.deriveFont(fontSize);
FontMetrics fm = g2d.getFontMetrics(font);
int height = fm.getHeight();
g2d.dispose();

Okay, so this creates a small (1x1) temporary image. We need Graphics context in order to calculate all the other properties. It then calculates the font point size, from it can then calculate the text height

With all that information in hand, we can get around to actually rendering the text...

img = new BufferedImage((int) Math.round(width), height, BufferedImage.TYPE_INT_ARGB);
g2d = img.createGraphics();
g2d.setFont(font);
fm = g2d.getFontMetrics();
g2d.setColor(Color.BLACK);
g2d.drawString(text, 0, fm.getAscent());
g2d.dispose();

Which will eventually output something like this...

Happy, Happy, Joy, Joy

I added the red border before I rendered the text so I could see how well it fitted.

Now, this is a really basic example, what this doesn't do is tell you when the text won't fit (ie, the point size is 1 or 0), you'll have to put traps in to catch that yourself

And, because I know you'll probably have lots of fun putting it together, my test code...

import java.awt.Color;
import java.awt.Font;
import java.awt.FontFormatException;
import java.awt.FontMetrics;
import java.awt.Graphics2D;
import java.awt.GraphicsEnvironment;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.text.NumberFormat;
import javax.imageio.ImageIO;
import javax.swing.ImageIcon;
import javax.swing.JOptionPane;

public class Test {

    public static void main(String[] args) {
        try {
            System.out.println(new File("Pacifico.ttf").exists());
            GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
            Font font = Font.createFont(Font.TRUETYPE_FONT, new File("Pacifico.ttf"));

            String text = "Happy, Happy, Joy, Joy";
            double width = cmToPixel(13.0, 72.0);
            BufferedImage img = new BufferedImage(1, 1, BufferedImage.TYPE_INT_ARGB);
            Graphics2D g2d = img.createGraphics();
            float fontSize = pointToFit(width, text, font, g2d, 0, (float) width);
            System.out.println(width);
            System.out.println(fontSize);
            font = font.deriveFont(fontSize);
            FontMetrics fm = g2d.getFontMetrics(font);
            int height = fm.getHeight();
            g2d.dispose();

            img = new BufferedImage((int) Math.round(width), height, BufferedImage.TYPE_INT_ARGB);
            g2d = img.createGraphics();
            g2d.setColor(Color.RED);
            g2d.drawRect(0, 0, img.getWidth() - 1, img.getHeight() - 1);
            g2d.setFont(font);
            fm = g2d.getFontMetrics();
            g2d.setColor(Color.BLACK);
            g2d.drawString(text, 0, fm.getAscent());
            g2d.dispose();

            JOptionPane.showConfirmDialog(null, new ImageIcon(img));
        } catch (IOException | FontFormatException e) {
            //Handle exception
        }
    }

    public static Float pointToFit(double width, String text, Font font, Graphics2D g2d) {
        return pointToFit(width, text, font, g2d, 0f, Float.MAX_VALUE);
    }

    protected static int widthOfText(String text, Font font, float fontSize, Graphics2D g2d) {
        font = font.deriveFont(fontSize);
        FontMetrics fm = g2d.getFontMetrics(font);
        int textWidth = fm.stringWidth(text);
        return textWidth;
    }

    public static Float pointToFit(double width, String text, Font font, Graphics2D g2d, float min, float max) {
        float fontSize = min + ((max - min) / 2f);
        NumberFormat nf = NumberFormat.getInstance();
        font = font.deriveFont(fontSize);
        FontMetrics fm = g2d.getFontMetrics(font);
        int textWidth = fm.stringWidth(text);

        if (fontSize == min || fontSize == max) {
            return fontSize;
        }

        if (textWidth < width) {
            return pointToFit(width, text, font, g2d, fontSize, max);
        } else if (textWidth > width) {
            return pointToFit(width, text, font, g2d, min, fontSize);
        }
        return fontSize;
    }

    public static double cmToPixel(double cm, double dpi) {
        return (dpi / 2.54) * cm;
    }

}
MadProgrammer
  • 343,457
  • 22
  • 230
  • 366
  • Truly Great. Exactly what I was looking for. Thanks a lot for the time. – Code_and_Coder Jun 08 '17 at 10:22
  • But you can delete the "GraphicsEnvironment ge", the "NumberFormat nf" and it does not make sense to return a Float object from your pointToFit methods instead of a simple float :-) – mipa Jun 08 '17 at 12:56
  • @mipa Yes, the NumberFormat is redundant, was testing. My intention for returning a Float (instead of float) was to return a null if it couldn't find a point which worked, just never got around to implementing it – MadProgrammer Jun 08 '17 at 19:50
  • @MadProgrammer A slight teaser again, I have been asked to have the Fontweight as well in the method so a return fontSize should show the weight of the font as well... I am thinking of Font enum? Will it work? – Code_and_Coder Jun 14 '17 at 13:46
  • Also I am taking the font from a file. As we know by default the font style is plain but I am interested in adding the font style as a Bold or italic and want to have it as a fontWeight parameter in the poinToFit method. – Code_and_Coder Jun 14 '17 at 13:59
  • 1
    @Code_and_Coder Just return the instance of `Font` that you used, weight, point size, the whole bunch – MadProgrammer Jun 14 '17 at 20:30