#35 emoji font wip
authorKevin Lamonte <kevin.lamonte@gmail.com>
Mon, 12 Aug 2019 03:14:42 +0000 (22:14 -0500)
committerKevin Lamonte <kevin.lamonte@gmail.com>
Mon, 12 Aug 2019 03:14:42 +0000 (22:14 -0500)
.gitignore
src/jexer/TWidget.java
src/jexer/backend/GlyphMaker.java
src/jexer/backend/LogicalScreen.java
src/jexer/backend/MultiScreen.java
src/jexer/backend/Screen.java
src/jexer/backend/SwingTerminal.java
src/jexer/bits/Cell.java
src/jexer/bits/MnemonicString.java
src/jexer/bits/StringUtils.java

index 9e8f22f5eda7f89dca21b941d75c292e79ae4323..30d9f7c701397a07ff13ad91bb0cf1c59cdf3351 100644 (file)
@@ -30,3 +30,6 @@ misc/**
 pmd.bash
 pmd-results.html
 examples/*.sh
+
+# Fonts for testing
+fonts/**
index 633b149575f24c74728ba740cb30fe7362e3e7fd..2b9d5cc28a17dff4f0b0157197841d7756b25cf3 100644 (file)
@@ -1501,7 +1501,7 @@ public abstract class TWidget implements Comparable<TWidget> {
      * @param ch character to draw
      * @param attr attributes to use (bold, foreColor, backColor)
      */
-    protected final void putAll(final char ch, final CellAttributes attr) {
+    protected final void putAll(final int ch, final CellAttributes attr) {
         getScreen().putAll(ch, attr);
     }
 
@@ -1524,7 +1524,7 @@ public abstract class TWidget implements Comparable<TWidget> {
      * @param ch character to draw
      * @param attr attributes to use (bold, foreColor, backColor)
      */
-    protected final void putCharXY(final int x, final int y, final char ch,
+    protected final void putCharXY(final int x, final int y, final int ch,
         final CellAttributes attr) {
 
         getScreen().putCharXY(x, y, ch, attr);
@@ -1537,7 +1537,7 @@ public abstract class TWidget implements Comparable<TWidget> {
      * @param y row coordinate.  0 is the top-most row.
      * @param ch character to draw
      */
-    protected final void putCharXY(final int x, final int y, final char ch) {
+    protected final void putCharXY(final int x, final int y, final int ch) {
         getScreen().putCharXY(x, y, ch);
     }
 
@@ -1577,7 +1577,7 @@ public abstract class TWidget implements Comparable<TWidget> {
      * @param attr attributes to use (bold, foreColor, backColor)
      */
     protected final void vLineXY(final int x, final int y, final int n,
-        final char ch, final CellAttributes attr) {
+        final int ch, final CellAttributes attr) {
 
         getScreen().vLineXY(x, y, n, ch, attr);
     }
@@ -1592,7 +1592,7 @@ public abstract class TWidget implements Comparable<TWidget> {
      * @param attr attributes to use (bold, foreColor, backColor)
      */
     protected final void hLineXY(final int x, final int y, final int n,
-        final char ch, final CellAttributes attr) {
+        final int ch, final CellAttributes attr) {
 
         getScreen().hLineXY(x, y, n, ch, attr);
     }
index 91bda6d7314059c0fbbe0bddfcb763688d8c378d..2a6610bbec4c8cedff479a6962b8502757bac874 100644 (file)
@@ -185,6 +185,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);
@@ -218,9 +223,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()) {
@@ -299,19 +303,14 @@ 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";
-
-    /**
-     * The CJKtc font resource filename.
-     */
-    private static final String CJKtc = "NotoSansMonoCJKtc-Regular.otf";
+    private static final String emojiFontFilename = "OpenSansEmoji.ttf";
 
     // ------------------------------------------------------------------------
     // Variables --------------------------------------------------------------
@@ -333,19 +332,14 @@ public class GlyphMaker {
     private GlyphMakerFont makerMono;
 
     /**
-     * The instance that has the CJKhk font.
-     */
-    // private GlyphMakerFont makerCJKhk;
-
-    /**
-     * The instance that has the CJKkr font.
+     * The instance that has the CJK font.
      */
-    // private GlyphMakerFont makerCJKkr;
+    private GlyphMakerFont makerCjk;
 
     /**
-     * The instance that has the CJKtc font.
+     * The instance that has the emoji font.
      */
-    private GlyphMakerFont makerCJKtc;
+    private GlyphMakerFont makerEmoji;
 
     // ------------------------------------------------------------------------
     // Constructors -----------------------------------------------------------
@@ -357,11 +351,15 @@ public class GlyphMaker {
      * @param fontSize the size of these fonts in pixels
      */
     private GlyphMaker(final int fontSize) {
-        assert (fontSize > 3);
         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);
     }
 
     // ------------------------------------------------------------------------
@@ -411,17 +409,12 @@ 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 ((ch >= 0x2e80) && (ch <= 0x9fff)) {
-            return makerCJKtc.getImage(cell, cellWidth, cellHeight, blinkVisible);
+            return makerCjk.getImage(cell, cellWidth, cellHeight, blinkVisible);
+        }
+        if ((ch >= 0x1f004) && (ch <= 0x1f9c0)) {
+            return makerEmoji.getImage(cell, cellWidth, cellHeight, blinkVisible);
         }
 
         // When all else fails, use the default.
index 558fdc4812c1e553b1434e0b1d6c173dc4ef0469..b4e6214177ecb4a2fb7036b1bdab7bec8e95954e 100644 (file)
@@ -369,7 +369,7 @@ public class LogicalScreen implements Screen {
      * @param ch character to draw
      * @param attr attributes to use (bold, foreColor, backColor)
      */
-    public final void putAll(final char ch, final CellAttributes attr) {
+    public final void putAll(final int ch, final CellAttributes attr) {
 
         for (int x = 0; x < width; x++) {
             for (int y = 0; y < height; y++) {
@@ -430,7 +430,7 @@ public class LogicalScreen implements Screen {
      * @param ch character to draw
      * @param attr attributes to use (bold, foreColor, backColor)
      */
-    public final void putCharXY(final int x, final int y, final char ch,
+    public final void putCharXY(final int x, final int y, final int ch,
         final CellAttributes attr) {
 
         if ((x < clipLeft)
@@ -476,8 +476,7 @@ public class LogicalScreen implements Screen {
      * @param y row coordinate.  0 is the top-most row.
      * @param ch character to draw
      */
-    public final void putCharXY(final int x, final int y, final char ch) {
-
+    public final void putCharXY(final int x, final int y, final int ch) {
         if ((x < clipLeft)
             || (x >= clipRight)
             || (y < clipTop)
@@ -520,8 +519,9 @@ public class LogicalScreen implements Screen {
         final CellAttributes attr) {
 
         int i = x;
-        for (int j = 0; j < str.length(); j++) {
-            char ch = str.charAt(j);
+        for (int j = 0; j < str.length();) {
+            int ch = str.codePointAt(j);
+            j += Character.charCount(ch);
             putCharXY(i, y, ch, attr);
             i += StringUtils.width(ch);
             if (i == width) {
@@ -541,8 +541,9 @@ public class LogicalScreen implements Screen {
     public final void putStringXY(final int x, final int y, final String str) {
 
         int i = x;
-        for (int j = 0; j < str.length(); j++) {
-            char ch = str.charAt(j);
+        for (int j = 0; j < str.length();) {
+            int ch = str.codePointAt(j);
+            j += Character.charCount(ch);
             putCharXY(i, y, ch);
             i += StringUtils.width(ch);
             if (i == width) {
@@ -561,7 +562,7 @@ public class LogicalScreen implements Screen {
      * @param attr attributes to use (bold, foreColor, backColor)
      */
     public final void vLineXY(final int x, final int y, final int n,
-        final char ch, final CellAttributes attr) {
+        final int ch, final CellAttributes attr) {
 
         for (int i = y; i < y + n; i++) {
             putCharXY(x, i, ch, attr);
@@ -578,7 +579,7 @@ public class LogicalScreen implements Screen {
      * @param attr attributes to use (bold, foreColor, backColor)
      */
     public final void hLineXY(final int x, final int y, final int n,
-        final char ch, final CellAttributes attr) {
+        final int ch, final CellAttributes attr) {
 
         for (int i = x; i < x + n; i++) {
             putCharXY(i, y, ch, attr);
@@ -1005,7 +1006,7 @@ public class LogicalScreen implements Screen {
      * @param attr attributes to use (bold, foreColor, backColor)
      */
     public final void putFullwidthCharXY(final int x, final int y,
-        final char ch, final CellAttributes attr) {
+        final int ch, final CellAttributes attr) {
 
         Cell cell = new Cell(ch, attr);
         putFullwidthCharXY(x, y, cell);
@@ -1019,7 +1020,7 @@ public class LogicalScreen implements Screen {
      * @param ch character to draw
      */
     public final void putFullwidthCharXY(final int x, final int y,
-        final char ch) {
+        final int ch) {
 
         Cell cell = new Cell(ch);
         cell.setAttr(getAttrXY(x, y));
index 0c3a2894bbe29c9bc0af7067cd9b79f8d9b060bc..9d66b69342896a50c6d8683d6cc4c463db4491c2 100644 (file)
@@ -241,7 +241,7 @@ public class MultiScreen implements Screen {
      * @param ch character to draw
      * @param attr attributes to use (bold, foreColor, backColor)
      */
-    public void putAll(final char ch, final CellAttributes attr) {
+    public void putAll(final int ch, final CellAttributes attr) {
         for (Screen screen: screens) {
             screen.putAll(ch, attr);
         }
@@ -268,7 +268,7 @@ public class MultiScreen implements Screen {
      * @param ch character to draw
      * @param attr attributes to use (bold, foreColor, backColor)
      */
-    public void putCharXY(final int x, final int y, final char ch,
+    public void putCharXY(final int x, final int y, final int ch,
         final CellAttributes attr) {
 
         for (Screen screen: screens) {
@@ -283,7 +283,7 @@ public class MultiScreen implements Screen {
      * @param y row coordinate.  0 is the top-most row.
      * @param ch character to draw
      */
-    public void putCharXY(final int x, final int y, final char ch) {
+    public void putCharXY(final int x, final int y, final int ch) {
         for (Screen screen: screens) {
             screen.putCharXY(x, y, ch);
         }
@@ -329,7 +329,7 @@ public class MultiScreen implements Screen {
      * @param attr attributes to use (bold, foreColor, backColor)
      */
     public void vLineXY(final int x, final int y, final int n,
-        final char ch, final CellAttributes attr) {
+        final int ch, final CellAttributes attr) {
 
         for (Screen screen: screens) {
             screen.vLineXY(x, y, n, ch, attr);
@@ -346,7 +346,7 @@ public class MultiScreen implements Screen {
      * @param attr attributes to use (bold, foreColor, backColor)
      */
     public void hLineXY(final int x, final int y, final int n,
-        final char ch, final CellAttributes attr) {
+        final int ch, final CellAttributes attr) {
 
         for (Screen screen: screens) {
             screen.hLineXY(x, y, n, ch, attr);
index 2d9cd65459d6ed5f9740a5abad4a1825e1ba06c2..2a71073176a6608b3a35d0740855e187c746d0e0 100644 (file)
@@ -159,7 +159,7 @@ public interface Screen {
      * @param ch character to draw
      * @param attr attributes to use (bold, foreColor, backColor)
      */
-    public void putAll(final char ch, final CellAttributes attr);
+    public void putAll(final int ch, final CellAttributes attr);
 
     /**
      * Render one character with attributes.
@@ -178,7 +178,7 @@ public interface Screen {
      * @param ch character to draw
      * @param attr attributes to use (bold, foreColor, backColor)
      */
-    public void putCharXY(final int x, final int y, final char ch,
+    public void putCharXY(final int x, final int y, final int ch,
         final CellAttributes attr);
 
     /**
@@ -188,7 +188,7 @@ public interface Screen {
      * @param y row coordinate.  0 is the top-most row.
      * @param ch character to draw
      */
-    public void putCharXY(final int x, final int y, final char ch);
+    public void putCharXY(final int x, final int y, final int ch);
 
     /**
      * Render a string.  Does not wrap if the string exceeds the line.
@@ -221,7 +221,7 @@ public interface Screen {
      * @param attr attributes to use (bold, foreColor, backColor)
      */
     public void vLineXY(final int x, final int y, final int n,
-        final char ch, final CellAttributes attr);
+        final int ch, final CellAttributes attr);
 
     /**
      * Draw a horizontal line from (x, y) to (x + n, y).
@@ -233,7 +233,7 @@ public interface Screen {
      * @param attr attributes to use (bold, foreColor, backColor)
      */
     public void hLineXY(final int x, final int y, final int n,
-        final char ch, final CellAttributes attr);
+        final int ch, final CellAttributes attr);
 
     /**
      * Change the width.  Everything on-screen will be destroyed and must be
index 134eceda63772cec5c776b9d0dedf323f4559bf3..a3825b852548c86bccf45817df32756da5ea729b 100644 (file)
@@ -1229,9 +1229,8 @@ public class SwingTerminal extends LogicalScreen
             || (cell.isBlink() && cursorBlinkVisible)
         ) {
             gr2.setColor(attrToForegroundColor(cellColor));
-            char [] chars = new char[1];
-            chars[0] = cell.getChar();
-            gr2.drawChars(chars, 0, 1, gr2x + textAdjustX,
+            char [] chars = Character.toChars(cell.getChar());
+            gr2.drawChars(chars, 0, chars.length, gr2x + textAdjustX,
                 gr2y + textHeight - maxDescent + textAdjustY);
 
             if (cell.isUnderline()) {
index ff10dae5a99732602359c929763873b74a5fed1a..a8efa2b3c56465dc7ee93dc76fefa399bed85603 100644 (file)
@@ -73,7 +73,7 @@ public final class Cell extends CellAttributes {
     /**
      * The character at this cell.
      */
-    private char ch = ' ';
+    private int ch = ' ';
 
     /**
      * The display width of this cell.
@@ -129,7 +129,7 @@ public final class Cell extends CellAttributes {
      * @param ch character to set to
      * @see #reset()
      */
-    public Cell(final char ch) {
+    public Cell(final int ch) {
         this.ch = ch;
     }
 
@@ -148,7 +148,7 @@ public final class Cell extends CellAttributes {
      * @param ch character to set to
      * @param attr attributes to use
      */
-    public Cell(final char ch, final CellAttributes attr) {
+    public Cell(final int ch, final CellAttributes attr) {
         super(attr);
         this.ch = ch;
     }
@@ -263,7 +263,7 @@ public final class Cell extends CellAttributes {
      *
      * @return cell character
      */
-    public char getChar() {
+    public int getChar() {
         return ch;
     }
 
@@ -272,7 +272,7 @@ public final class Cell extends CellAttributes {
      *
      * @param ch new cell character
      */
-    public void setChar(final char ch) {
+    public void setChar(final int ch) {
         this.ch = ch;
     }
 
index 2d5dbc8cd2533ebb8697b247a744ea2b82aa3202..58575b570cf0f84330aa93859e8c36d974b798e2 100644 (file)
@@ -43,7 +43,7 @@ public class MnemonicString {
     /**
      * Keyboard shortcut to activate this item.
      */
-    private char shortcut;
+    private int shortcut;
 
     /**
      * Location of the highlighted character.
@@ -74,23 +74,25 @@ public class MnemonicString {
     public MnemonicString(final String label) {
 
         // Setup the menu shortcut
-        String newLabel = "";
+        StringBuilder newLabel = new StringBuilder();
         boolean foundAmp = false;
         boolean foundShortcut = false;
         int scanShortcutIdx = 0;
         int scanScreenShortcutIdx = 0;
-        for (int i = 0; i < label.length(); i++) {
-            char c = label.charAt(i);
+        for (int i = 0; i < label.length();) {
+            int c = label.codePointAt(i);
+            i += Character.charCount(c);
+
             if (c == '&') {
                 if (foundAmp) {
-                    newLabel += '&';
+                    newLabel.append('&');
                     scanShortcutIdx++;
                     scanScreenShortcutIdx++;
                 } else {
                     foundAmp = true;
                 }
             } else {
-                newLabel += c;
+                newLabel.append(Character.toChars(c));
                 if (foundAmp) {
                     if (!foundShortcut) {
                         shortcut = c;
@@ -105,7 +107,7 @@ public class MnemonicString {
                 }
             }
         }
-        this.rawLabel = newLabel;
+        this.rawLabel = newLabel.toString();
     }
 
     // ------------------------------------------------------------------------
@@ -117,7 +119,7 @@ public class MnemonicString {
      *
      * @return the highlighted character
      */
-    public char getShortcut() {
+    public int getShortcut() {
         return shortcut;
     }
 
index f5e2d47362a62e9c76bc0f731141a7cf43810d62..26d90a400577bf663f0b4550d1429de628c68b01 100644 (file)
@@ -449,7 +449,8 @@ public class StringUtils {
                 || ((ch >= 0xffe0) && (ch <= 0xffe6))
                 || ((ch >= 0x20000) && (ch <= 0x2fffd))
                 || ((ch >= 0x30000) && (ch <= 0x3fffd))
-                // TODO: emoji / twemoji
+                // emoji
+                || ((ch >= 0x1f004) && (ch <= 0x1f9c0))
             )
         ) {
             return 2;
@@ -466,8 +467,10 @@ public class StringUtils {
      */
     public static int width(final String str) {
         int n = 0;
-        for (int i = 0; i < str.length(); i++) {
-            n += width(str.charAt(i));
+        for (int i = 0; i < str.length();) {
+            int ch = str.codePointAt(i);
+            n += width(ch);
+            i += Character.charCount(ch);
         }
         return n;
     }