Zeichen eines Font als Bilder

Da Icons in Android ja immer in 4-5 Größen vorliegen müssen, kam ein Kollege auf die Idee, ein entsprechendes Font einzubinden, das ne Menge Symbole mitbringt, und uns damit der ganzen Icon-Problematik zu entledigen. Klang erstmal gut.

Nun habe ich aber das Problem, das Android das so natürlich nicht unterstützt, da ich jetzt überall wo Andorid ein “Drawable” erwartet einen leeren Dummy eintrage und mein eigentliches “Icon” in den Text packe.

Irgendwie finde ich diese Lösung darum etwas umständlich, wesewegen ich jetzt einen Weg suche, die einzelnen Zeichen aus dem Font irgendwie als Vector-Grafik oder so zu exportieren, um sie eben doch als normale Icons zu nutzen. Falls es jemanden interessiert: Ich verwenden dieses Font.

Über Hilfe oder weiterführende Links würde ich mich freuen.

Wenn du 'nen Font verwendest musst du den Font der App mitgeben und die entsprechenden Zeichen in einer TextView anzeigen nachdem du den Font geladen hast. Allerdings glaube ich, dass du dir damit mehr Arbeit antust als dir Gefallen tust.

Hier hast du Beispielcode wie das geht: FontHelper von HN-News.

Na eingebunden habe ich das Font ja schon. Läuft teilweise auch. Aber, wie schon gesagt, ist es halt teilweise Extra-Arbeit, da ich damit an Android vorbei-programmiere.

Da ich aber keine Lust habe mir nach neuen Icons (und dann auch noch in allen größen) umzusehen, suche ich halt ein Programm, womit ich die Font-Zeichen in normale .png-Dateien umwandeln kann.

Habe mir jetzt beholfen, indem ich selbst ein kleines Programm geschrieben habe, dass mir das Zeichen auf ein Graphics-Objekt malt, dass ich dann als Bild abspeichere. Funktioniert wunderbar.

Lust das ganze Open-Source zu machen? Würde sich auch anderen helfen. BTW: Das von Google veröffentlichte Icon-Pack kanntest du?

[QUOTE=schlingel]BTW: Das von Google veröffentlichte Icon-Pack kanntest du?[/QUOTE]Ja, dass kannte ich. Aber das da haben mir die Icons teilweise nicht gefallen. Außerdem sind es auch nicht atemberaubend viele, sodass ich eh noch andere gebraucht hätte. Und das war ja auch nicht die Problemstellung. Ich hatte ja oben genanntes Font gefunden, dass mir eigentlich gefallen hatte. Problem war eben nur, dass es nur ein Font ist. Aber das habe ich jetzt ja auch gelöst (s.u.).

Warum nicht…

Quellcode

[spoiler]```package font2img;

import java.awt.Color;
import java.awt.Font;
import java.awt.FontFormatException;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.RenderingHints;
import java.awt.Toolkit;
import java.awt.font.FontRenderContext;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
import java.awt.image.FilteredImageSource;
import java.awt.image.ImageFilter;
import java.awt.image.ImageProducer;
import java.awt.image.RGBImageFilter;
import java.io.File;
import java.io.IOException;

import javax.imageio.ImageIO;

import org.imgscalr.Scalr;
import org.imgscalr.Scalr.Method;
import org.imgscalr.Scalr.Mode;

public class IconCreator {

private static final String iconFont     = "iconfont.ttf";

private static final int    defaultSize  = 32;

private static final Color  defaultColor = Color.BLACK;

// ------------------------
// Private Attributes

private final String        icon;

// ------------------------
// Constructors

public IconCreator(final int integer) {
    this((char) integer);
}

public IconCreator(final char character) {
    this(Character.toString(character));
}

public IconCreator(final String icon) {
    this.icon = icon;
}

// ------------------------
// Public Methods

public void toFile(final String file) throws IOException, FontFormatException {
    toFile(file, defaultSize, defaultColor);
}

public void toFile(final String file, final int size) throws IOException, FontFormatException {
    toFile(file, size, defaultColor);
}

public void toFile(final String file, final Color color) throws IOException, FontFormatException {
    toFile(file, defaultSize, color);
}

public void toFile(final String file, final int size, final Color color) throws IOException, FontFormatException {
    if ((file == null) || !file.endsWith(".png")) {
        throw new IllegalArgumentException("file has to be a .png-file! Invalid file=" + file);
    }
    
    final BufferedImage image = createImg(this.icon, size, color);
    ImageIO.write(image, "PNG", new File(file));
}

// ------------------------
// Private Methods

private static BufferedImage createImg(final String text, final int size, final Color color) throws FontFormatException, IOException {
    final int internalSize = size * 8;
    
    final Color background = createBackground(color);
    final Font font = loadFont(iconFont, internalSize);
    
    // calculate the size of the text
    final FontUtil util = new FontUtil(font, internalSize, text);
    final int width = util.getWidth();
    final int height = util.getHeight();
    
    final int xOffset = (int) ((internalSize - width) / 2.0);
    final int yOffset = (int) ((internalSize - height) / 2.0);
    
    final BufferedImage image = createImage(internalSize);
    final Graphics2D g = createGraphics(image, background);
    g.setColor(color);
    
    g.setFont(util.getFont());
    setRenderingHints(g);
    
    final int x = xOffset;
    final int y = height - yOffset - (int) (internalSize / 7.5);
    g.drawString(text, x, y);
    
    final Image transparent = makeColorTransparent(image, background);
    
    final BufferedImage result = Scalr.resize(toBufferedImage(transparent), Method.QUALITY, Mode.FIT_EXACT, size, Scalr.OP_ANTIALIAS);
    
    return result;
    
}

private static Color createBackground(final Color color) {
    return new Color(255 - color.getRed(), 255 - color.getGreen(), 255 - color.getBlue());
}

private static Graphics2D createGraphics(final BufferedImage image, final Color background) {
    final Graphics2D g = (Graphics2D) image.getGraphics();
    g.setColor(background);
    g.fillRect(0, 0, image.getWidth(), image.getHeight());
    return g;
}

private static BufferedImage createImage(final int size) {
    return new BufferedImage(size, size, BufferedImage.TYPE_INT_ARGB);
}

private static Font loadFont(final String name, final double size) throws FontFormatException, IOException {
    Font font = Font.createFont(Font.TRUETYPE_FONT, IconCreator.class.getResourceAsStream(name));
    font = font.deriveFont((float) size);
    return font;
}

private static Image makeColorTransparent(final Image image, final Color color) {
    final ImageFilter filter = new RGBImageFilter() {
        // the color we are looking for... Alpha bits are set to opaque
        public int markerRGB = color.getRGB() | 0xFF000000;
        
        @Override
        public final int filterRGB(final int x, final int y, final int rgb) {
            if ((rgb | 0xFF000000) == this.markerRGB) {
                // Mark the alpha bits as zero - transparent
                return 0x00FFFFFF & rgb;
            }
            else {
                // nothing to do
                return rgb;
            }
        }
    };
    
    final ImageProducer ip = new FilteredImageSource(image.getSource(), filter);
    final Image result = Toolkit.getDefaultToolkit().createImage(ip);
    return result;
}

private static BufferedImage toBufferedImage(final Image src) {
    final int w = src.getWidth(null);
    final int h = src.getHeight(null);
    final int type = BufferedImage.TYPE_INT_ARGB; // other options
    final BufferedImage dest = new BufferedImage(w, h, type);
    final Graphics2D g2 = dest.createGraphics();
    setRenderingHints(g2);
    g2.drawImage(src, 0, 0, null);
    g2.dispose();
    return dest;
}

private static void setRenderingHints(final Graphics2D graphics) {
    graphics.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
    graphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
    graphics.setRenderingHint(RenderingHints.KEY_COLOR_RENDERING, RenderingHints.VALUE_COLOR_RENDER_QUALITY);
    graphics.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_GASP);
    
}

// ------------------------
// Private Class

private static final class FontUtil {
    
    private final Font font;
    
    private final int  width;
    private final int  height;
    
    public FontUtil(final Font font, final int size, final String text) {
        final BufferedImage buffer = new BufferedImage(1, 1, BufferedImage.TYPE_INT_RGB);
        final Graphics2D temp = buffer.createGraphics();
        setRenderingHints(temp);
        FontRenderContext context = temp.getFontRenderContext();
        Rectangle2D bounds = font.getStringBounds(text, context);
        
        int width = (int) bounds.getWidth();
        int height = (int) bounds.getHeight();
        Font tempFont = font.deriveFont((float) size - 1);
        
        while ((width > size) || (height > size)) {
            context = temp.getFontRenderContext();
            bounds = tempFont.getStringBounds(text, context);
            
            width = (int) bounds.getWidth();
            height = (int) bounds.getHeight();
            tempFont = tempFont.deriveFont((float) tempFont.getSize() - 1);
            
        }
        
        this.font = tempFont;
        this.width = width;
        this.height = height;
    }
    
    public Font getFont() {
        return this.font;
    }
    
    public int getWidth() {
        return this.width;
    }
    
    public int getHeight() {
        return this.height;
    }
}

}



Zur erwähnen wäre noch, dass eine Version der [Scalr](http://www.thebuzzmedia.com/software/imgscalr-java-image-scaling-library/)-Bibliothek im Classpath liegen sollte, da ich in Zeile 108 diese nutze, um das Bild auf die gewünschte Größe zu verkleinern.

Die Nutzung ist ansonsten denkbar einfach:
```new IconCreator('a').toFile("C:\\myIcon.png", 32, Color.Green);```

Generiert das Zeichen, dass im Font (einstellbar über die Konstante `iconFont` in der Klasse `IconCreator`) "a" entspricht in der Größe 32x32 Pixel in grün in die Datei "C:\\myIcon.png". Die Datei muss immer eine png-Datei sein, sonst gibts ne Fehlermeldung. Die font-datei muss neben der Klasse liegen, da über ein simples "getResources" drauf zugegriffen wird.

Bei größeren Bildern (>1024) kann die Generierung etwas dauern, da ich das Bild intern erst auf ein 8faches hochskaliere, um nachher ne vernünftige Qualität zu haben. (Kann bestimmt optimiert werden).

Ich weiß, man könnte noch ein paar Sachen verbessern, aber für mich reicht es so, darum bekommt ihr auch nur diese Version. Fühlt euch frei sie für was auch immer zu gebrauchen. Fehler dürft ihr gerne ausbessern. Falls ihr den Code produktiv verwendet wäre ein Hinweis auf mich als Original-Autor wünschenswert aber nicht zwingend ;).

Ich werde obigen Code nicht pflegen oder in irgendeiner Weise Support dafür geben. Kommentare sind natürlich trotzdem gerne gesehen. :)

Hoffe damit zukünftigen Suchenden einen Hinweise gegeben zu haben. ;P