From 218d18dbda14a7bf482d6c07bed66f16bcd6a6ba Mon Sep 17 00:00:00 2001 From: Kevin Lamonte Date: Sun, 11 Aug 2019 22:14:42 -0500 Subject: [PATCH] #35 emoji font wip --- .gitignore | 3 ++ src/jexer/TWidget.java | 10 ++--- src/jexer/backend/GlyphMaker.java | 63 +++++++++++++--------------- src/jexer/backend/LogicalScreen.java | 25 +++++------ src/jexer/backend/MultiScreen.java | 10 ++--- src/jexer/backend/Screen.java | 10 ++--- src/jexer/backend/SwingTerminal.java | 5 +-- src/jexer/bits/Cell.java | 10 ++--- src/jexer/bits/MnemonicString.java | 18 ++++---- src/jexer/bits/StringUtils.java | 9 ++-- 10 files changed, 82 insertions(+), 81 deletions(-) diff --git a/.gitignore b/.gitignore index 9e8f22f..30d9f7c 100644 --- a/.gitignore +++ b/.gitignore @@ -30,3 +30,6 @@ misc/** pmd.bash pmd-results.html examples/*.sh + +# Fonts for testing +fonts/** diff --git a/src/jexer/TWidget.java b/src/jexer/TWidget.java index 633b149..2b9d5cc 100644 --- a/src/jexer/TWidget.java +++ b/src/jexer/TWidget.java @@ -1501,7 +1501,7 @@ public abstract class TWidget implements Comparable { * @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 { * @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 { * @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 { * @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 { * @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); } diff --git a/src/jexer/backend/GlyphMaker.java b/src/jexer/backend/GlyphMaker.java index 91bda6d..2a6610b 100644 --- a/src/jexer/backend/GlyphMaker.java +++ b/src/jexer/backend/GlyphMaker.java @@ -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. diff --git a/src/jexer/backend/LogicalScreen.java b/src/jexer/backend/LogicalScreen.java index 558fdc4..b4e6214 100644 --- a/src/jexer/backend/LogicalScreen.java +++ b/src/jexer/backend/LogicalScreen.java @@ -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)); diff --git a/src/jexer/backend/MultiScreen.java b/src/jexer/backend/MultiScreen.java index 0c3a289..9d66b69 100644 --- a/src/jexer/backend/MultiScreen.java +++ b/src/jexer/backend/MultiScreen.java @@ -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); diff --git a/src/jexer/backend/Screen.java b/src/jexer/backend/Screen.java index 2d9cd65..2a71073 100644 --- a/src/jexer/backend/Screen.java +++ b/src/jexer/backend/Screen.java @@ -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 diff --git a/src/jexer/backend/SwingTerminal.java b/src/jexer/backend/SwingTerminal.java index 134eced..a3825b8 100644 --- a/src/jexer/backend/SwingTerminal.java +++ b/src/jexer/backend/SwingTerminal.java @@ -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()) { diff --git a/src/jexer/bits/Cell.java b/src/jexer/bits/Cell.java index ff10dae..a8efa2b 100644 --- a/src/jexer/bits/Cell.java +++ b/src/jexer/bits/Cell.java @@ -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; } diff --git a/src/jexer/bits/MnemonicString.java b/src/jexer/bits/MnemonicString.java index 2d5dbc8..58575b5 100644 --- a/src/jexer/bits/MnemonicString.java +++ b/src/jexer/bits/MnemonicString.java @@ -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; } diff --git a/src/jexer/bits/StringUtils.java b/src/jexer/bits/StringUtils.java index f5e2d47..26d90a4 100644 --- a/src/jexer/bits/StringUtils.java +++ b/src/jexer/bits/StringUtils.java @@ -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; } -- 2.27.0