Replace internal vector font with LibreCAD's GPLv2+ vector font.
This font is less complete than our bitmap font, Unifont: Unifont has essentially complete Unicode coverage and LibreCAD's font only has Latin, Cyrillic and Japanese, but it can be extended rather easily, so this should be fine for now. These embedded fonts fatten glhelper.o quite a bit: bitmapfont.table.h is about 8M in gzip-compressed bitmaps and vectorfont.table.h is about 2M in raw vector data. In spite of that it takes just around five seconds to build glhelper.c on my laptop, so it should be fine. The final executable grows from about 2M to about 8M, but this is a small price to pay for fairly extensive i18n support. The new font has somewhat different metrics, so the rendering code has been fudged to make it look good.pull/4/head
parent
784f3e5548
commit
57fb3bf3dc
|
@ -102,7 +102,15 @@ add_custom_command(
|
|||
${chars}
|
||||
DEPENDS unifont2c ${chars})
|
||||
|
||||
add_custom_command(
|
||||
OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/generated/vectorfont.table.h"
|
||||
COMMAND $<TARGET_FILE:lff2c>
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/generated/vectorfont.table.h"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/fonts/unicode.lff.gz"
|
||||
DEPENDS lff2c ${chars})
|
||||
|
||||
set(generated_HEADERS
|
||||
${CMAKE_CURRENT_BINARY_DIR}/generated/vectorfont.table.h
|
||||
${CMAKE_CURRENT_BINARY_DIR}/generated/bitmapfont.table.h
|
||||
${CMAKE_CURRENT_BINARY_DIR}/generated/icons.h)
|
||||
|
||||
|
@ -236,7 +244,6 @@ set(solvespace_HEADERS
|
|||
config.h
|
||||
dsc.h
|
||||
expr.h
|
||||
font.table.h
|
||||
polygon.h
|
||||
sketch.h
|
||||
solvespace.h
|
||||
|
|
|
@ -93,7 +93,7 @@ void Constraint::DoLabel(Vector ref, Vector *labelPos, Vector gr, Vector gu) {
|
|||
}
|
||||
|
||||
std::string s = Label();
|
||||
double swidth = ssglStrWidth(s.c_str(), th),
|
||||
double swidth = ssglStrWidth(s, th),
|
||||
sheight = ssglStrHeight(th);
|
||||
|
||||
// By default, the reference is from the center; but the style could
|
||||
|
@ -164,7 +164,7 @@ int Constraint::DoLineTrimmedAgainstBox(Vector ref, Vector a, Vector b) {
|
|||
|
||||
double pixels = 1.0 / SS.GW.scale;
|
||||
std::string s = Label();
|
||||
double swidth = ssglStrWidth(s.c_str(), DEFAULT_TEXT_HEIGHT) + 4*pixels,
|
||||
double swidth = ssglStrWidth(s, DEFAULT_TEXT_HEIGHT) + 4*pixels,
|
||||
sheight = ssglStrHeight(DEFAULT_TEXT_HEIGHT) + 8*pixels;
|
||||
|
||||
struct {
|
||||
|
@ -370,7 +370,7 @@ void Constraint::DoArcForAngle(Vector a0, Vector da, Vector b0, Vector db,
|
|||
// complex and this looks pretty good.
|
||||
double tl = atan2(rm.Dot(gu), rm.Dot(gr));
|
||||
double adj = EllipticalInterpolation(
|
||||
ssglStrWidth(Label().c_str(), DEFAULT_TEXT_HEIGHT)/2,
|
||||
ssglStrWidth(Label(), DEFAULT_TEXT_HEIGHT)/2,
|
||||
ssglStrHeight(DEFAULT_TEXT_HEIGHT)/2,
|
||||
tl);
|
||||
*ref = (*ref).Plus(rm.WithMagnitude(adj + 3/SS.GW.scale));
|
||||
|
|
|
@ -614,7 +614,7 @@ void Entity::DrawOrGetDistance(void) {
|
|||
if(dogd.drawing) {
|
||||
ssglWriteText(str.c_str(), th, mm2, u, v, NULL, NULL);
|
||||
} else {
|
||||
Vector pos = mm2.Plus(u.ScaledBy(ssglStrWidth(str.c_str(), th)/2)).Plus(
|
||||
Vector pos = mm2.Plus(u.ScaledBy(ssglStrWidth(str, th)/2)).Plus(
|
||||
v.ScaledBy(ssglStrHeight(th)/2));
|
||||
Point2d pp = SS.GW.ProjectPoint(pos);
|
||||
dogd.dmin = min(dogd.dmin, pp.DistanceTo(dogd.mp) - 10);
|
||||
|
|
1774
src/font.table.h
1774
src/font.table.h
File diff suppressed because it is too large
Load Diff
Binary file not shown.
130
src/glhelper.cpp
130
src/glhelper.cpp
|
@ -8,8 +8,8 @@
|
|||
|
||||
namespace SolveSpace {
|
||||
|
||||
// A public-domain Hershey vector font ("Simplex").
|
||||
#include "font.table.h"
|
||||
// A vector font.
|
||||
#include "generated/vectorfont.table.h"
|
||||
|
||||
// A bitmap font.
|
||||
#include "generated/bitmapfont.table.h"
|
||||
|
@ -17,25 +17,51 @@ namespace SolveSpace {
|
|||
static bool ColorLocked;
|
||||
static bool DepthOffsetLocked;
|
||||
|
||||
#define FONT_SCALE(h) ((h)/22.0)
|
||||
double ssglStrWidth(const char *str, double h)
|
||||
{
|
||||
int w = 0;
|
||||
for(; *str; str++) {
|
||||
int c = *str;
|
||||
if(c < 32 || c > 126) c = 32;
|
||||
c -= 32;
|
||||
|
||||
w += Font[c].width;
|
||||
static const VectorGlyph &GetVectorGlyph(char32_t chr) {
|
||||
int first = 0;
|
||||
int last = sizeof(VectorFont) / sizeof(VectorGlyph);
|
||||
while(first <= last) {
|
||||
int mid = (first + last) / 2;
|
||||
char32_t midChr = VectorFont[mid].character;
|
||||
if(midChr > chr) {
|
||||
last = mid - 1; // and first stays the same
|
||||
continue;
|
||||
}
|
||||
return w*FONT_SCALE(h)/SS.GW.scale;
|
||||
if(midChr < chr) {
|
||||
first = mid + 1; // and last stays the same
|
||||
continue;
|
||||
}
|
||||
return VectorFont[mid];
|
||||
}
|
||||
return GetVectorGlyph(0xfffd); // replacement character
|
||||
}
|
||||
|
||||
#define FONT_SCALE(h) ((h)/75.0)
|
||||
double ssglStrWidth(const std::string &str, double h)
|
||||
{
|
||||
int width = 0;
|
||||
const char *iter = str.c_str();
|
||||
while(*iter) {
|
||||
char32_t chr;
|
||||
iter = ReadUTF8(iter, &chr);
|
||||
|
||||
const VectorGlyph &glyph = GetVectorGlyph(chr);
|
||||
int glyphWidth = glyph.width;
|
||||
if(glyph.baseCharacter != 0) {
|
||||
const VectorGlyph &baseGlyph = GetVectorGlyph(glyph.baseCharacter);
|
||||
width += max(glyph.width, baseGlyph.width);
|
||||
} else {
|
||||
width += glyph.width;
|
||||
}
|
||||
}
|
||||
return width * FONT_SCALE(h) / SS.GW.scale;
|
||||
}
|
||||
double ssglStrHeight(double h)
|
||||
{
|
||||
// The characters have height ~22, as they appear in the table.
|
||||
return 22.0*FONT_SCALE(h)/SS.GW.scale;
|
||||
// The characters have height ~90, as they appear in the table.
|
||||
return 90.0 * FONT_SCALE(h) / SS.GW.scale;
|
||||
}
|
||||
void ssglWriteTextRefCenter(const char *str, double h, Vector t, Vector u, Vector v,
|
||||
void ssglWriteTextRefCenter(const std::string &str, double h, Vector t, Vector u, Vector v,
|
||||
ssglLineFn *fn, void *fndata)
|
||||
{
|
||||
u = u.WithMagnitude(1);
|
||||
|
@ -82,43 +108,55 @@ static void LineDrawCallback(void *fndata, Vector a, Vector b)
|
|||
glEnd();
|
||||
}
|
||||
|
||||
void ssglWriteText(const char *str, double h, Vector t, Vector u, Vector v,
|
||||
int ssglDrawCharacter(const VectorGlyph &glyph, Vector t, Vector o, Vector u, Vector v,
|
||||
double scale, ssglLineFn *fn, void *fndata) {
|
||||
int width = glyph.width;
|
||||
|
||||
if(glyph.baseCharacter != 0) {
|
||||
const VectorGlyph &baseGlyph = GetVectorGlyph(glyph.baseCharacter);
|
||||
int baseWidth = ssglDrawCharacter(baseGlyph, t, o, u, v, scale, fn, fndata);
|
||||
width = max(glyph.width, baseWidth);
|
||||
}
|
||||
|
||||
const int8_t *data = glyph.data;
|
||||
bool pen_up = true;
|
||||
Vector prevp;
|
||||
while(true) {
|
||||
int8_t x = *data++;
|
||||
int8_t y = *data++;
|
||||
|
||||
if(x == PEN_UP && y == PEN_UP) {
|
||||
if(pen_up) break;
|
||||
pen_up = true;
|
||||
} else {
|
||||
Vector p = t;
|
||||
p = p.Plus(u.ScaledBy((o.x + x) * scale));
|
||||
p = p.Plus(v.ScaledBy((o.y + y) * scale));
|
||||
if(!pen_up) fn(fndata, prevp, p);
|
||||
prevp = p;
|
||||
pen_up = false;
|
||||
}
|
||||
}
|
||||
|
||||
return width;
|
||||
}
|
||||
|
||||
void ssglWriteText(const std::string &str, double h, Vector t, Vector u, Vector v,
|
||||
ssglLineFn *fn, void *fndata)
|
||||
{
|
||||
if(!fn) fn = LineDrawCallback;
|
||||
u = u.WithMagnitude(1);
|
||||
v = v.WithMagnitude(1);
|
||||
|
||||
double scale = FONT_SCALE(h)/SS.GW.scale;
|
||||
int xo = 5;
|
||||
int yo = 5;
|
||||
double scale = FONT_SCALE(h) / SS.GW.scale;
|
||||
Vector o = { 5.0, 5.0 };
|
||||
const char *iter = str.c_str();
|
||||
while(*iter) {
|
||||
char32_t chr;
|
||||
iter = ReadUTF8(iter, &chr);
|
||||
|
||||
for(; *str; str++) {
|
||||
int c = *str;
|
||||
if(c < 32 || c > 126) c = 32;
|
||||
|
||||
c -= 32;
|
||||
|
||||
int j;
|
||||
Vector prevp = Vector::From(VERY_POSITIVE, 0, 0);
|
||||
for(j = 0; j < Font[c].points; j++) {
|
||||
int x = Font[c].coord[j*2];
|
||||
int y = Font[c].coord[j*2+1];
|
||||
|
||||
if(x == PEN_UP && y == PEN_UP) {
|
||||
prevp.x = VERY_POSITIVE;
|
||||
} else {
|
||||
Vector p = t;
|
||||
p = p.Plus(u.ScaledBy((xo + x)*scale));
|
||||
p = p.Plus(v.ScaledBy((yo + y)*scale));
|
||||
if(EXACT(prevp.x != VERY_POSITIVE)) {
|
||||
fn(fndata, prevp, p);
|
||||
}
|
||||
prevp = p;
|
||||
}
|
||||
}
|
||||
|
||||
xo += Font[c].width;
|
||||
const VectorGlyph &glyph = GetVectorGlyph(chr);
|
||||
o.x += ssglDrawCharacter(glyph, t, o, u, v, scale, fn, fndata);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -330,11 +330,11 @@ void ssglDrawEdges(SEdgeList *l, bool endpointsToo);
|
|||
void ssglDebugMesh(SMesh *m);
|
||||
void ssglMarkPolygonNormal(SPolygon *p);
|
||||
typedef void ssglLineFn(void *data, Vector a, Vector b);
|
||||
void ssglWriteText(const char *str, double h, Vector t, Vector u, Vector v,
|
||||
void ssglWriteText(const std::string &str, double h, Vector t, Vector u, Vector v,
|
||||
ssglLineFn *fn, void *fndata);
|
||||
void ssglWriteTextRefCenter(const char *str, double h, Vector t, Vector u, Vector v,
|
||||
void ssglWriteTextRefCenter(const std::string &str, double h, Vector t, Vector u, Vector v,
|
||||
ssglLineFn *fn, void *fndata);
|
||||
double ssglStrWidth(const char *str, double h);
|
||||
double ssglStrWidth(const std::string &str, double h);
|
||||
double ssglStrHeight(double h);
|
||||
void ssglLockColorTo(RgbaColor rgb);
|
||||
void ssglFatLine(Vector a, Vector b, double width);
|
||||
|
|
|
@ -15,3 +15,9 @@ add_executable(unifont2c
|
|||
|
||||
target_link_libraries(unifont2c
|
||||
${PNG_LIBRARIES} ${ZLIB_LIBRARIES})
|
||||
|
||||
add_executable(lff2c
|
||||
lff2c.cpp)
|
||||
|
||||
target_link_libraries(lff2c
|
||||
${ZLIB_LIBRARIES})
|
||||
|
|
|
@ -0,0 +1,396 @@
|
|||
#define _USE_MATH_DEFINES
|
||||
#include <zlib.h>
|
||||
#include <cmath>
|
||||
#include <cctype>
|
||||
#include <algorithm>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <map>
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
|
||||
#define TOLERANCE 1e-6
|
||||
|
||||
double correctAngle(double a) {
|
||||
return M_PI + remainder(a - M_PI, 2 * M_PI);
|
||||
}
|
||||
|
||||
struct Point {
|
||||
double x;
|
||||
double y;
|
||||
|
||||
Point operator+(const Point &o) const { return { x + o.x, y + o.y }; }
|
||||
Point operator-(const Point &o) const { return { x - o.x, y - o.y }; }
|
||||
Point operator*(const Point &o) const { return { x * o.x, y * o.y }; }
|
||||
Point operator/(const Point &o) const { return { x / o.x, y / o.y }; }
|
||||
|
||||
Point operator*(double k) const { return { x * k, y * k }; }
|
||||
Point operator/(double k) const { return { x / k, y / k }; }
|
||||
|
||||
double length() const{
|
||||
return sqrt(x * x + y * y);
|
||||
}
|
||||
|
||||
double angle() const {
|
||||
return correctAngle(atan2(y, x));
|
||||
}
|
||||
|
||||
double distanceTo(const Point &v) const {
|
||||
return (*this - v).length();
|
||||
}
|
||||
|
||||
double angleTo(const Point &v) const {
|
||||
return (v - *this).angle();
|
||||
}
|
||||
|
||||
static Point polar(double radius, double angle) {
|
||||
return { radius * cos(angle), radius * sin(angle) };
|
||||
}
|
||||
|
||||
bool operator==(const Point &o) const { return x == o.x && y == o.y; }
|
||||
bool operator!=(const Point &o) const { return x != o.x || y != o.y; }
|
||||
|
||||
};
|
||||
|
||||
struct Curve {
|
||||
std::vector<Point> points;
|
||||
};
|
||||
|
||||
struct Glyph {
|
||||
char32_t character;
|
||||
char32_t baseCharacter;
|
||||
std::vector<Curve> curves;
|
||||
|
||||
void getBoundWidth(double *rminw, double *rmaxw) const {
|
||||
if(curves.empty()) {
|
||||
*rminw = 0.0;
|
||||
*rmaxw = 0.0;
|
||||
return;
|
||||
}
|
||||
double minw = curves[0].points[0].x;
|
||||
double maxw = minw;
|
||||
for(const Curve &c : curves) {
|
||||
for(const Point &p : c.points) {
|
||||
maxw = std::max(maxw, p.x);
|
||||
minw = std::min(minw, p.x);
|
||||
}
|
||||
}
|
||||
*rminw = minw;
|
||||
*rmaxw = maxw;
|
||||
}
|
||||
|
||||
void getBoundHeight(double *rminh, double *rmaxh) const {
|
||||
if(curves.empty()) {
|
||||
*rminh = 0.0;
|
||||
*rmaxh = 0.0;
|
||||
return;
|
||||
}
|
||||
double minh = curves[0].points[0].y;
|
||||
double maxh = minh;
|
||||
for(const Curve &c : curves) {
|
||||
for(const Point &p : c.points) {
|
||||
maxh = std::max(maxh, p.y);
|
||||
minh = std::min(minh, p.y);
|
||||
}
|
||||
}
|
||||
*rminh = minh;
|
||||
*rmaxh = maxh;
|
||||
}
|
||||
|
||||
double getWidth() const {
|
||||
double maxw;
|
||||
double minw;
|
||||
getBoundWidth(&minw, &maxw);
|
||||
return maxw - minw;
|
||||
}
|
||||
|
||||
double getHeight() const {
|
||||
double maxh;
|
||||
double minh;
|
||||
getBoundHeight(&minh, &maxh);
|
||||
return maxh - minh;
|
||||
}
|
||||
|
||||
bool operator<(const Glyph &o) const { return character < o.character; }
|
||||
};
|
||||
|
||||
struct Font {
|
||||
double letterSpacing;
|
||||
double wordSpacing;
|
||||
std::vector<Glyph> glyphs;
|
||||
|
||||
void getGlyphBound(double *rminw, double *rminh, double *rmaxw, double *rmaxh) {
|
||||
if(glyphs.empty()) {
|
||||
*rminw = 0.0;
|
||||
*rmaxw = 0.0;
|
||||
*rminh = 0.0;
|
||||
*rmaxh = 0.0;
|
||||
return;
|
||||
}
|
||||
|
||||
glyphs[0].getBoundWidth(rminw, rmaxw);
|
||||
glyphs[0].getBoundHeight(rminh, rmaxh);
|
||||
for(const Glyph &g : glyphs) {
|
||||
double minw, minh, maxw, maxh;
|
||||
g.getBoundWidth(&minw, &maxw);
|
||||
g.getBoundHeight(&minh, &maxh);
|
||||
*rminw = std::min(*rminw, minw);
|
||||
*rminh = std::min(*rminh, minh);
|
||||
*rmaxw = std::max(*rmaxw, maxw);
|
||||
*rmaxh = std::max(*rmaxh, maxh);
|
||||
}
|
||||
}
|
||||
|
||||
void createArc(Curve &curve, const Point &cp, double radius,
|
||||
double a1, double a2, bool reversed) {
|
||||
if (radius < 1e-6) return;
|
||||
|
||||
double aSign = 1.0;
|
||||
if(reversed) {
|
||||
if(a1 <= a2 + TOLERANCE) a1 += 2.0 * M_PI;
|
||||
aSign = -1.0;
|
||||
} else {
|
||||
if(a2 <= a1 + TOLERANCE) a2 += 2.0 * M_PI;
|
||||
}
|
||||
|
||||
// Angle Step (rad)
|
||||
double da = fabs(a2 - a1);
|
||||
int numPoints = 8;
|
||||
double aStep = aSign * da / double(numPoints);
|
||||
|
||||
for(int i = 0; i <= numPoints; i++) {
|
||||
curve.points.push_back(cp + Point::polar(radius, a1 + aStep * i));
|
||||
}
|
||||
}
|
||||
|
||||
void createBulge(const Point &v, double bulge, Curve &curve) {
|
||||
bool reversed = bulge < 0.0;
|
||||
double alpha = atan(bulge) * 4.0;
|
||||
Point &point = curve.points.back();
|
||||
|
||||
Point middle = (point + v) / 2.0;
|
||||
double dist = point.distanceTo(v) / 2.0;
|
||||
double angle = point.angleTo(v);
|
||||
|
||||
// alpha can't be 0.0 at this point
|
||||
double radius = fabs(dist / sin(alpha / 2.0));
|
||||
double wu = fabs(radius*radius - dist*dist);
|
||||
double h = sqrt(wu);
|
||||
|
||||
if(bulge > 0.0) {
|
||||
angle += M_PI_2;
|
||||
} else {
|
||||
angle -= M_PI_2;
|
||||
}
|
||||
|
||||
if (fabs(alpha) > M_PI) {
|
||||
h *= -1.0;
|
||||
}
|
||||
|
||||
Point center = Point::polar(h, angle);
|
||||
center = center + middle;
|
||||
|
||||
double a1 = center.angleTo(point);
|
||||
double a2 = center.angleTo(v);
|
||||
createArc(curve, center, radius, a1, a2, reversed);
|
||||
}
|
||||
|
||||
void readLff(const std::string &path) {
|
||||
gzFile lfffont = gzopen(path.c_str(), "rb");
|
||||
if(!lfffont) {
|
||||
std::cerr << path << ": gzopen failed" << std::endl;
|
||||
std::exit(1);
|
||||
}
|
||||
|
||||
// Read line by line until we find a new letter:
|
||||
Glyph *currentGlyph = NULL;
|
||||
while(!gzeof(lfffont)) {
|
||||
std::string line;
|
||||
do {
|
||||
char buf[128] = {0};
|
||||
if(!gzgets(lfffont, buf, sizeof(buf)))
|
||||
break;
|
||||
line += buf;
|
||||
} while(line.back() != '\n');
|
||||
|
||||
if(line.empty() || line[0] == '\n') {
|
||||
continue;
|
||||
} else if(line[0] == '#') {
|
||||
// This is comment or metadata.
|
||||
std::istringstream ss(line.substr(1));
|
||||
|
||||
std::vector<std::string> tokens;
|
||||
while(!ss.eof()) {
|
||||
std::string token;
|
||||
std::getline(ss, token, ':');
|
||||
std::istringstream(token) >> token; // trim
|
||||
if(!token.empty())
|
||||
tokens.push_back(token);
|
||||
}
|
||||
|
||||
// If not in form of "a:b" then it's not metadata, just a comment.
|
||||
if (tokens.size() != 2)
|
||||
continue;
|
||||
|
||||
std::string &identifier = tokens[0];
|
||||
std::string &value = tokens[1];
|
||||
|
||||
std::transform(identifier.begin(), identifier.end(), identifier.begin(),
|
||||
::tolower);
|
||||
if (identifier == "letterspacing") {
|
||||
std::istringstream(value) >> letterSpacing;
|
||||
} else if (identifier == "wordspacing") {
|
||||
std::istringstream(value) >> wordSpacing;
|
||||
} else if (identifier == "linespacingfactor") {
|
||||
/* don't care */
|
||||
} else if (identifier == "author") {
|
||||
/* don't care */
|
||||
} else if (identifier == "name") {
|
||||
/* don't care */
|
||||
} else if (identifier == "license") {
|
||||
/* don't care */
|
||||
} else if (identifier == "encoding") {
|
||||
/* don't care */
|
||||
} else if (identifier == "created") {
|
||||
/* don't care */
|
||||
}
|
||||
} else if(line[0] == '[') {
|
||||
// This is a glyph.
|
||||
size_t closingPos;
|
||||
char32_t chr = std::stoi(line.substr(1), &closingPos, 16);;
|
||||
if(line[closingPos + 1] != ']') {
|
||||
std::cerr << "unrecognized character number: " << line << std::endl;
|
||||
currentGlyph = NULL;
|
||||
continue;
|
||||
}
|
||||
|
||||
glyphs.emplace_back();
|
||||
currentGlyph = &glyphs.back();
|
||||
currentGlyph->character = chr;
|
||||
currentGlyph->baseCharacter = 0;
|
||||
} else if(currentGlyph != NULL) {
|
||||
if (line[0] == 'C') {
|
||||
// This is a reference to another glyph.
|
||||
currentGlyph->baseCharacter = std::stoi(line.substr(1), NULL, 16);
|
||||
} else {
|
||||
// This is a series of curves.
|
||||
currentGlyph->curves.emplace_back();
|
||||
Curve &curve = currentGlyph->curves.back();
|
||||
|
||||
std::stringstream ss(line);
|
||||
while (!ss.eof()) {
|
||||
std::string vertex;
|
||||
std::getline(ss, vertex, ';');
|
||||
|
||||
std::stringstream ssv(vertex);
|
||||
std::string coord;
|
||||
Point p;
|
||||
|
||||
if(!std::getline(ssv, coord, ',')) continue;
|
||||
p.x = std::stod(coord);
|
||||
|
||||
if(!std::getline(ssv, coord, ',')) continue;
|
||||
p.y = std::stod(coord);
|
||||
|
||||
if(!std::getline(ssv, coord, ',') || coord[0] != 'A') {
|
||||
// This is just a point.
|
||||
curve.points.push_back(p);
|
||||
} else {
|
||||
// This is a point with a bulge.
|
||||
double bulge = std::stod(coord.substr(1));
|
||||
createBulge(p, bulge, curve);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
std::cerr << "unrecognized line: " << line << std::endl;
|
||||
}
|
||||
}
|
||||
gzclose(lfffont);
|
||||
}
|
||||
|
||||
void writeCppHeader(const std::string &hName) {
|
||||
std::sort(glyphs.begin(), glyphs.end());
|
||||
|
||||
std::ofstream ts(hName, std::ios::out);
|
||||
|
||||
double minX, minY, maxX, maxY;
|
||||
getGlyphBound(&minX, &minY, &maxX, &maxY);
|
||||
|
||||
double size = 126.0;
|
||||
double scale = size / std::max({ fabs(maxX), fabs(minX), fabs(maxY), fabs(minY) });
|
||||
|
||||
// We use tabs for indentation here to make compilation slightly faster
|
||||
ts <<
|
||||
"/**** This is a generated file - do not edit ****/\n\n"
|
||||
"#ifndef __VECTORFONT_TABLE_H\n"
|
||||
"#define __VECTORFONT_TABLE_H\n"
|
||||
"\n"
|
||||
"#define PEN_UP 127\n"
|
||||
"#define UP PEN_UP\n"
|
||||
"\n"
|
||||
"struct VectorGlyph {\n"
|
||||
"\tchar32_t character;\n"
|
||||
"\tchar32_t baseCharacter;\n"
|
||||
"\tint width;\n"
|
||||
"\tconst int8_t *data;\n"
|
||||
"};\n"
|
||||
"\n"
|
||||
"const int8_t VectorFontData[] = {\n"
|
||||
"\tUP, UP,\n";
|
||||
|
||||
std::map<char32_t, size_t> glyphIndexes;
|
||||
size_t index = 2;
|
||||
for(const Glyph &g : glyphs) {
|
||||
ts << "\t// U+" << std::hex << g.character << std::dec << "\n";
|
||||
glyphIndexes[g.character] = index;
|
||||
for(const Curve &c : g.curves) {
|
||||
for(const Point &p : c.points) {
|
||||
ts << "\t" << int(floor(p.x * scale)) << ", " <<
|
||||
int(floor(p.y * scale)) << ",\n";
|
||||
index += 2;
|
||||
}
|
||||
ts << "\tUP, UP,\n";
|
||||
index += 2;
|
||||
}
|
||||
ts << "\tUP, UP,\n"; // end-of-glyph marker
|
||||
index += 2;
|
||||
}
|
||||
|
||||
ts <<
|
||||
"};\n"
|
||||
"\n"
|
||||
"const VectorGlyph VectorFont[] = {\n"
|
||||
"\t// U+20\n"
|
||||
"\t{ 32, 0, " << int(floor(wordSpacing * scale)) << ", &VectorFontData[0] },\n";
|
||||
|
||||
for(const Glyph &g : glyphs) {
|
||||
ts << "\t// U+" << std::hex << g.character << std::dec << "\n";
|
||||
ts << "\t{ " << g.character << ", "
|
||||
<< g.baseCharacter << ", "
|
||||
<< int(floor((g.getWidth() + letterSpacing) * scale)) << ", ";
|
||||
ts << "&VectorFontData[" << glyphIndexes[g.character] << "] },\n";
|
||||
}
|
||||
|
||||
ts <<
|
||||
"};\n"
|
||||
"\n"
|
||||
"#undef UP\n"
|
||||
"\n"
|
||||
"#endif\n";
|
||||
}
|
||||
};
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
if(argc != 3) {
|
||||
std::cerr << "Usage: " << argv[0] << " <header out> <lff in>\n" << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
Font font;
|
||||
font.readLff(argv[2]);
|
||||
font.writeCppHeader(argv[1]);
|
||||
|
||||
return 0;
|
||||
}
|
Loading…
Reference in New Issue