Subversion Repositories DIN Is Noise

Rev

Rev 1370 | Rev 1713 | Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | RSS feed

/*
* font.cc
* DIN Is Noise is copyright (c) 2006-2020 Jagannathan Sampath
* For more information, please visit https://dinisnoise.org/
*/



#include <fstream>
#include <sstream>
#include <cmath>
#include "font.h"
using namespace std;

extern string user_data_dir;
extern ofstream dlog;
static const char eol = '\n';

font::font (const string& filename) : modified (0)
#ifdef __SVG_FNT__
  ,svg ((user_data_dir + "text.svg").c_str(), ios::out)
#endif
#ifdef __PLOTTER_FNT__
  ,hpgl ((user_data_dir + "text.hpgl").c_str(), ios::out)
#endif
{
  x_cell_size = 2;
  y_cell_size = 2;
  charspc = 2 * x_cell_size;
  headroom = 2 * y_cell_size;
  fname = filename;
  load (fname);
#ifdef __SVG_FNT__
  svg << "<?xml version=\"1.0\"?>" << eol;
  svg << "<svg xmlns=\"https://www.w3.org/2000/svg\">" << eol;
#endif

#ifdef __PLOTTER_FNT__
  hpgl << "IN;" << eol;
#endif
}

font::~font () {
  if (modified) save ();
#ifdef __SVG_FNT__
  svg << "</svg>" << eol;
#endif
  dlog << "--- destroyed font ---" << endl;
}

void font::load (const string& fn) {
  string ffname (user_data_dir + fname);
  ifstream file (ffname.c_str(), ios::in);
  if (file) {
    dlog << "<<< loading font from: " << ffname;
    load (file);
    dlog << ", done. >>>" << eol;
  } else {
    dlog << "!!! bad font file: " << ffname << " !!!" << endl;
  }
}

void font::load (ifstream& file) {

  string ignore;

  characters.clear ();
  kern.clear ();

  file >> ignore >> name;
  file >> ignore >> nchars;

  int sum_char_width, sum_char_height;
  max_char_width = max_char_height = sum_char_width = sum_char_height = 0;

  for (int i = 0; i < nchars; i++) {
      char c; file >> c;
      glyph g;
      int nln; file >> nln;
      for (int l = 0; l < nln; ++l) {
        line ln;
        int npts; file >> npts;
        for (int s = 0; s < npts; ++s) {
          int x, y; file >> x >> y;
          ln.points.push_back (point<int>(x * x_cell_size, y * y_cell_size));
        }
        g.lines.push_back (ln);
      }

      g.find_width_height ();
      if (g.width > max_char_width) max_char_width = g.width;
      if (g.height > max_char_height) max_char_height = g.height;
      sum_char_width += g.width;
      sum_char_height += g.height;

      characters[c] = g;

  }

  avg_char_width = (int) (sum_char_width * 1.0f / nchars + 0.5);
  avg_char_height = (int)(sum_char_height * 1.0f / nchars + 0.5);

  lift = 2 * y_cell_size;

  wordspc = 2 * charspc;

  calc_line_height ();

  int nkerns = 0;
  file >> ignore >> nkerns;
  for (int i = 0; i < nkerns; ++i) {
    char a, b;
    int k;
    file >> a >> b >> k;
    kern [a][b] = k * x_cell_size;
  }

}

void font::calc_line_height () {
  extern int line_height;
  line_height = fnt.max_char_height + fnt.headroom;
}

void font::save () {

  ofstream file ((user_data_dir + fname).c_str(), ios::out);
  if (!file) { dlog << "cannot save font in: " << fname << eol; return;} else { dlog << "saving font in: " << fname << eol;}

  // build chars
  int nc = 0;
  stringstream ss1;
  for (map<char, glyph>::iterator i = characters.begin(), j = characters.end(); i != j; ++i) {
    glyph& g = (*i).second;
    vector<line>& lines = g.lines; // poly lines
    int nlines = lines.size ();
    if (nlines) {
      ss1 << (*i).first << ' ';
      ss1 << nlines << ' ';
      for (int s = 0; s < nlines; ++s) {
        vector < point<int> >& points = lines[s].points;
        int npts = points.size ();
        ss1 << npts << ' ';
        for (int m = 0; m < npts; ++m) {
          point<int>& pt = points[m];
          ss1 << (int) (pt.x / x_cell_size) << ' ' << (int) (pt.y / y_cell_size) << ' ';
        }
      }
      ss1 << eol;
      ++nc;
    }
  }

  // write chars
  file << "name " << name << eol;
  file << "num_chars " << nc << eol;
  file << ss1.str();

  // build kerns
  int nk = 0;
  stringstream ss2;
  for (map<char, map<char, int> >::iterator i = kern.begin(), j = kern.end(); i != j; ++i) {
    char a = (*i).first;
    map<char, int>& m = (*i).second;
    for (map<char, int>::iterator p = m.begin(), q = m.end(); p != q; ++p) {
      char b = (*p).first;
      int k = (*p).second;
      if (k != 0) {
        ss2 << a << ' ' << b << ' ' << k/x_cell_size << '\n';
        ++nk;
      }
    }
  }

  // write kerns
  file << "num_kerns " << nk << eol;
  file << ss2.str();

}

int font::char_width (char c) { return characters[c].width;}
int font::char_height (char c) { return characters[c].height;}

void font::draw_char (char c, int x, int y, int z) {
  const glyph& gl = characters[c];
  const vector<line>& lines = gl.lines;
  static const unsigned int MAX_CHAR_POINTS = 128;
  static int pdata [3 * MAX_CHAR_POINTS] = {0};
  glVertexPointer (3, GL_INT, 0, pdata);
  for (unsigned int i = 0, nlines = lines.size(); i < nlines; ++i) {
    const line& li = lines[i];
    const vector< point<int> >& points = li.points;
    unsigned int npts = points.size();
    if (npts < MAX_CHAR_POINTS) {
      int k = 0;
      for (unsigned int j = 0; j < npts; ++j) {
        const point<int>& p = points[j];
        pdata[k++] = x + p.x;
        pdata[k++] = y + p.y;
        pdata[k++] = z;
      }
      glDrawArrays (GL_LINE_STRIP, 0, npts);
    }
  }
}

const map<char, glyph>& font::get_chars () {
  return characters;
}

void font::set_chars (const map<char, glyph>& chars) {
  characters = chars;
  modified = 1;
}

int draw_string (const string& s, int x, int y, int z) {
  char prevc = ' ';
  for (int p = 0, q = s.length(); p < q; ++p) {
      char c = s[p];
      if (c == ' ') x += fnt.wordspc; else {
        x += fnt.kern[prevc][c];
        fnt.draw_char (c, x, y, z);
        x += (fnt.char_width (c) + fnt.charspc);
      }
      prevc = c;
  }
  return x;
}

int get_char_width (const string& s) {
  char prevc = ' ';
  int x = 0;
  for (int p = 0, q = s.length(); p < q; ++p) {
    char c = s[p];
    if (c == ' ')
      x += fnt.wordspc;
    else
      x = x + fnt.kern[prevc][c] + fnt.char_width (c) + fnt.charspc;
    prevc = c;
  }
  return x;
}

int get_max_char_height (const string& s) {
  int h = 0;
  for (int p = 0, q = s.length(); p < q; ++p) {
    char c = s[p];
    int ch = fnt.char_height (c);
    if (ch > h) h = ch;
  }
  return h;
}

#ifdef __PLOTTER_FNT__

void font::plot_char (char c, int x, int y, int z) {
  const glyph& gl = characters[c];
  const vector<line>& lines = gl.lines;
  for (unsigned int i = 0, nlines = lines.size(); i < nlines; ++i) {
    const line& li = lines[i];
    const vector< point<int> >& points = li.points;
    unsigned int npts = points.size();
    const point<int>& p0 = points[0];
    hpgl << "PU " << (x + p0.x) << ',' << (y + p0.y) << ';' << eol;
    hpgl << "PD;" << eol;
    for (unsigned int j = 0; j < npts; ++j) {
      const point<int>& p = points[j];
      int xp = x + p.x, yp = y + p.y;
      hpgl << "PA " << xp << ',' << yp << ';' << eol;
    }
  }
}

int plot_string (const string& s, int x, int y, int z) {
  char prevc = ' ';
  for (int p = 0, q = s.length(); p < q; ++p) {
      char c = s[p];
      if (c == ' ') x += fnt.wordspc; else {
        x += fnt.kern[prevc][c];
        fnt.plot_char (c, x, y, z);
        x += (fnt.char_width (c) + fnt.charspc);
      }
      prevc = c;
  }
  return x;
}
#endif

#ifdef __SVG_FNT__
void font::write_char (char c, int x, int y, int z) {
  const glyph& gl = characters[c];
  const vector<line>& lines = gl.lines;
  for (unsigned int i = 0, nlines = lines.size(); i < nlines; ++i) {
    const line& li = lines[i];
    const vector< point<int> >& points = li.points;
    unsigned int npts = points.size();
    svg << "<g>" << eol;
    svg << "<polyline fill=\"none\" stroke=\"black\" points=\"";
      for (unsigned int j = 0; j < npts; ++j) {
        const point<int>& p = points[j];
        glVertex3i(x + p.x, y + p.y, z);
        int xp = x + p.x, yp = y - p.y;
        svg << xp << "," << yp << " ";
      }
    svg << "\"/>" << eol;
    svg << "</g>" << eol;
  }
}

int write_string (const string& s, int x, int y, int z) {
  char prevc = ' ';
  for (int p = 0, q = s.length(); p < q; ++p) {
      char c = s[p];
      if (c == ' ') x += fnt.wordspc; else {
        x += fnt.kern[prevc][c];
        fnt.write_char (c, x, y, z);
        x += (fnt.char_width (c) + fnt.charspc);
      }
      prevc = c;
  }
  return x;
}
#endif