Use more consistent fonts for braille and legacy computing symbols

This commit is contained in:
Autumn Lamonte 2022-01-05 12:43:15 -06:00
parent d4ed006243
commit eda549b9cc
4 changed files with 132 additions and 8 deletions

View file

@ -1616,16 +1616,33 @@ public class ECMA48Terminal extends LogicalScreen
* draw everything else afterwards. This works OK, but performance
* is still a drag on larger pictures.
*/
GlyphMaker glyphMaker = GlyphMaker.getInstance(getTextHeight());
for (int y = 0; y < height; y++) {
boolean unsetRow = false;
for (int x = 0; x < width; x++) {
// If physical had non-image data that is now image data, the
// entire row must be redrawn.
Cell lCell = logical[x][y];
Cell pCell = physical[x][y];
if (lCell.isImage() && !pCell.isImage()) {
unsetImageRow(y);
break;
unsetRow = true;
}
int ch = lCell.getChar();
if (!lCell.isImage()
&& (StringUtils.isLegacyComputingSymbol(ch)
|| StringUtils.isBraille(ch))
&& glyphMaker.canDisplay(ch)
) {
// If a fallback font is available that can support
// Symbols for Legacy Computing, always use it.
BufferedImage newImage = glyphMaker.getImage(lCell,
getTextWidth(), getTextHeight(), getBackend());
lCell.setImage(newImage);
unsetRow = true;
}
}
if (unsetRow) {
unsetImageRow(y);
}
}
for (int y = 0; y < height; y++) {

View file

@ -126,6 +126,11 @@ class GlyphMakerFont {
*/
private HashMap<Cell, BufferedImage> glyphCache;
/**
* If true, this font loaded OK.
*/
private boolean loaded = false;
// ------------------------------------------------------------------------
// Constructors -----------------------------------------------------------
// ------------------------------------------------------------------------
@ -140,7 +145,7 @@ class GlyphMakerFont {
if (filename.length() == 0) {
// Fallback font
font = new Font(Font.MONOSPACED, Font.PLAIN, fontSize - 2);
font = new Font(Font.MONOSPACED, Font.PLAIN, fontSize);
return;
}
@ -149,16 +154,17 @@ class GlyphMakerFont {
ClassLoader loader = Thread.currentThread().getContextClassLoader();
InputStream in = loader.getResourceAsStream(filename);
fontRoot = Font.createFont(Font.TRUETYPE_FONT, in);
font = fontRoot.deriveFont(Font.PLAIN, fontSize - 2);
font = fontRoot.deriveFont(Font.PLAIN, fontSize);
loaded = true;
} 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);
font = new Font(Font.MONOSPACED, Font.PLAIN, fontSize);
} catch (IOException e) {
// See comment above.
font = new Font(Font.MONOSPACED, Font.PLAIN, fontSize - 2);
font = new Font(Font.MONOSPACED, Font.PLAIN, fontSize);
}
}
@ -315,6 +321,16 @@ class GlyphMakerFont {
public boolean canDisplay(final int codePoint) {
return font.canDisplay(codePoint);
}
/**
* See if this font loaded OK.
*
* @return true if this font loaded OK, otherwise it is rendering using
* MONO
*/
public boolean isLoaded() {
return loaded;
}
}
/**
@ -381,6 +397,11 @@ public class GlyphMaker {
*/
private GlyphMakerFont makerFallback;
/**
* The system mono font.
*/
private GlyphMakerFont makerSystemMono;
// ------------------------------------------------------------------------
// Constructors -----------------------------------------------------------
// ------------------------------------------------------------------------
@ -392,6 +413,7 @@ public class GlyphMaker {
*/
private GlyphMaker(final int fontSize) {
makerMono = new GlyphMakerFont(MONO, fontSize);
makerSystemMono = new GlyphMakerFont("", fontSize);
String fontFilename = null;
fontFilename = System.getProperty("jexer.cjkFont.filename",
@ -479,10 +501,65 @@ public class GlyphMaker {
blinkVisible);
}
if (makerSystemMono.canDisplay(ch)) {
// System.err.println("system mono: " + String.format("0x%x", ch));
return makerSystemMono.getImage(cell, cellWidth, cellHeight,
backend, blinkVisible);
}
// When all else fails, use the default.
// System.err.println("mono: " + String.format("0x%x", ch));
return makerMono.getImage(cell, cellWidth, cellHeight, backend,
blinkVisible);
}
/**
* Check if a CJK font is available.
*
* @return true if a CJK font is available
*/
public boolean isCjk() {
return makerCjk.isLoaded();
}
/**
* Check if an emoji font is available.
*
* @return true if an emoji font is available
*/
public boolean isEmoji() {
return makerEmoji.isLoaded();
}
/**
* Check if a fallback font is available.
*
* @return true if a fallback font is available
*/
public boolean isFallback() {
return makerFallback.isLoaded();
}
/**
* Checks if a fallback 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) {
if ((makerFallback.isLoaded() && makerFallback.canDisplay(codePoint))
|| (makerEmoji.isLoaded() && makerEmoji.canDisplay(codePoint))
|| (makerCjk.isLoaded() && makerCjk.canDisplay(codePoint))
// Put the system mono font ahead of terminus.
|| makerSystemMono.canDisplay(codePoint)
|| (makerMono.isLoaded() && makerMono.canDisplay(codePoint))
) {
return true;
}
return false;
}
}

View file

@ -64,6 +64,7 @@ import javax.swing.SwingUtilities;
import jexer.TKeypress;
import jexer.bits.Cell;
import jexer.bits.CellAttributes;
import jexer.bits.StringUtils;
import jexer.event.TCommandEvent;
import jexer.event.TInputEvent;
import jexer.event.TKeypressEvent;
@ -1477,9 +1478,17 @@ public class SwingTerminal extends LogicalScreen
return;
}
if (!swing.getFont().canDisplay(cell.getChar())) {
GlyphMaker glyphMaker = GlyphMaker.getInstance(textHeight);
int ch = cell.getChar();
// If a fallback font is available that can support Symbols for
// Legacy Computing, always use it. This is for consistency --
// we assume the fallback font has better coverage than Terminus.
if (((StringUtils.isLegacyComputingSymbol(ch)
|| StringUtils.isBraille(ch))
&& glyphMaker.canDisplay(ch))
|| !swing.getFont().canDisplay(ch)
) {
// The main font cannot display this glyph. Try a fallback font.
GlyphMaker glyphMaker = GlyphMaker.getInstance(textHeight);
BufferedImage newImage = glyphMaker.getImage(cell, textWidth,
textHeight, getBackend(), cursorBlinkVisible);

View file

@ -512,6 +512,27 @@ public class StringUtils {
return ((ch >= 0x1f004) && (ch <= 0x1fffd));
}
/**
* Check if character is in the Symbols for Legacy Computing range.
*
* @param ch character to check
* @return true if this character is in the Symbols for Legacy Computing
* range
*/
public static boolean isLegacyComputingSymbol(final int ch) {
return ((ch >= 0x1fb00) && (ch <= 0x1fbff));
}
/**
* Check if character is in the braille range.
*
* @param ch character to check
* @return true if this character is in the braille range
*/
public static boolean isBraille(final int ch) {
return ((ch >= 0x2800) && (ch <= 0x28ff));
}
// ------------------------------------------------------------------------
// Base64 -----------------------------------------------------------------
// ------------------------------------------------------------------------