What fun is a hinting implementation without a tool to try it out. Here
it comes now. It allows you to:

- Select single glyphs and show their hinted and unhinted outline
(magnified) as well as the real rendered glyph.
- Set various hinting options. In order to see the actual hinting
process, deselect edge/strong/weak hinting and the select them again, in
this order. You can also disable/enable horizontal and vertical hinting.
- Load arbitrary TTF files.
- Show how complete strings are rendered.

In order to try it, start the program with:
java -cp $(CLASSPATH) gnu.classpath.examples.awt.HintingDemo

then load a font via the file menu and have fun.

If you experience problems with GNU Classpath (there's a glitch in
CairoGraphics2D which makes the magnified view funny, and the file
chooser is also not 100% reliable), this demo also works with other VMs.
Simply include the examples.zip AND the classpath install in the
classpath.

2006-12-16  Roman Kennke  <[EMAIL PROTECTED]>

        * examples/gnu/classpath/examples/awt/HintingDemo.java:
        New demonstration program for the autohinter.

/Roman

Index: examples/gnu/classpath/examples/awt/HintingDemo.java
===================================================================
RCS file: examples/gnu/classpath/examples/awt/HintingDemo.java
diff -N examples/gnu/classpath/examples/awt/HintingDemo.java
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ examples/gnu/classpath/examples/awt/HintingDemo.java	16 Dec 2006 20:52:06 -0000
@@ -0,0 +1,377 @@
+/* Demo.java -- Shows examples of AWT components
+   Copyright (C) 1998, 1999, 2002, 2004, 2006 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath examples.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING.  If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA. */
+
+package gnu.classpath.examples.awt;
+
+import gnu.java.awt.font.*;
+import gnu.java.awt.font.opentype.*;
+
+import java.awt.*;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.font.*;
+import java.io.*;
+import java.nio.*;
+import java.nio.channels.*;
+import java.text.*;
+
+import javax.swing.BoxLayout;
+import javax.swing.JCheckBox;
+import javax.swing.JFileChooser;
+import javax.swing.JFrame;
+import javax.swing.JMenu;
+import javax.swing.JMenuBar;
+import javax.swing.JMenuItem;
+import javax.swing.JPanel;
+import javax.swing.JSpinner;
+import javax.swing.JTextField;
+import javax.swing.border.TitledBorder;
+import javax.swing.event.ChangeEvent;
+import javax.swing.event.ChangeListener;
+
+public class HintingDemo extends JFrame {
+
+  FontDelegate font;
+  GNUGlyphVector glyph;
+  GlyphPreview glyphPreview;
+  HintPanel hintPanel;
+  StringViewer stringViewer;
+  Chooser chooser;
+  char character;
+  Options options;
+  boolean antiAlias;
+  int flags;
+
+  class StringViewer extends JPanel
+  implements ActionListener
+  {
+    JTextField input;
+    GNUGlyphVector gv;
+    Viewer viewer;
+    StringViewer()
+    {
+      setLayout(new GridLayout(0, 1));
+      setBorder(new TitledBorder("Use this field to render complete strings"));
+      input = new JTextField();
+      input.addActionListener(this);
+      add(input);
+      viewer = new Viewer();
+      add(viewer);
+    }
+
+    public void actionPerformed(ActionEvent event)
+    {
+      refresh();
+    }
+
+    void refresh()
+    {
+      gv = (GNUGlyphVector)
+      font.createGlyphVector(new Font("Dialog", 0, 12),
+      new FontRenderContext(null, false, false),
+      new StringCharacterIterator(input.getText()));
+      viewer.repaint();
+    }
+
+    class Viewer extends JPanel
+    {
+      protected void paintComponent(Graphics g)
+      {
+        if (gv != null && g instanceof Graphics2D)
+          {
+            Graphics2D g2d = (Graphics2D) g;
+            if (antiAlias)
+              {
+                g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
+                                     RenderingHints.VALUE_ANTIALIAS_ON);
+              }
+            else
+              {
+                g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
+                                     RenderingHints.VALUE_ANTIALIAS_OFF);
+              }
+            g2d.clearRect(0, 0, getWidth(), getHeight());
+            g2d.setColor(Color.BLACK);
+            Shape outline = gv.getOutline(0, 0,
+                                          flags | FontDelegate.FLAG_FITTED);
+            g2d.translate(20, Math.floor(outline.getBounds2D().getHeight()) + 2);
+            g2d.fill(outline);
+          }
+      }
+    }
+  }
+
+  class HintPanel extends JPanel
+  {
+
+    HintPanel()
+    {
+      setBorder(new TitledBorder("Detailed glyph view"));
+    }
+    protected void paintComponent(Graphics g)
+    {
+      if (glyph != null && g instanceof Graphics2D)
+        {
+          Graphics2D g2d = (Graphics2D) g.create();
+          Insets i = getInsets();
+          g2d.clearRect(i.left, i.top, getWidth() - i.left - i.right,
+                        getHeight() - i.top - i.bottom);
+          g2d.setColor(Color.GRAY);
+          for (int x = 20; x < getWidth(); x += 20)
+            {
+              g2d.drawLine(x, i.top, x, getHeight() - i.top - i.bottom);
+            }
+          for (int y = 20; y < getHeight(); y += 20)
+            {
+              g2d.drawLine(i.left, y, getWidth() - i.left - i.right, y);
+            }
+          g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
+                               RenderingHints.VALUE_ANTIALIAS_OFF);
+          g2d.translate(40, 300);
+          g2d.scale(20., 20.);
+          g2d.setStroke(new BasicStroke((float) (1/20.)));
+          g2d.setColor(Color.RED);
+          g2d.draw(glyph.getOutline(0, 0, flags & ~FontDelegate.FLAG_FITTED));
+          g2d.setColor(Color.GREEN);
+          g2d.draw(glyph.getOutline(0, 0, flags | FontDelegate.FLAG_FITTED));
+      }
+    }
+
+  }
+
+  class GlyphPreview extends JPanel
+  {
+    protected void paintComponent(Graphics g)
+    {
+      if (glyph != null && g instanceof Graphics2D)
+        {
+          Graphics2D g2d = (Graphics2D) g;
+          if (antiAlias)
+            {
+              g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
+                                   RenderingHints.VALUE_ANTIALIAS_ON);
+            }
+          else
+            {
+              g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
+                                   RenderingHints.VALUE_ANTIALIAS_OFF);
+            }
+          g2d.clearRect(0, 0, getWidth(), getHeight());
+          g2d.setColor(Color.BLACK);
+          Shape outline = glyph.getOutline(0, 0,
+                                           flags | FontDelegate.FLAG_FITTED);
+          g2d.translate(20, outline.getBounds2D().getHeight() + 2);
+          g2d.fill(outline);
+        }
+    }
+
+  }
+
+  HintingDemo()
+  {
+    File file = new File("/var/lib/defoma/x-ttcidfont-conf.d/dirs/TrueType/DejaVuSerif.ttf");
+    loadFont(file);
+    setLayout(new BorderLayout());
+    chooser = new Chooser();
+    add(chooser, BorderLayout.NORTH);
+    hintPanel = new HintPanel();
+    character = 'A';
+    add(hintPanel);
+
+    options = new Options();
+    add(options, BorderLayout.EAST);
+
+    stringViewer = new StringViewer();
+    add(stringViewer, BorderLayout.SOUTH);
+    refresh();
+
+    JMenuBar mb = new JMenuBar();
+    setJMenuBar(mb);
+    JMenu fileMenu = new JMenu("File");
+    mb.add(fileMenu);
+    JMenuItem loadFont = new JMenuItem("Load font");
+    loadFont.addActionListener(new ActionListener(){
+      public void actionPerformed(ActionEvent ev)
+      {
+        JFileChooser fc = new JFileChooser()
+        {
+          public boolean accept(File f)
+          {
+            return f.isDirectory() || f.getName().endsWith(".ttf");
+          }
+        };
+        int status = fc.showOpenDialog(HintingDemo.this);
+        if (status == JFileChooser.APPROVE_OPTION)
+          {
+            File file = fc.getSelectedFile();
+            loadFont(file);
+          }
+      }
+    });
+    fileMenu.add(loadFont);
+  }
+
+  void refresh()
+  {
+    if (chooser != null)
+      chooser.refresh();
+    if (glyphPreview != null)
+      glyphPreview.repaint();
+    if (hintPanel != null)
+      hintPanel.repaint();
+    if (stringViewer != null)
+      stringViewer.refresh();
+  }
+
+  class Options extends JPanel
+    implements ActionListener
+  {
+    JCheckBox antiAliasOpt;
+    JCheckBox hintHorizontalOpt;
+    JCheckBox hintVerticalOpt;
+    JCheckBox hintEdgeOpt;
+    JCheckBox hintStrongOpt;
+    JCheckBox hintWeakOpt;
+    Options()
+    {
+      setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
+      setBorder(new TitledBorder("Hinting options"));
+      antiAliasOpt = new JCheckBox("Antialias");
+      antiAliasOpt.setSelected(true);
+      antiAliasOpt.addActionListener(this);
+      add(antiAliasOpt);
+      hintHorizontalOpt = new JCheckBox("Hint horizontal");
+      hintHorizontalOpt.setSelected(true);
+      hintHorizontalOpt.addActionListener(this);
+      add(hintHorizontalOpt);
+      hintVerticalOpt = new JCheckBox("Hint vertical");
+      hintVerticalOpt.setSelected(true);
+      hintVerticalOpt.addActionListener(this);
+      add(hintVerticalOpt);
+      hintEdgeOpt = new JCheckBox("Hint edge points");
+      hintEdgeOpt.setSelected(true);
+      hintEdgeOpt.addActionListener(this);
+      add(hintEdgeOpt);
+      hintStrongOpt = new JCheckBox("Hint strong points");
+      hintStrongOpt.setSelected(true);
+      hintStrongOpt.addActionListener(this);
+      add(hintStrongOpt);
+      hintWeakOpt = new JCheckBox("Hint weak points");
+      hintWeakOpt.setSelected(true);
+      hintWeakOpt.addActionListener(this);
+      add(hintWeakOpt);
+      sync();
+    }
+
+    void sync()
+    {
+      antiAlias = antiAliasOpt.isSelected();
+      if (hintHorizontalOpt.isSelected())
+        flags &= ~FontDelegate.FLAG_NO_HINT_HORIZONTAL;
+      else
+        flags |= FontDelegate.FLAG_NO_HINT_HORIZONTAL;
+      if (hintVerticalOpt.isSelected())
+        flags &= ~FontDelegate.FLAG_NO_HINT_VERTICAL;
+      else
+        flags |= FontDelegate.FLAG_NO_HINT_VERTICAL;
+      if (hintEdgeOpt.isSelected())
+        flags &= ~FontDelegate.FLAG_NO_HINT_EDGE_POINTS;
+      else
+        flags |= FontDelegate.FLAG_NO_HINT_EDGE_POINTS;
+      if (hintStrongOpt.isSelected())
+        flags &= ~FontDelegate.FLAG_NO_HINT_STRONG_POINTS;
+      else
+        flags |= FontDelegate.FLAG_NO_HINT_STRONG_POINTS;
+      if (hintWeakOpt.isSelected())
+        flags &= ~FontDelegate.FLAG_NO_HINT_WEAK_POINTS;
+      else
+        flags |= FontDelegate.FLAG_NO_HINT_WEAK_POINTS;
+        
+      refresh();
+    }
+
+    public void actionPerformed(ActionEvent event)
+    {
+      sync();
+    }
+  }
+
+  class Chooser extends JPanel
+  {
+    JSpinner spin;
+    Chooser()
+    {
+      setLayout(new GridLayout(1, 0));
+      setBorder(new TitledBorder("Choose and preview the character to render"));
+      spin = new JSpinner();
+      spin.addChangeListener(new ChangeListener()
+      {
+
+        public void stateChanged(ChangeEvent event)
+        {
+          int val = ((Integer) spin.getValue()).intValue();
+          setGlyph((char) val);
+        }
+      });
+      add(spin);
+      glyphPreview = new GlyphPreview();
+      add(glyphPreview);
+    }
+    void refresh()
+    {
+      spin.setValue(new Integer(character));
+      repaint();
+    }
+  }
+
+  private void loadFont(File file)
+  {
+    try
+      {
+        RandomAccessFile raf = new RandomAccessFile(file, "r");
+        FileChannel chan = raf.getChannel();
+        ByteBuffer buf = chan.map(FileChannel.MapMode.READ_ONLY, 0, raf.length());
+        FontDelegate[] fonts = OpenTypeFontFactory.createFonts(buf);
+        font = fonts[0];
+        setGlyph(character);
+        refresh();
+      }
+    catch (Exception ex)
+      {
+        ex.printStackTrace();
+      }
+  }
+
+  void setGlyph(char ch)
+  {
+    character = ch;
+    glyph = (GNUGlyphVector)
+            font.createGlyphVector(new Font("Dialog", 0, 12),
+            new FontRenderContext(null, false, false),
+            new StringCharacterIterator(new String(new char[]{ch})));
+    refresh();
+  }
+
+  public static void main(String[] args) {
+    HintingDemo f = new HintingDemo();
+    f.setSize(500, 500);
+    f.setVisible(true);
+  }
+}

Reply via email to