0

I am trying to make an image to text program that converts a 0-255 value to a character that matches it (visually). The important thing here is having all characters the same width so in a text editor the text is square...

basically I want all characters from 32-4000 that have the same width as 'X' so I can use 'Q' but not '|' (this font is mono tho so there is no difference)

I am using the Unifont font.

What I have currently works, but I want to supply a general range of unicode characters that are all the same size (so it looks good in text at the end)

    BufferedImage img = new BufferedImage(1, 1, BufferedImage.TYPE_INT_RGB);
    Color fntC = new Color(255, 255, 255);
    Graphics2D g = img.createGraphics();
    g.setFont(f);
    FontMetrics fm = g.getFontMetrics();
    for (int i = 32; i <= 4000/*126*/; i++) { //33 126
        if (fm.charWidth((char)i) == fm.charWidth('X') && Character.isLetter(i))
            dictionary.add(new letter((char) i, getValue((char) i)));
    }

This code works pretty well, but I still get characters like 'ȷ' that has a smaller width in the text editor but is treated as the same width as 'X' according to font metrics (I'm using Courier new since that's the default of Notepad++)

MOAR CODE:

import java.awt.Color;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Random;

import javax.imageio.ImageIO;
import javax.swing.JFileChooser;
import javax.swing.filechooser.FileNameExtensionFilter;

public class MainImageToText {

    public static void main(String[] args) {
        CharDictionary cd = new CharDictionary(new Font("Unifont", 1, 20));
        JFileChooser chooser = new JFileChooser();
        /*FileNameExtensionFilter filter = new FileNameExtensionFilter("PNG Images", "png");
        chooser.setFileFilter(filter);*/
        chooser.setCurrentDirectory(new File(System.getProperty("user.home"), "Desktop"));
        int returnVal = chooser.showOpenDialog(null);
        File f = chooser.getSelectedFile();
        if (returnVal == JFileChooser.APPROVE_OPTION) {
            System.out.println("\nYou chose to open this file: " + f);

            File output = new File(f.getParentFile(), "$output.txt");
            saveTextFile(output, ImageToText(f, cd));
        }
    }

    static String ImageToText(File f, CharDictionary cd) {
        BufferedImage img = null;
        String text = "";
        try {
            img = ImageIO.read(f);
        } catch (IOException e) {
            e.printStackTrace();
        }

        int w = img.getWidth();
        int h = img.getHeight();

        for (int y = 0; y < h; y++) {
            for (int x = 0; x < w; x++) {
                Color col = new Color(img.getRGB(x, y));
                int red = col.getRed();
                int green = col.getGreen();
                int blue = col.getBlue();
                int finalAverage = (red + green + blue) / 3;
                text += cd.getDictionaryLetter(finalAverage);
                System.out.println(x + ", " + y);
            }
            text += "\n";
        }
        cd.debugPrintDictionary();
        // System.out.println(text);
        return text;
    }

    static void saveTextFile(File f, String s) {

        File desktop = new File(System.getProperty("user.home"), "Desktop");
        File outputfile = new File(desktop.toString() + "\\" + "file.txt");
        try {
            System.out.println("creating file...");
            PrintWriter out = new PrintWriter(outputfile, "UTF-8");
            out.write(s);
            out.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
        System.out.println("Saved to: " + f);
    }
}

class letter {
    public String c;
    public int val;

    letter(String ch, int dub) {
        c = ch;
        val = dub;
    }

    public char getChar() {
        Random rand = new Random();
        return c.charAt(rand.nextInt(c.length()));
    }

    public void addChar(char ch) {
        c += ch;
    }

    public void addChar(String ch) {
        c += ch;
    }
}

class LetterComparator implements Comparator<letter> {
    @Override
    public int compare(letter a, letter b) {
        return (a.val - b.val); // *100 for more precision
    }
}

class CharDictionary {
    private Font f;
    private List<letter> dictionary = new ArrayList<letter>();

    CharDictionary(Font font) {
        f = font;
        createDictionary();
    }

    public void createDictionary() {
        BufferedImage img = new BufferedImage(1, 1, BufferedImage.TYPE_INT_RGB);
        Color fntC = new Color(255, 255, 255);
        Graphics2D g = img.createGraphics();
        g.setFont(f);
        FontMetrics fm = g.getFontMetrics();
        dictionary.add(new letter(" ", getValue(' ')));
        for (int i = 32; i <= 65000; i++) { //33 126
            if (fm.charWidth(i) == fm.charWidth('X') && !Character.isWhitespace(i))
                dictionary.add(new letter((char) i + "", getValue((char) i)));
        }
        sort();
        compact();
    }

    public void sort() {
        Collections.sort(dictionary, new LetterComparator());
    }

    public void compact() {
        int val;
        for (int i = dictionary.size()-1; i > 0 ; i--) {
            val = dictionary.get(i).val;
            if (val == dictionary.get(i-1).val) {
                dictionary.get(i-1).addChar(dictionary.get(i).c);
                dictionary.remove(i);
            }

        }
    }

    public void debugPrintDictionary() {
        for (int i = 0; i < dictionary.size(); i++) {
            System.out.println("Char: " + dictionary.get(i).c + " Value: " + dictionary.get(i).val);
        }
    }

    /*public int getIndexofVal(int n) {
        return null;
    }*/

    public char getDictionaryLetter(int val) {
        for (int i = 0; i < dictionary.size(); i++) {
            int charvalue = dictionary.get(i).val;
            if (charvalue >= 255-val) {//inverted here

                return dictionary.get(i).getChar();
            }
        }

        return dictionary.get(dictionary.size() - 1).getChar(); /// PROBLEM HERE
    }

    public int getValue(char c) {
        BufferedImage img = new BufferedImage(1, 1, BufferedImage.TYPE_INT_RGB);
        Color fntC = new Color(255, 255, 255);
        Graphics2D g = img.createGraphics();

        g.setFont(f);

        FontMetrics fm = g.getFontMetrics();

        int width = fm.charWidth(c);
        int height = fm.getAscent()+ fm.getDescent(); // too big

        img = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
        g = img.createGraphics();
        g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
        g.setColor(fntC);
        g.setFont(f);

        // g.fill
        g.drawString(c + "", 0, fm.getAscent());

        int w = img.getWidth();
        int h = img.getHeight();
        double finalAverage = 0;

        for (int y = 0; y < h; y++) {
            for (int x = 0; x < w; x++) {
                Color col = new Color(img.getRGB(x, y));
                int red = col.getRed();
                int green = col.getGreen();
                int blue = col.getBlue();
                finalAverage += (red + green + blue) / 3;

            }
        }

        finalAverage /= w * h;

        /*try {
            File desktop = new File(System.getProperty("user.home"), "Desktop");
            File outputfile = new File(desktop.toString() + "\\letters\\" + (int) c + ".png");
            ImageIO.write(img, "png", outputfile);
        } catch (IOException e) {

        }*/

        return (int)finalAverage;
    }
}
Jugbot
  • 142
  • 1
  • 14
  • 1
    I am not so sure what you are actually trying to do. But if you are really using [Courier New](https://en.wikipedia.org/wiki/Courier_(typeface)) (the definition of `f` is missing in your above snippet) then equal widths for all characters aren't surprising since that font is [monospaced](https://en.wikipedia.org/wiki/Monospaced_font). – morido Jan 21 '16 at 21:27
  • So even if two characters are are NOT the same width like above, it will return that the two characters are the same anyway? – Jugbot Jan 22 '16 at 00:38
  • How are characters handled that are not in the font? Does a "default" font take over? – Jugbot Jan 22 '16 at 01:07
  • What do you mean by *NOT the same width*? In a monospaced font there must not be any characters that have different widths - that's what monospacing is all about. So even an "i" will (technically) be as wide as an "M". *Courier New* covers [quite a bit](https://www.microsoft.com/typography/fonts/font.aspx?FMID=1735) of the Unicode range. Which glyphs have you tried that are not part of the font? – morido Jan 22 '16 at 08:23
  • uh... I'm trying a pretty vast range-- from 32 to 4000. A little ridiculous, but I want to see if I can get as many characters as possible that are the same width. I know Arabic characters definitely cause problems, but there are also relatively "normal" characters that seem to have a different width despite being mono. – Jugbot Jan 22 '16 at 21:36
  • Ok, it does seem like characters outside the font are ignored because I got a character with 0 width after the width check. What now? – Jugbot Jan 22 '16 at 21:48
  • Well, I'd love to help you but I just have no clue what you're actually trying to achieve... – morido Jan 22 '16 at 21:51
  • I just don't understand why certain characters are measured as equal with FontMetrics, but in reality they can have vastly different widths. – Jugbot Jan 22 '16 at 22:07
  • Ok, provide a fully working example that exhibits your observed behavior and then we may have a chance to unterstand your problem. – morido Jan 22 '16 at 22:13
  • Updated question, might as well give all the code too... – Jugbot Jan 22 '16 at 22:17
  • Added all the code. Ready to run. – Jugbot Jan 22 '16 at 22:21
  • Ahh... *Fully working* doesn't mean I want all your code! I wanted an [MCVE](http://stackoverflow.com/help/mcve). Any chance you can trim this down so the code does nothing more than to show your problem?! Other than that: Are you really sure you want `int finalAverage = (red + green + blue) / 3;` instead of `int finalAverage = (int) Math.round((red + green + blue) / 3.0);`? – morido Jan 22 '16 at 22:39
  • That doesn't solve the problem, but yeah I guess so. Again, my problem is that 'FontMetrics.charWidth(char)' returns a width that is inconsistent with the actual width on some letters. – Jugbot Jan 22 '16 at 22:54
  • You can try my program if you want. It might explain more... – Jugbot Jan 22 '16 at 22:58

0 Answers0