Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    primarykey
    data
    text
    <p>Why not just compare glyphs with something like this?</p> <pre><code>package similarglyphcharacterdetector; import java.awt.Color; import java.awt.Font; import java.awt.Graphics2D; import java.awt.Rectangle; import java.awt.font.FontRenderContext; import java.awt.image.BufferedImage; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.Map; public class SimilarGlyphCharacterDetector { static char[] TEST_CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890".toCharArray(); static BufferedImage[] SAMPLES = null; public static BufferedImage drawGlyph(Font font, String string) { FontRenderContext frc = ((Graphics2D) new BufferedImage(1, 1, BufferedImage.TYPE_BYTE_GRAY).getGraphics()).getFontRenderContext(); Rectangle r= font.getMaxCharBounds(frc).getBounds(); BufferedImage res = new BufferedImage(r.width, r.height, BufferedImage.TYPE_BYTE_GRAY); Graphics2D g = (Graphics2D) res.getGraphics(); g.setBackground(Color.WHITE); g.fillRect(0, 0, r.width, r.height); g.setPaint(Color.BLACK); g.setFont(font); g.drawString(string, 0, r.height - font.getLineMetrics(string, g.getFontRenderContext()).getDescent()); return res; } private static void drawSamples(Font f) { SAMPLES = new BufferedImage[TEST_CHARS.length]; for (int i = 0; i &lt; TEST_CHARS.length; i++) SAMPLES[i] = drawGlyph(f, String.valueOf(TEST_CHARS[i])); } private static int compareImages(BufferedImage img1, BufferedImage img2) { if (img1.getWidth() != img2.getWidth() || img1.getHeight() != img2.getHeight()) throw new IllegalArgumentException(); int d = 0; for (int y = 0; y &lt; img1.getHeight(); y++) { for (int x = 0; x &lt; img1.getWidth(); x++) { if (img1.getRGB(x, y) != img2.getRGB(x, y)) d++; } } return d; } private static int nearestSampleIndex(BufferedImage image, int maxDistance) { int best = Integer.MAX_VALUE; int bestIdx = -1; for (int i = 0; i &lt; SAMPLES.length; i++) { int diff = compareImages(image, SAMPLES[i]); if (diff &lt; best) { best = diff; bestIdx = i; } } if (best &gt; maxDistance) return -1; return bestIdx; } public static void main(String[] args) throws Exception { Font f = new Font("FreeMono", Font.PLAIN, 13); drawSamples(f); HashMap&lt;Character, StringBuilder&gt; res = new LinkedHashMap&lt;Character, StringBuilder&gt;(); for (char c : TEST_CHARS) res.put(c, new StringBuilder(String.valueOf(c))); int maxDistance = 5; for (int i = 0x80; i &lt;= 0xFFFF; i++) { char c = (char)i; if (f.canDisplay(c)) { int n = nearestSampleIndex(drawGlyph(f, String.valueOf(c)), maxDistance); if (n != -1) { char nc = TEST_CHARS[n]; res.get(nc).append(c); } } } for (Map.Entry&lt;Character, StringBuilder&gt; entry : res.entrySet()) if (entry.getValue().length() &gt; 1) System.out.println(entry.getValue()); } } </code></pre> <p>Output:</p> <pre><code>AÀÁÂÃÄÅĀĂĄǍǞȀȦΆΑΛАѦӒẠẢἈἉᾸᾹᾺᾼ₳Å BƁƂΒБВЬḂḄḆ CĆĈĊČƇΓЄГСὉℂⅭ ... </code></pre>
    singulars
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. VO
      singulars
      1. This table or related slice is empty.
    2. VO
      singulars
      1. This table or related slice is empty.
    1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload