Package fontastic

Source Code of fontastic.Fontastic

/**
* Fontastic
* A font file writer to create TTF and WOFF (Webfonts).
* http://code.andreaskoller.com/libraries/fontastic
*
* Copyright (C) 2013 Andreas Koller http://andreaskoller.com
*
* Uses:
*  doubletype http://sourceforge.net/projects/doubletype/ for TTF creation
*  sfntly http://code.google.com/p/sfntly/ for WOFF creation
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General
* Public License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place, Suite 330,
* Boston, MA  02111-1307  USA
*
* @author      Andreas Koller http://andreaskoller.com
* @modified    06/19/2013
* @version     0.4 (4)
*/

package fontastic;

import processing.core.*;

import fontastic.FGlyph;
import fontastic.FContour;
import fontastic.FPoint;

import org.doubletype.ossa.*;
import org.doubletype.ossa.module.*;
import org.doubletype.ossa.truetype.*;
import org.doubletype.ossa.adapter.*;

import com.google.typography.font.sfntly.Font;
import com.google.typography.font.sfntly.FontFactory;
import com.google.typography.font.sfntly.data.WritableFontData;
import com.google.typography.font.tools.conversion.woff.WoffWriter;

import java.io.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import java.util.List;
import java.util.regex.Pattern;
import java.util.regex.Matcher;

/**
* Fontastic A font file writer to create TTF and WOFF (Webfonts). http://code.andreaskoller.com/libraries/fontastic
*
*/
public class Fontastic {

  private PApplet myParent;

  private org.doubletype.ossa.module.TypefaceFile typeface;
  private org.doubletype.ossa.Engine m_engine;

  private String fontname;

  private String TTFfilename;
  private String WOFFfilename;
  private String HTMLfilename;
 
  private List<FGlyph> glyphs;

  private int advanceWidth = 512;

  public final static String VERSION = "0.4";
  private boolean debug = true; // debug toggles println calls

  /** Uppercase alphabet 26 characters **/
  public final static char alphabet[] = { 'A', 'B', 'C', 'D', 'E', 'F', 'G',
      'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T',
      'U', 'V', 'W', 'X', 'Y', 'Z' };
  /** Lowercase alphabet 26 characters **/
  public final static char alphabetLc[] = { 'a', 'b', 'c', 'd', 'e', 'f',
      'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's',
      't', 'u', 'v', 'w', 'x', 'y', 'z' };

  /**
   * Return the version of the library.
   *
   * @return String
   */
  public static String version() {
    return VERSION;
  }

  /**
   * Constructor
   *
   * @example Fontastic f = new Fontastic(this, "MyFont");
   *
   * @param theParent
   *            Your processing sketch (this).
   * @param fontname
   *            Font name
   *
   */
  public Fontastic(PApplet myParent, String fontname) {
    this.myParent = myParent;
    this.fontname = fontname;
    intitialiseFont();
    this.glyphs = new ArrayList<FGlyph>();
  }

  /**
   * Returns the font name.
   *
   * @return String
   */
  public String getFontname() {
    return fontname;
  }

  /**
   * Creates and initialises a new typeface. Font data is put into sketch
   * folder data/fontname.
   */
  private void intitialiseFont() {
    File data_dir = new File(myParent.dataPath(""));
    if (!data_dir.exists()) {
      data_dir.mkdir();
    }

    File a_dir = new File(myParent.dataPath(fontname));
    if (!a_dir.exists()) {
      a_dir.mkdir();
    } else {
      deleteFolderContents(a_dir, false);
    }

    m_engine = Engine.getSingletonInstance();
    m_engine.buildNewTypeface(fontname, a_dir);

    this.setFontFamilyName(fontname);
    this.setVersion("CC BY-SA 3.0 http://creativecommons.org/licenses/by-sa/3.0/"); // default
                                            // license

    String directoryName = a_dir + File.separator + "bin" + File.separator;

    TTFfilename = directoryName + fontname + ".ttf";
    WOFFfilename = directoryName + fontname + ".woff";
    HTMLfilename = directoryName + "template.html";
  }

  /**
   * Builds the font and writes the .ttf and the .woff file as well as a HTML template for previewing the WOFF.
   * If debug is set (default is true) then you'll see the .ttf and .woff file name in the console.
   */
  public void buildFont() {
   
    // Create TTF file with doubletype
   
    m_engine.addDefaultGlyphs();

    for (FGlyph glyph : glyphs) {

      GlyphFile glyphFile = m_engine.addNewGlyph(glyph.getGlyphChar());
      glyphFile.setAdvanceWidth(glyph.getAdvanceWidth());

      for (FContour contour : glyph.getContours()) {

        EContour econtour = new EContour();
        econtour.setType(EContour.k_cubic);

        for (FPoint point : contour.points) {

          EContourPoint e = new EContourPoint(point.x, point.y, true);

          if (point.hasControlPoint1()) {
            EControlPoint cp1 = new EControlPoint(true,
                point.controlPoint1.x, point.controlPoint1.y);
            e.setControlPoint1(cp1);
          }

          if (point.hasControlPoint2()) {
            EControlPoint cp2 = new EControlPoint(false,
                point.controlPoint2.x, point.controlPoint2.y);
            e.setControlPoint2(cp2);
          }

          econtour.addContourPoint(e);
        }

        glyphFile.addContour(econtour);
      }
      glyphFile.saveGlyphFile();
    }

    m_engine.buildTrueType(false);
    if (debug)
      System.out.println("TTF file created successfully: " + getTTFfilename());

    // End TTF creation
   
    // Create a WOFF file from this TTF file using sfntly

    FontFactory fontFactory = FontFactory.getInstance();

    File fontFile = new File(getTTFfilename());
    File outputFile = new File(WOFFfilename);

    byte[] fontBytes = new byte[0];
    try {
      FileInputStream fis = new FileInputStream(fontFile);
      fontBytes = new byte[(int) fontFile.length()];
      fis.read(fontBytes);
    } catch (IOException e) {
      System.out
          .println("Error while creating WOFF File. TTF file not found: "
              + getTTFfilename());
      e.printStackTrace();
    }

    Font[] fontArray = null;
    try {
      fontArray = fontFactory.loadFonts(fontBytes);
    } catch (IOException e) {
      System.out
          .println("Error while creating WOFF File. TTF file could not be read: "
              + getTTFfilename());
      e.printStackTrace();
    }
    Font font = fontArray[0];

    try {
      FileOutputStream fos = new FileOutputStream(outputFile);
      WoffWriter w = new WoffWriter();
      WritableFontData woffData = w.convert(font);
      woffData.copyTo(fos);
      if (debug)
        System.out.println("WOFF File created successfully: "
            + getWOFFfilename());
    } catch (IOException e) {
      System.out
          .println("Error while creating WOFF File. WOFF file could not be written."
              + outputFile);
      e.printStackTrace();
    }

    // End of WOFF creation
   
    // Create HTML Template for WOFF file
    String htmlTemplate = myParent.join(myParent.loadStrings("template.html"), "\n");
    Map<String, String> params = new HashMap<String, String>();
    params.put("FONTNAME", fontname);
    params.put("WOFFFILENAME", getFontname()+".woff");
    String htmlContent = replaceAll(htmlTemplate, params);
   
    myParent.saveStrings(HTMLfilename, myParent.split(htmlContent, "\n"));
    // End HTML Template
  }

  /**
   * Deletes all the glyph files created by doubletype in your data/fontname
   * folder.
   */
  public void cleanup() {

    File a_dir = new File(myParent.dataPath(fontname));
    File[] filesToExclude = new File[3];
    filesToExclude[0] = new File(getTTFfilename());
    filesToExclude[1] = new File(getWOFFfilename());
    filesToExclude[2] = new File(HTMLfilename);

    deleteFolderContents(a_dir, true, filesToExclude);
    if (debug)
      System.out
          .println("Cleaned up and deleted all glyph files, except font files.");

  }

  /**
   * Sets the author of the font.
   */
  public void setAuthor(String author) {
    m_engine.setAuthor(author);
  }

  /**
   * Sets the copyright year of the font.
   */
  public void setCopyrightYear(String copyrightYear) {
    m_engine.setCopyrightYear(copyrightYear);
  }

  /**
   * Sets the version of the font (default is "0.1").
   */
  public void setVersion(String version) {
    m_engine.getTypeface().getGlyph().getHead().setVersion(version);
  }

  /**
   * Sets the font family name of the font. Also called in the constructor. If
   * changed with setFontFamilyName() it won't affect folder the font is
   * stored in.
   */
  public void setFontFamilyName(String fontFamilyName) {
    m_engine.setFontFamilyName(fontFamilyName);
  }

  /**
   * Sets the sub family of the font.
   */
  public void setSubFamily(String subFamily) {
    m_engine.getTypeface().setSubFamily(subFamily);
  }

  /**
   * Sets the license of the font (default is
   * "CC BY-SA 3.0 http://creativecommons.org/licenses/by-sa/3.0/")
   */
  public void setTypefaceLicense(String typefaceLicense) {
    m_engine.setTypefaceLicense(typefaceLicense);
  }

  /**
   * Sets the baseline of the font.
   */
  public void setBaseline(float baseline) {
    m_engine.setBaseline(baseline);
  }

  /**
   * Sets the meanline of the font.
   */
  public void setMeanline(float meanline) {
    m_engine.setMeanline(meanline);
  }

  /**
   * Sets the advanceWidth of the font. Can be changed for every glyph
   * individually. Won't affect already created glyphs.
   */
  public void setAdvanceWidth(int advanceWidth) {
    m_engine.setAdvanceWidth(advanceWidth);
    this.advanceWidth = advanceWidth;
  }

  public void setTopSideBearing(float topSideBearing) {
    try {
      m_engine.getTypeface().setTopSideBearing(topSideBearing);
    } catch (OutOfRangeException e) {
      System.out
          .println("Error while setting aopSideBearing (must be within range "
              + m_engine.getTypeface().getEm());
      e.printStackTrace();
    }
  }

  public void setBottomSideBearing(float bottomSideBearing) {
    try {
      m_engine.getTypeface().setBottomSideBearing(bottomSideBearing);
    } catch (OutOfRangeException e) {
      System.out
          .println("Error while setting bottomSideBearing (must be within range "
              + m_engine.getTypeface().getEm());
      e.printStackTrace();
    }
  }

  public void setAscender(float ascender) {
    try {
      m_engine.getTypeface().setAscender(ascender);
    } catch (OutOfRangeException e) {
      System.out
          .println("Error while setting ascender (must be within range 0 to "
              + m_engine.getTypeface().getEm() + ")");
      e.printStackTrace();
    }
  }

  public void setDescender(float descender) {
    try {
      m_engine.getTypeface().setDescender(descender);
    } catch (OutOfRangeException e) {
      System.out
          .println("Error while setting descender (must be within range 0 to "
              + m_engine.getTypeface().getEm() + ")");
      e.printStackTrace();
    }
  }

  public void setXHeight(float xHeight) {
    try {
      m_engine.getTypeface().setXHeight(xHeight);
    } catch (OutOfRangeException e) {
      System.out
          .println("Error while setting xHeight (must be within range 0 to "
              + m_engine.getTypeface().getEm()
              + " as well as lower than the ascender "
              + m_engine.getTypeface().getAscender() + ")");
      e.printStackTrace();
    }
  }

  /**
   * Sets the default metrics for the typeface: setTopSideBearing(170); // 2
   * px setAscender(683); // 8 px setXHeight(424); // 5 px setDescender(171);
   * // 2 px setBottomSideBearing(0); // 0px
   *
   */
  public void setDefaultMetrics() {
    m_engine.getTypeface().setDefaultMetrics();
  }

  /**
   * Sets debug to true (in debug mode, the library outputs what happens under
   * the hood).
   */
  public void setDebug() {
    setDebug(true);
  }

  /**
   * Sets the value of debug
   *
   * @param debug
   *            true or false
   */
  public void setDebug(boolean debug) {
    this.debug = debug;
  }

  /**
   * Add a glyph
   *
   * @param c
   *            Character of the glyph.
   *
   * @return FGlyph that has been created.
   *
   */
  public FGlyph addGlyph(char c) {

    FGlyph glyph = new FGlyph(c);
    glyph.setAdvanceWidth(advanceWidth);
    glyphs.add(glyph);
    if (debug)
      System.out.println("Glyph " + c + " added. Number of glyphs: "
          + glyphs.size());
    return glyph;

  }

  /**
   * Add a glyph and its one contour
   *
   * @param c
   *            Character of the glyph.
   *
   * @param FContour
   *            Shape of the glyph as FContour.
   *
   * @return The glyph FGlyph that has been created. You can use this to store
   *         the glyph and add contours afterwards. Alternatively, you can
   *         call getGlyph(char c) to retrieve it.
   */
  public FGlyph addGlyph(char c, FContour contour) {

    FGlyph glyph = new FGlyph(c);
    glyphs.add(glyph);
    if (debug)
      System.out.println("Glyph " + c + " added. Number of glyphs: "
          + glyphs.size());

    glyph.addContour(contour);

    glyph.setAdvanceWidth(advanceWidth);
    return glyph;

  }

  /**
   * Add a glyph and its contours
   *
   * @param c
   *            Character of the glyph.
   *
   * @param FContour
   *            [] Shape of the glyph in an array of FContour.
   *
   * @return The FGlyph that has been created. You can use this to store the
   *         glyph and add contours afterwards. Alternatively, you can call
   *         getGlyph(char c) to retrieve it.
   */
  public FGlyph addGlyph(char c, FContour[] contours) {

    FGlyph glyph = new FGlyph(c);
    glyphs.add(glyph);
    if (debug)
      System.out.println("Glyph " + c + " added. Number of glyphs: "
          + glyphs.size());

    for (FContour contour : contours) {
      // if (debug) System.out.println(p.x + " - " + p.y);
      glyph.addContour(contour);
    }
    glyph.setAdvanceWidth(advanceWidth);
    return glyph;

  }

  /**
   * Get glyph by character
   *
   * @param c
   *            The character of the glyph
   *
   * @return The glyph
   */

  public FGlyph getGlyph(char c) {

    FGlyph glyph = null;
    for (int i = 0; i < glyphs.size(); i++) {
      if (glyphs.get(i).getGlyphChar() == c) {
        glyph = glyphs.get(i);
        break;
      }
    }
    return glyph;

  }

  /**
   * Engine getter
   *
   * @return The doubletype Engine used for font creation, so that you can
   *         access all functions of doubletype in case you need them.
   */
  public Engine getEngine() {
    return m_engine;
  }

  /**
   * Returns the TypefaceFile
   *
   * @return The doubletype TypefaceFile used for font creation, so that you
   *         can access functions of doubletype in case you need them.
   */
  public TypefaceFile getTypefaceFile() {
    return m_engine.getTypeface();
  }

  /**
   * Returns the .ttf file name
   *
   * @return The .ttf file name, which is being created when you call build()
   */
  public String getTTFfilename() {
    return TTFfilename;
  }

  /**
   * Returns the .woff file name
   *
   * @return The .woff file name, which is being created when you call build()
   */
  public String getWOFFfilename() {
    return WOFFfilename;
  }

  private static void deleteFolderContents(File folder,
      boolean deleteFolderItself) {
    File[] files = folder.listFiles();
    if (files != null) { // some JVMs return null for empty dirs
      for (File f : files) {
        if (f.isDirectory()) {
          deleteFolderContents(f, true);
          f.delete();
        } else {
          f.delete();
        }
      }
    }
    if (deleteFolderItself)
      folder.delete();
  }

  private static void deleteFolderContents(File folder,
      boolean deleteFolderItself, File[] exceptions) {
    File[] files = folder.listFiles();
    if (files != null) { // some JVMs return null for empty dirs
      for (File f : files) {
        boolean deleteFile = true;
        for (File exceptfile : exceptions) {
          if (f.equals(exceptfile))
            deleteFile = false;
        }
        if (deleteFile) {
          if (f.isDirectory()) {
            deleteFolderContents(f, true, exceptions);
            f.delete();
          } else {
            f.delete();
          }
        }
      }
    }
    if (deleteFolderItself)
      folder.delete();
  }

  private String sketchName() {
    String s = myParent.sketchPath("");
    s = s.substring(0, s.length() - 1);
    String sketchName = s.substring(s.lastIndexOf(File.separator) + 1,
        s.length());
    return sketchName;
  }

  // http://stackoverflow.com/questions/2368802/how-to-create-dynamic-template-string
  // Author: cletus http://stackoverflow.com/users/18393/cletus
  private static String replaceAll(String text, Map<String, String> params) {
    return replaceAll(text, params, '%', '%');
  }

  // http://stackoverflow.com/questions/2368802/how-to-create-dynamic-template-string
  // Author: cletus http://stackoverflow.com/users/18393/cletus
  private static String replaceAll(String text, Map<String, String> params,
      char leading, char trailing) {
    String pattern = "";
    if (leading != 0) {
      pattern += leading;
    }
    pattern += "(\\w+)";
    if (trailing != 0) {
      pattern += trailing;
    }
    Pattern p = Pattern.compile(pattern);
    Matcher m = p.matcher(text);
    boolean result = m.find();
    if (result) {
      StringBuffer sb = new StringBuffer();
      do {
        String replacement = params.get(m.group(1));
        if (replacement == null) {
          replacement = m.group();
        }
        m.appendReplacement(sb, replacement);
        result = m.find();
      } while (result);
      m.appendTail(sb);
      return sb.toString();
    }
    return text;
  }

}
TOP

Related Classes of fontastic.Fontastic

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.