package edu.berkeley.cs.nlp.ocular.image; import edu.berkeley.cs.nlp.ocular.data.textreader.Charset; import edu.berkeley.cs.nlp.ocular.image.ImageUtils.PixelType; import tberg.murphy.indexer.Indexer; import java.awt.Color; import java.awt.Font; import java.awt.FontMetrics; import java.awt.Graphics2D; import java.awt.GraphicsEnvironment; import java.awt.RenderingHints; import java.awt.geom.AffineTransform; import java.awt.geom.Rectangle2D; import java.awt.image.BufferedImage; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Set; import static edu.berkeley.cs.nlp.ocular.data.textreader.Charset.unescapeChar; /** * @author Taylor Berg-Kirkpatrick (tberg@eecs.berkeley.edu) */ public class FontRenderer { private static Set<String> OUTLAWED_FONTS; static { OUTLAWED_FONTS = new HashSet<String>(); OUTLAWED_FONTS.add("Vemana2000"); OUTLAWED_FONTS.add("utkal"); OUTLAWED_FONTS.add("Untitled1"); OUTLAWED_FONTS.add("Symbol"); OUTLAWED_FONTS.add("Standard Symbols L"); OUTLAWED_FONTS.add("Saab"); OUTLAWED_FONTS.add("Pothana2000"); OUTLAWED_FONTS.add("OpenSymbol"); OUTLAWED_FONTS.add("mry_KacstQurn"); OUTLAWED_FONTS.add("Mallige"); OUTLAWED_FONTS.add("Lohit Tamil"); OUTLAWED_FONTS.add("Lohit Punjabi"); OUTLAWED_FONTS.add("Lohit Hindi"); OUTLAWED_FONTS.add("Lohit Gujarati"); OUTLAWED_FONTS.add("Lohit Bengali"); OUTLAWED_FONTS.add("Kedage"); OUTLAWED_FONTS.add("KacstTitleL"); OUTLAWED_FONTS.add("KacstTitle"); OUTLAWED_FONTS.add("KacstScreen"); OUTLAWED_FONTS.add("KacstQurn"); OUTLAWED_FONTS.add("KacstPoster"); OUTLAWED_FONTS.add("KacstPen"); OUTLAWED_FONTS.add("KacstOne"); OUTLAWED_FONTS.add("KacstOffice"); OUTLAWED_FONTS.add("KacstNaskh"); OUTLAWED_FONTS.add("KacstLetter"); OUTLAWED_FONTS.add("KacstFarsi"); OUTLAWED_FONTS.add("KacstDigital"); OUTLAWED_FONTS.add("KacstDecorative"); OUTLAWED_FONTS.add("KacstBook"); OUTLAWED_FONTS.add("KacstArt"); OUTLAWED_FONTS.add("GFS Solomos"); OUTLAWED_FONTS.add("GFS Porson"); OUTLAWED_FONTS.add("GFS Olga"); OUTLAWED_FONTS.add("GFS Gazis"); OUTLAWED_FONTS.add("GFS Didot Classic"); OUTLAWED_FONTS.add("GFS Baskerville"); OUTLAWED_FONTS.add("Dingbats"); OUTLAWED_FONTS.add("GFS BodoniClassic"); OUTLAWED_FONTS.add("Webdings"); OUTLAWED_FONTS.add("Te X Gyre Chorus"); OUTLAWED_FONTS.add("URW Chancery L"); OUTLAWED_FONTS.add("TeXGyreChorus"); OUTLAWED_FONTS.add("Lohit Devanagari"); OUTLAWED_FONTS.add("Droid Sans Thai"); OUTLAWED_FONTS.add("Droid Sans Hebrew"); OUTLAWED_FONTS.add("Droid Sans Georgian"); OUTLAWED_FONTS.add("Droid Sans Ethiopic"); OUTLAWED_FONTS.add("Droid Sans Armenian"); OUTLAWED_FONTS.add("Droid Arabic Naskh"); OUTLAWED_FONTS.add("STIXIntegralsD"); OUTLAWED_FONTS.add("STIXIntegralsSm"); OUTLAWED_FONTS.add("STIXIntegralsUp"); OUTLAWED_FONTS.add("STIXIntegralsUpD"); OUTLAWED_FONTS.add("STIXIntegralsUpSm"); OUTLAWED_FONTS.add("STIXNonUnicode"); OUTLAWED_FONTS.add("STIXSizeFiveSym"); OUTLAWED_FONTS.add("STIXSizeFourSym"); OUTLAWED_FONTS.add("STIXSizeOneSym"); OUTLAWED_FONTS.add("STIXSizeThreeSym"); OUTLAWED_FONTS.add("STIXSizeTwoSym"); OUTLAWED_FONTS.add("STIXVariants"); OUTLAWED_FONTS.add("TakaoPGothic"); OUTLAWED_FONTS.add("Droid Sans Japanese"); OUTLAWED_FONTS.add("LKLUG"); OUTLAWED_FONTS.add("Tibetan Machine Uni"); OUTLAWED_FONTS.add("esint10"); OUTLAWED_FONTS.add("eufm10"); OUTLAWED_FONTS.add("cmex10"); OUTLAWED_FONTS.add("cmsy10"); OUTLAWED_FONTS.add("cmr10"); OUTLAWED_FONTS.add("Rachana"); OUTLAWED_FONTS.add("rsfs10"); OUTLAWED_FONTS.add("wasy10"); OUTLAWED_FONTS.add("Academy Engraved LET"); OUTLAWED_FONTS.add("Bodoni Ornaments ITC TT"); OUTLAWED_FONTS.add("Bordeaux Roman Bold LET"); OUTLAWED_FONTS.add("Braggadocio"); OUTLAWED_FONTS.add("Curlz MT"); OUTLAWED_FONTS.add("Didot"); OUTLAWED_FONTS.add("Desdemona"); OUTLAWED_FONTS.add("Engravers MT"); OUTLAWED_FONTS.add("Princetown LET"); OUTLAWED_FONTS.add("Type Embellishments One LET"); OUTLAWED_FONTS.add("Wide Latin"); OUTLAWED_FONTS.add("Wingdings"); OUTLAWED_FONTS.add("Wingdings 2"); OUTLAWED_FONTS.add("Wingdings 3"); OUTLAWED_FONTS.add("Zapf Dingbats"); OUTLAWED_FONTS.add("Zapfino"); OUTLAWED_FONTS.add("Al Tarikh"); OUTLAWED_FONTS.add("Apple Chancery"); OUTLAWED_FONTS.add("Apple LiGothic"); OUTLAWED_FONTS.add("Apple LiSung"); OUTLAWED_FONTS.add("Apple Symbols"); OUTLAWED_FONTS.add("AppleMyungjo"); OUTLAWED_FONTS.add("Avenir Next"); OUTLAWED_FONTS.add("Avenir Next Condensed"); OUTLAWED_FONTS.add("Ayuthaya"); OUTLAWED_FONTS.add("Bank Gothic"); OUTLAWED_FONTS.add("Baoli SC"); OUTLAWED_FONTS.add("Baskerville Old Face"); OUTLAWED_FONTS.add("Batang"); OUTLAWED_FONTS.add("Bauhaus 93"); OUTLAWED_FONTS.add("Bell MT"); OUTLAWED_FONTS.add("Bernard MT Condensed"); OUTLAWED_FONTS.add("BiauKai"); OUTLAWED_FONTS.add("Bodoni SvtyTwo ITC TT"); OUTLAWED_FONTS.add("Bodoni SvtyTwo OS ITC TT"); OUTLAWED_FONTS.add("Bodoni SvtyTwo SC ITC TT"); OUTLAWED_FONTS.add("Book Antiqua"); OUTLAWED_FONTS.add("Bookman Old Style"); OUTLAWED_FONTS.add("Bookshelf Symbol 7"); OUTLAWED_FONTS.add("Britannic Bold"); OUTLAWED_FONTS.add("Brush Script MT"); OUTLAWED_FONTS.add("Calisto MT"); OUTLAWED_FONTS.add("Cambria"); OUTLAWED_FONTS.add("Cambria Math"); OUTLAWED_FONTS.add("Capitals"); OUTLAWED_FONTS.add("Century"); OUTLAWED_FONTS.add("Century Gothic"); OUTLAWED_FONTS.add("Century Schoolbook"); OUTLAWED_FONTS.add("Chalkboard"); OUTLAWED_FONTS.add("Chalkboard SE"); OUTLAWED_FONTS.add("Chalkduster"); OUTLAWED_FONTS.add("Cochin"); OUTLAWED_FONTS.add("Colonna MT"); OUTLAWED_FONTS.add("Comic Sans MS"); OUTLAWED_FONTS.add("Consolas"); OUTLAWED_FONTS.add("Constantia"); OUTLAWED_FONTS.add("Cooper Black"); OUTLAWED_FONTS.add("Copperplate"); OUTLAWED_FONTS.add("Copperplate Gothic Bold"); OUTLAWED_FONTS.add("Copperplate Gothic Light"); OUTLAWED_FONTS.add("Corsiva Hebrew"); OUTLAWED_FONTS.add("Courier New"); OUTLAWED_FONTS.add("Damascus"); OUTLAWED_FONTS.add("Devanagari MT"); OUTLAWED_FONTS.add("DIN Alternate"); OUTLAWED_FONTS.add("DIN Condensed"); OUTLAWED_FONTS.add("Edwardian Script ITC"); OUTLAWED_FONTS.add("Eurostile"); OUTLAWED_FONTS.add("Footlight MT Light"); OUTLAWED_FONTS.add("Garamond"); OUTLAWED_FONTS.add("Gloucester MT Extra Condensed"); OUTLAWED_FONTS.add("Goudy Old Style"); OUTLAWED_FONTS.add("Gujarati MT"); OUTLAWED_FONTS.add("Gulim"); OUTLAWED_FONTS.add("GungSeo"); OUTLAWED_FONTS.add("Gurmukhi MN"); OUTLAWED_FONTS.add("Haettenschweiler"); OUTLAWED_FONTS.add("Hannotate SC"); OUTLAWED_FONTS.add("Hannotate TC"); OUTLAWED_FONTS.add("HanziPen SC"); OUTLAWED_FONTS.add("HanziPen TC"); OUTLAWED_FONTS.add("Harrington"); OUTLAWED_FONTS.add("HeadLineA"); OUTLAWED_FONTS.add("Heiti SC"); OUTLAWED_FONTS.add("Heiti TC"); OUTLAWED_FONTS.add("Helvetica CY"); OUTLAWED_FONTS.add("Helvetica Neue"); OUTLAWED_FONTS.add("Herculanum"); OUTLAWED_FONTS.add("Hiragino Kaku Gothic Pro"); OUTLAWED_FONTS.add("Hiragino Kaku Gothic ProN"); OUTLAWED_FONTS.add("Hiragino Kaku Gothic Std"); OUTLAWED_FONTS.add("Hiragino Kaku Gothic StdN"); OUTLAWED_FONTS.add("Hiragino Maru Gothic Pro"); OUTLAWED_FONTS.add("Hiragino Maru Gothic ProN"); OUTLAWED_FONTS.add("Hiragino Mincho Pro"); OUTLAWED_FONTS.add("Hiragino Mincho ProN"); OUTLAWED_FONTS.add("Hoefler Text"); OUTLAWED_FONTS.add("Impact"); OUTLAWED_FONTS.add("Imprint MT Shadow"); OUTLAWED_FONTS.add("InaiMathi"); OUTLAWED_FONTS.add("Jazz LET"); OUTLAWED_FONTS.add("Kai"); OUTLAWED_FONTS.add("Kaiti SC"); OUTLAWED_FONTS.add("Kaiti TC"); OUTLAWED_FONTS.add("Kannada MN"); OUTLAWED_FONTS.add("Khmer MN"); OUTLAWED_FONTS.add("Kino MT"); OUTLAWED_FONTS.add("Kokonor"); OUTLAWED_FONTS.add("Krungthep"); OUTLAWED_FONTS.add("Lao MN"); OUTLAWED_FONTS.add("Libian SC"); OUTLAWED_FONTS.add("LiSong Pro"); OUTLAWED_FONTS.add("Lucida Blackletter"); OUTLAWED_FONTS.add("Lucida Bright"); OUTLAWED_FONTS.add("Lucida Calligraphy"); OUTLAWED_FONTS.add("Lucida Fax"); OUTLAWED_FONTS.add("Lucida Handwriting"); OUTLAWED_FONTS.add("Lucida Sans Typewriter"); OUTLAWED_FONTS.add("Malayalam MN"); OUTLAWED_FONTS.add("Marion"); OUTLAWED_FONTS.add("Marlett"); OUTLAWED_FONTS.add("Matura MT Script Capitals"); OUTLAWED_FONTS.add("Meiryo"); OUTLAWED_FONTS.add("Menlo"); OUTLAWED_FONTS.add("Microsoft Himalaya"); OUTLAWED_FONTS.add("Microsoft Yi Baiti"); OUTLAWED_FONTS.add("MingLiU"); OUTLAWED_FONTS.add("MingLiU-ExtB"); OUTLAWED_FONTS.add("MingLiU_HKSCS"); OUTLAWED_FONTS.add("MingLiU_HKSCS-ExtB"); OUTLAWED_FONTS.add("Mistral"); OUTLAWED_FONTS.add("Modern No. 20"); OUTLAWED_FONTS.add("Mona Lisa Solid ITC TT"); OUTLAWED_FONTS.add("Mongolian Baiti"); OUTLAWED_FONTS.add("Monospaced"); OUTLAWED_FONTS.add("Monotype Corsiva"); OUTLAWED_FONTS.add("MS Gothic"); OUTLAWED_FONTS.add("MS Mincho"); OUTLAWED_FONTS.add("MS PGothic"); OUTLAWED_FONTS.add("MS PMincho"); OUTLAWED_FONTS.add("MS Reference Sans Serif"); OUTLAWED_FONTS.add("Mshtakan"); OUTLAWED_FONTS.add("Myanmar MN"); OUTLAWED_FONTS.add("Nanum Brush Script"); OUTLAWED_FONTS.add("Nanum Myeongjo"); OUTLAWED_FONTS.add("Nanum Pen Script"); OUTLAWED_FONTS.add("Noteworthy"); OUTLAWED_FONTS.add("Onyx"); OUTLAWED_FONTS.add("Optima"); OUTLAWED_FONTS.add("Oriya MN"); OUTLAWED_FONTS.add("Oriya Sangam MN"); OUTLAWED_FONTS.add("Palatino"); OUTLAWED_FONTS.add("Palatino Linotype"); OUTLAWED_FONTS.add("Papyrus"); OUTLAWED_FONTS.add("Party LET"); OUTLAWED_FONTS.add("PCMyungjo"); OUTLAWED_FONTS.add("Perpetua"); OUTLAWED_FONTS.add("Perpetua Titling MT"); OUTLAWED_FONTS.add("PilGi"); OUTLAWED_FONTS.add("Plantagenet Cherokee"); OUTLAWED_FONTS.add("Playbill"); OUTLAWED_FONTS.add("PMingLiU"); OUTLAWED_FONTS.add("PMingLiU-ExtB"); OUTLAWED_FONTS.add("PortagoITC TT"); OUTLAWED_FONTS.add("PT Serif"); OUTLAWED_FONTS.add("PT Serif Caption"); OUTLAWED_FONTS.add("Rockwell"); OUTLAWED_FONTS.add("Rockwell Extra Bold"); OUTLAWED_FONTS.add("Santa Fe LET"); OUTLAWED_FONTS.add("Savoye LET"); OUTLAWED_FONTS.add("SchoolHouse Cursive B"); OUTLAWED_FONTS.add("SchoolHouse Printed A"); OUTLAWED_FONTS.add("Seravek"); OUTLAWED_FONTS.add("Serif"); OUTLAWED_FONTS.add("Sinhala MN"); OUTLAWED_FONTS.add("Snell Roundhand"); OUTLAWED_FONTS.add("Songti SC"); OUTLAWED_FONTS.add("Songti TC"); OUTLAWED_FONTS.add("Stencil"); OUTLAWED_FONTS.add("STFangsong"); OUTLAWED_FONTS.add("STHeiti"); OUTLAWED_FONTS.add("STKaiti"); OUTLAWED_FONTS.add("STSong"); OUTLAWED_FONTS.add("Superclarendon"); OUTLAWED_FONTS.add("Synchro LET"); OUTLAWED_FONTS.add("Tahoma"); OUTLAWED_FONTS.add("Tamil MN"); OUTLAWED_FONTS.add("Tamil Sangam MN"); OUTLAWED_FONTS.add("Telugu MN"); OUTLAWED_FONTS.add("Times"); OUTLAWED_FONTS.add("Times New Roman"); OUTLAWED_FONTS.add("Wawati SC"); OUTLAWED_FONTS.add("Wawati TC"); OUTLAWED_FONTS.add("Xingkai SC"); OUTLAWED_FONTS.add("YuGothicYuMincho"); } private static List<String> getFontsToUse(Set<String> allowedFonts) { List<String> fontsToUse = new ArrayList<String>(); GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment(); for (String fontName : ge.getAvailableFontFamilyNames()) { if (!allowedFonts.isEmpty()) { if (allowedFonts.contains(fontName)) { fontsToUse.add(fontName); } } else if (!OUTLAWED_FONTS.contains(fontName)) { fontsToUse.add(fontName); } } return fontsToUse; } public static void main(String[] args) { Set<String> allowedFonts = new HashSet<String>(); for (String fontName : getFontsToUse(allowedFonts)) { System.out.println(fontName); PixelType[][] data = renderString(fontName, "q̃", 30, "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"); StringBuffer buf = new StringBuffer(); if (data.length > 0) { for (int j = 0; j < data[0].length; ++j) { for (int i = 0; i < data.length; ++i) { PixelType val = data[i][j]; if (val == PixelType.WHITE) buf.append(". "); else if (val == PixelType.BLACK) buf.append("O "); else if (val == PixelType.OBSCURED) buf.append("X "); } buf.append("\n"); } } System.out.println(buf.toString()); } } public static PixelType[][][][] getRenderedFont(Indexer<String> charIndexer, int height, Set<String> allowedFonts) { StringBuffer alphabetStr = new StringBuffer(); for (int c=0; c<charIndexer.size(); ++c) { alphabetStr.append(unescapeChar(charIndexer.getObject(c))); } PixelType[][][][] result = new PixelType[charIndexer.size()][][][]; for (int c=0; c<charIndexer.size(); ++c) { List<PixelType[][]> rendered = new ArrayList<ImageUtils.PixelType[][]>(); for (String font : getFontsToUse(allowedFonts)) { PixelType[][] renderedChar = renderString(font, unescapeChar(charIndexer.getObject(c)), height, alphabetStr.toString()); if (renderedChar.length > 0) rendered.add(renderedChar); else { if (!Charset.SPACE.equals(charIndexer.getObject(c))) { System.out.println("Ignoring empty character rendering: " + font +", " + charIndexer.getObject(c)); } } } result[c] = rendered.toArray(new PixelType[0][][]); } return result; } private static PixelType[][] renderString(String fontName, String s, int height, String alphabetString) { BufferedImage image = new BufferedImage(height, height, BufferedImage.TYPE_BYTE_GRAY); Graphics2D g = image.createGraphics(); g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); g.setPaint(Color.WHITE); g.fillRect(0, 0, height, height); g.setPaint(Color.BLACK); Font font = new Font(fontName, Font.PLAIN, 10); g.setFont(font); FontMetrics fm = g.getFontMetrics(font); fm.getAscent(); Rectangle2D strBounds = fm.getStringBounds(s, g); Rectangle2D alphabetBounds = fm.getStringBounds(alphabetString, g); AffineTransform affine = new AffineTransform(); affine.translate(((double) height)/2.0 - strBounds.getCenterX()*((double) height)/alphabetBounds.getHeight(), ((double) height)/2.0 - alphabetBounds.getCenterY()*((double) height)/alphabetBounds.getHeight()); affine.scale(((double) height)/alphabetBounds.getHeight(), ((double) height)/(alphabetBounds.getHeight())); g.setTransform(affine); g.drawString(s, 0, 0); double[][] levels = ImageUtils.getLevels(image); levels = horizontalCrop(levels); return ImageUtils.getPixelTypes(levels); } private static double[][] horizontalCrop(double[][] levels) { double whitenessThresh = 0.4 * ImageUtils.MAX_LEVEL; int left = -1; for (int i=0; i<levels.length; ++i) { if (!allAbove(levels[i], whitenessThresh)) { left = i; break; } } int right = -1; for (int i=levels.length-1; i>=0; --i) { if (!allAbove(levels[i], whitenessThresh)) { right = i+1; break; } } if (left == -1) return new double[0][]; double[][] result = new double[right-left][]; for (int i=0; i<right-left; ++i) { result[i] = levels[left+i]; } return result; } private static boolean allAbove(double[] vect, double threshold) { for (double val : vect) { if (val < threshold) { return false; } } return true; } }