X-Git-Url: http://git.nikiroo.be/?a=blobdiff_plain;f=src%2Fjexer%2Fbackend%2FGlyphMaker.java;h=e5fcc522da927fb2c8788b98f8a5075815389f38;hb=cfdaf6052ddc5ca44cf19f1f6d9f154cc8443024;hp=0c798b1d7e1fdd48eb7d0df0b2f10f45326c5231;hpb=9588c7134280341ab6e92e37d1c1d00b3756cee5;p=fanfix.git diff --git a/src/jexer/backend/GlyphMaker.java b/src/jexer/backend/GlyphMaker.java index 0c798b1..e5fcc52 100644 --- a/src/jexer/backend/GlyphMaker.java +++ b/src/jexer/backend/GlyphMaker.java @@ -29,6 +29,7 @@ package jexer.backend; import java.awt.Font; +import java.awt.FontFormatException; import java.awt.FontMetrics; import java.awt.Graphics2D; import java.awt.geom.Rectangle2D; @@ -38,6 +39,7 @@ import java.io.IOException; import java.util.HashMap; import jexer.bits.Cell; +import jexer.bits.StringUtils; /** * GlyphMakerFont creates glyphs as bitmaps from a font. @@ -135,18 +137,28 @@ class GlyphMakerFont { * @param fontSize the size of font to use */ public GlyphMakerFont(final String filename, final int fontSize) { + + if (filename.length() == 0) { + // Fallback font + font = new Font(Font.MONOSPACED, Font.PLAIN, fontSize - 2); + return; + } + Font fontRoot = null; try { ClassLoader loader = Thread.currentThread().getContextClassLoader(); InputStream in = loader.getResourceAsStream(filename); fontRoot = Font.createFont(Font.TRUETYPE_FONT, in); - font = fontRoot.deriveFont(Font.PLAIN, fontSize); - } catch (java.awt.FontFormatException e) { - e.printStackTrace(); - font = new Font(Font.MONOSPACED, Font.PLAIN, fontSize); - } catch (java.io.IOException e) { - e.printStackTrace(); - font = new Font(Font.MONOSPACED, Font.PLAIN, fontSize); + font = fontRoot.deriveFont(Font.PLAIN, fontSize - 2); + } catch (FontFormatException e) { + // Ideally we would report an error here, either via System.err + // or TExceptionDialog. However, I do not want GlyphMaker to + // know about available backends, so we quietly fallback to + // whatever is available as MONO. + font = new Font(Font.MONOSPACED, Font.PLAIN, fontSize - 2); + } catch (IOException e) { + // See comment above. + font = new Font(Font.MONOSPACED, Font.PLAIN, fontSize - 2); } } @@ -185,6 +197,11 @@ class GlyphMakerFont { getFontDimensions(); } + if (DEBUG && !font.canDisplay(cell.getChar())) { + System.err.println("font " + font + " has no glyph for " + + String.format("0x%x", cell.getChar())); + } + BufferedImage image = null; if (cell.isBlink() && !blinkVisible) { image = glyphCacheBlink.get(cell); @@ -201,8 +218,7 @@ class GlyphMakerFont { Graphics2D gr2 = image.createGraphics(); gr2.setFont(font); - Cell cellColor = new Cell(); - cellColor.setTo(cell); + Cell cellColor = new Cell(cell); // Check for reverse if (cell.isReverse()) { @@ -219,9 +235,8 @@ class GlyphMakerFont { || (cell.isBlink() && blinkVisible) ) { gr2.setColor(SwingTerminal.attrToForegroundColor(cellColor)); - char [] chars = new char[1]; - chars[0] = cell.getChar(); - gr2.drawChars(chars, 0, 1, textAdjustX, + char [] chars = Character.toChars(cell.getChar()); + gr2.drawChars(chars, 0, chars.length, textAdjustX, cellHeight - maxDescent + textAdjustY); if (cell.isUnderline()) { @@ -231,14 +246,18 @@ class GlyphMakerFont { gr2.dispose(); // We need a new key that will not be mutated by invertCell(). - Cell key = new Cell(); - key.setTo(cell); + Cell key = new Cell(cell); if (cell.isBlink() && !blinkVisible) { glyphCacheBlink.put(key, image); } else { glyphCache.put(key, image); } + /* + System.err.println("cellWidth " + cellWidth + + " cellHeight " + cellHeight + " image " + image); + */ + return image; } @@ -267,10 +286,28 @@ class GlyphMakerFont { textHeight = fontTextHeight + textAdjustHeight; textWidth = fontTextWidth + textAdjustWidth; + /* + System.err.println("font " + font); + System.err.println("fontTextWidth " + fontTextWidth); + System.err.println("fontTextHeight " + fontTextHeight); + System.err.println("textWidth " + textWidth); + System.err.println("textHeight " + textHeight); + */ gotFontDimensions = true; } + /** + * Checks if this maker's Font has a glyph for the specified character. + * + * @param codePoint the character (Unicode code point) for which a glyph + * is needed. + * @return true if this Font has a glyph for the character; false + * otherwise. + */ + public boolean canDisplay(final int codePoint) { + return font.canDisplay(codePoint); + } } /** @@ -289,19 +326,19 @@ public class GlyphMaker { private static final String MONO = "terminus-ttf-4.39/TerminusTTF-Bold-4.39.ttf"; /** - * The CJKhk font resource filename. + * The CJK font resource filename. */ - // private static final String CJKhk = "NotoSansMonoCJKhk-Regular.otf"; + private static final String cjkFontFilename = "NotoSansMonoCJKtc-Regular.otf"; /** - * The CJKkr font resource filename. + * The emoji font resource filename. */ - // private static final String CJKkr = "NotoSansMonoCJKkr-Regular.otf"; + private static final String emojiFontFilename = "OpenSansEmoji.ttf"; /** - * The CJKtc font resource filename. + * The fallback font resource filename. */ - private static final String CJKtc = "NotoSansMonoCJKtc-Regular.otf"; + private static final String fallbackFontFilename = ""; // ------------------------------------------------------------------------ // Variables -------------------------------------------------------------- @@ -323,19 +360,19 @@ public class GlyphMaker { private GlyphMakerFont makerMono; /** - * The instance that has the CJKhk font. + * The instance that has the CJK font. */ - // private GlyphMakerFont makerCJKhk; + private GlyphMakerFont makerCjk; /** - * The instance that has the CJKkr font. + * The instance that has the emoji font. */ - // private GlyphMakerFont makerCJKkr; + private GlyphMakerFont makerEmoji; /** - * The instance that has the CJKtc font. + * The instance that has the fallback font. */ - private GlyphMakerFont makerCJKtc; + private GlyphMakerFont makerFallback; // ------------------------------------------------------------------------ // Constructors ----------------------------------------------------------- @@ -348,9 +385,17 @@ public class GlyphMaker { */ private GlyphMaker(final int fontSize) { makerMono = new GlyphMakerFont(MONO, fontSize); - // makerCJKhk = new GlyphMakerFont(CJKhk, fontSize); - // makerCJKkr = new GlyphMakerFont(CJKkr, fontSize); - makerCJKtc = new GlyphMakerFont(CJKtc, fontSize); + + String fontFilename = null; + fontFilename = System.getProperty("jexer.cjkFont.filename", + cjkFontFilename); + makerCjk = new GlyphMakerFont(fontFilename, fontSize); + fontFilename = System.getProperty("jexer.emojiFont.filename", + emojiFontFilename); + makerEmoji = new GlyphMakerFont(fontFilename, fontSize); + fontFilename = System.getProperty("jexer.fallbackFont.filename", + fallbackFontFilename); + makerFallback = new GlyphMakerFont(fontFilename, fontSize); } // ------------------------------------------------------------------------ @@ -400,21 +445,29 @@ public class GlyphMaker { public BufferedImage getImage(final Cell cell, final int cellWidth, final int cellHeight, final boolean blinkVisible) { - char ch = cell.getChar(); - /* - if ((ch >= 0x4e00) && (ch <= 0x9fff)) { - return makerCJKhk.getImage(cell, cellWidth, cellHeight, blinkVisible); - } - if ((ch >= 0x4e00) && (ch <= 0x9fff)) { - return makerCJKkr.getImage(cell, cellWidth, cellHeight, blinkVisible); + int ch = cell.getChar(); + if (StringUtils.isCjk(ch)) { + if (makerCjk.canDisplay(ch)) { + return makerCjk.getImage(cell, cellWidth, cellHeight, + blinkVisible); + } } - */ - if ((ch >= 0x2e80) && (ch <= 0x9fff)) { - return makerCJKtc.getImage(cell, cellWidth, cellHeight, blinkVisible); + if (StringUtils.isEmoji(ch)) { + if (makerEmoji.canDisplay(ch)) { + // System.err.println("emoji: " + String.format("0x%x", ch)); + return makerEmoji.getImage(cell, cellWidth, cellHeight, + blinkVisible); + } } // When all else fails, use the default. - return makerMono.getImage(cell, cellWidth, cellHeight, blinkVisible); + if (makerMono.canDisplay(ch)) { + return makerMono.getImage(cell, cellWidth, cellHeight, + blinkVisible); + } + + return makerFallback.getImage(cell, cellWidth, cellHeight, + blinkVisible); } }