Add implementation of text() module.

text-module
Torsten Paul 2014-01-19 03:05:00 +01:00 committed by Marius Kintel
parent 9c9915a6f9
commit bb45e7e52a
17 changed files with 1088 additions and 6 deletions

View File

@ -13,3 +13,4 @@ include(eigen.pri)
include(boost.pri)
include(glib-2.0.pri)
include(sparkle.pri)
include(freetype.pri)

5
freetype.pri Normal file
View File

@ -0,0 +1,5 @@
freetype {
QMAKE_CXXFLAGS += `pkg-config --cflags freetype2 harfbuzz fontconfig`
LIBS += `pkg-config --libs freetype2 harfbuzz fontconfig`
}

View File

@ -155,6 +155,7 @@ CONFIG += opencsg
CONFIG += boost
CONFIG += eigen
CONFIG += glib-2.0
CONFIG += freetype
#Uncomment the following line to enable QCodeEdit
#CONFIG += qcodeedit
@ -230,6 +231,7 @@ HEADERS += src/typedefs.h \
src/transformnode.h \
src/colornode.h \
src/rendernode.h \
src/textnode.h \
src/openscad.h \
src/handle_dep.h \
src/Geometry.h \
@ -252,6 +254,9 @@ HEADERS += src/typedefs.h \
src/GeometryEvaluator.h \
src/CSGTermEvaluator.h \
src/Tree.h \
src/DrawingCallback.h \
src/FreetypeRenderer.h \
src/FontCache.h \
src/mathc99.h \
src/memory.h \
src/linalg.h \
@ -303,6 +308,7 @@ SOURCES += src/version_check.cc \
src/surface.cc \
src/control.cc \
src/render.cc \
src/text.cc \
src/dxfdata.cc \
src/dxfdim.cc \
src/linearextrude.cc \
@ -321,6 +327,9 @@ SOURCES += src/version_check.cc \
src/ModuleCache.cc \
src/GeometryCache.cc \
src/Tree.cc \
src/DrawingCallback.cc \
src/FreetypeRenderer.cc \
src/FontCache.cc \
\
src/rendersettings.cc \
src/highlighter.cc \

118
src/DrawingCallback.cc Normal file
View File

@ -0,0 +1,118 @@
/*
* OpenSCAD (www.openscad.org)
* Copyright (C) 2009-2011 Clifford Wolf <clifford@clifford.at> and
* Marius Kintel <marius@kintel.net>
*
* This program 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 of the License, or
* (at your option) any later version.
*
* As a special exception, you have permission to link this program
* with the CGAL library and distribute executables, as long as you
* follow the requirements of the GNU GPL in regard to all of the
* software in the executable aside from CGAL.
*
* This program 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 this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#include <math.h>
#include <iostream>
#include <algorithm>
#include "dxfdata.h"
#include "dxftess.h"
#include "polyset.h"
#include "DrawingCallback.h"
DrawingCallback::DrawingCallback(unsigned long fn) : fn(fn),
pen(Vector2d(0, 0)), offset(Vector2d(0, 0)), advance(Vector2d(0, 0))
{
}
DrawingCallback::~DrawingCallback()
{
}
void DrawingCallback::start_glyph()
{
data = new DxfData();
}
void DrawingCallback::finish_glyph()
{
PolySet *p = new PolySet();
p->is2d = true;
dxf_tesselate(p, *data, 0, Vector2d(1,1), true, false, 0);
dxf_border_to_ps(p, *data);
result.push_back(p);
delete data;
data = NULL;
}
std::vector<PolySet *> DrawingCallback::get_result()
{
return result;
}
void DrawingCallback::set_glyph_offset(double offset_x, double offset_y)
{
offset = Vector2d(offset_x, offset_y);
}
void DrawingCallback::add_glyph_advance(double advance_x, double advance_y)
{
advance += Vector2d(advance_x, advance_y);
}
void DrawingCallback::add_vertex(Vector2d v)
{
double x = offset[0] + advance[0];
double y = offset[1] + advance[1];
data->paths.back().indices.push_back(data->addPoint(x + v[0], y + v[1]));
}
void DrawingCallback::move_to(Vector2d to)
{
data->paths.push_back(DxfData::Path());
data->paths.back().is_closed = true;
add_vertex(to);
pen = to;
}
void DrawingCallback::line_to(Vector2d to)
{
add_vertex(to);
pen = to;
}
void DrawingCallback::curve_to(Vector2d c1, Vector2d to)
{
for (unsigned long idx = 1;idx <= fn;idx++) {
const double a = idx * (1.0 / (double)fn);
const double x = pen[0] * t(a, 2) + c1[0] * 2 * t(a, 1) * a + to[0] * a * a;
const double y = pen[1] * t(a, 2) + c1[1] * 2 * t(a, 1) * a + to[1] * a * a;
add_vertex(Vector2d(x, y));
}
pen = to;
}
void DrawingCallback::curve_to(Vector2d c1, Vector2d c2, Vector2d to)
{
for (unsigned long idx = 1;idx <= fn;idx++) {
const double a = idx * (1.0 / (double)fn);
const double x = pen[0] * t(a, 3) + c1[0] * 3 * t(a, 2) * a + c2[0] * 3 * t(a, 1) * a * a + to[0] * a * a * a;
const double y = pen[1] * t(a, 3) + c1[1] * 3 * t(a, 2) * a + c2[1] * 3 * t(a, 1) * a * a + to[1] * a * a * a;
add_vertex(Vector2d(x, y));
}
pen = to;
}

68
src/DrawingCallback.h Normal file
View File

@ -0,0 +1,68 @@
/*
* OpenSCAD (www.openscad.org)
* Copyright (C) 2009-2011 Clifford Wolf <clifford@clifford.at> and
* Marius Kintel <marius@kintel.net>
*
* This program 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 of the License, or
* (at your option) any later version.
*
* As a special exception, you have permission to link this program
* with the CGAL library and distribute executables, as long as you
* follow the requirements of the GNU GPL in regard to all of the
* software in the executable aside from CGAL.
*
* This program 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 this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#ifndef DRAWINGCALLBACK_H
#define DRAWINGCALLBACK_H
#include <vector>
#include <math.h>
#include <Eigen/Core>
#include "dxfdata.h"
#include "polyset.h"
class DrawingCallback {
public:
DrawingCallback(unsigned long fn);
virtual ~DrawingCallback();
void start_glyph();
void finish_glyph();
void set_glyph_offset(double offset_x, double offset_y);
void add_glyph_advance(double advance_x, double advance_y);
std::vector<PolySet *> get_result();
void move_to(Vector2d to);
void line_to(Vector2d to);
void curve_to(Vector2d c1, Vector2d to);
void curve_to(Vector2d c1, Vector2d c2, Vector2d to);
private:
unsigned long fn;
Vector2d pen;
Vector2d offset;
Vector2d advance;
DxfData *data;
std::vector<PolySet *> result;
void add_vertex(Vector2d v);
inline double t(double t, int exp) const {
return pow(1.0 - t, exp);
}
};
#endif /* DRAWINGCALLBACK_H */

227
src/FontCache.cc Normal file
View File

@ -0,0 +1,227 @@
/*
* OpenSCAD (www.openscad.org)
* Copyright (C) 2009-2011 Clifford Wolf <clifford@clifford.at> and
* Marius Kintel <marius@kintel.net>
*
* This program 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 of the License, or
* (at your option) any later version.
*
* As a special exception, you have permission to link this program
* with the CGAL library and distribute executables, as long as you
* follow the requirements of the GNU GPL in regard to all of the
* software in the executable aside from CGAL.
*
* This program 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 this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#include <iostream>
#include <boost/filesystem.hpp>
#include <boost/algorithm/string.hpp>
#include "boosty.h"
#include "FontCache.h"
namespace fs = boost::filesystem;
FontCache * FontCache::self = NULL;
FontCache::FontCache()
{
init_ok = false;
config = FcInitLoadConfigAndFonts();
if(!config) {
PRINT("Can't initialize fontconfig library, text() objects will not be rendered");
return;
}
add_font_dir("/System/Library/Fonts");
const char *home = getenv("HOME");
if (home) {
add_font_dir(std::string(home) + "/Library/Fonts");
add_font_dir(std::string(home) + "/.fonts");
}
const char *windir = getenv("WinDir");
if (windir) {
add_font_dir(std::string(windir) + "\\Fonts");
}
const FT_Error error = FT_Init_FreeType(&library);
if (error) {
PRINT("Can't initialize freetype library, text() objects will not be rendered");
return;
}
init_ok = true;
}
FontCache::~FontCache()
{
}
FontCache * FontCache::instance()
{
if (!self) {
self = new FontCache();
}
return self;
}
void FontCache::register_font_file(std::string path) {
if (!FcConfigAppFontAddFile(config, reinterpret_cast<const FcChar8 *>(path.c_str()))) {
PRINTB("Can't register font '%s'", path);
}
}
void FontCache::add_font_dir(std::string path) {
if (!fs::is_directory(path)) {
return;
}
if (!FcConfigAppFontAddDir(config, reinterpret_cast<const FcChar8 *>(path.c_str()))) {
PRINTB("Can't register font directory '%s'", path);
}
}
bool FontCache::is_init_ok()
{
return init_ok;
}
void FontCache::clear()
{
cache.clear();
}
void FontCache::dump_cache(std::string info)
{
std::cout << info << ":";
for (cache_t::iterator it = cache.begin();it != cache.end();it++) {
std::cout << " " << (*it).first << " (" << (*it).second.second << ")";
}
std::cout << std::endl;
}
void FontCache::check_cleanup()
{
if (cache.size() < MAX_NR_OF_CACHE_ENTRIES) {
return;
}
cache_t::iterator pos = cache.begin()++;
for (cache_t::iterator it = cache.begin();it != cache.end();it++) {
if ((*pos).second.second > (*it).second.second) {
pos = it;
}
}
FT_Done_Face((*pos).second.first);
cache.erase(pos);
}
FT_Face FontCache::get_font(std::string font)
{
FT_Face face;
cache_t::iterator it = cache.find(font);
if (it == cache.end()) {
face = find_face(font);
if (!face) {
return NULL;
}
check_cleanup();
} else {
face = (*it).second.first;
}
cache[font] = cache_entry_t(face, time(NULL));
return face;
}
FT_Face FontCache::find_face(std::string font)
{
FT_Face face;
face = find_face_fontconfig(font);
return face ? face : find_face_in_path_list(font);
}
FT_Face FontCache::find_face_in_path_list(std::string font)
{
const char *env_font_path = getenv("OPENSCAD_FONT_PATH");
std::string paths = (env_font_path == NULL) ? "/usr/share/fonts/truetype" : env_font_path;
typedef boost::split_iterator<std::string::iterator> string_split_iterator;
for (string_split_iterator it = boost::make_split_iterator(paths, boost::first_finder(":", boost::is_iequal()));it != string_split_iterator();it++) {
fs::path p(boost::copy_range<std::string>(*it));
if (fs::exists(p)) {
std::string path = boosty::absolute(p).string();
FT_Face face = find_face_in_path(path, font);
if (face) {
return face;
}
}
}
return NULL;
}
FT_Face FontCache::find_face_fontconfig(std::string font)
{
FcResult result;
FcPattern *pattern = FcNameParse((unsigned char *)font.c_str());
FcValue true_value;
true_value.type = FcTypeBool;
true_value.u.b = true;
FcPatternAdd(pattern, FC_OUTLINE, true_value, true);
FcPatternAdd(pattern, FC_SCALABLE, true_value, true);
FcDefaultSubstitute(pattern);
FcConfigSubstitute(config, pattern, FcMatchFont);
FcPattern *match = FcFontMatch(config, pattern, &result);
FcValue file_value;
FcPatternGet(match, FC_FILE, 0, &file_value);
FT_Face face;
FT_Error error = FT_New_Face(library, (const char *)file_value.u.s, 0, &face);
FcPatternDestroy(pattern);
FcPatternDestroy(match);
return error ? NULL : face;
}
FT_Face FontCache::find_face_in_path(std::string path, std::string font)
{
FT_Error error;
if (!fs::is_directory(path)) {
PRINTB("Font path '%s' does not exist or is not a directory.", path);
}
for (fs::recursive_directory_iterator it(path);it != fs::recursive_directory_iterator();it++) {
fs::directory_entry entry = (*it);
if (fs::is_regular(entry.path())) {
FT_Face face;
error = FT_New_Face(library, entry.path().c_str(), 0, &face);
if (error) {
continue;
}
const char *name = FT_Get_Postscript_Name(face);
if (font == name) {
return face;
}
FT_Done_Face(face);
}
}
return NULL;
}

78
src/FontCache.h Normal file
View File

@ -0,0 +1,78 @@
/*
* OpenSCAD (www.openscad.org)
* Copyright (C) 2009-2011 Clifford Wolf <clifford@clifford.at> and
* Marius Kintel <marius@kintel.net>
*
* This program 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 of the License, or
* (at your option) any later version.
*
* As a special exception, you have permission to link this program
* with the CGAL library and distribute executables, as long as you
* follow the requirements of the GNU GPL in regard to all of the
* software in the executable aside from CGAL.
*
* This program 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 this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#ifndef FONTCACHE_H
#define FONTCACHE_H
#include <map>
#include <string>
#include <time.h>
#include <ft2build.h>
#include FT_FREETYPE_H
#include <fontconfig/fontconfig.h>
#include <hb.h>
#include <hb-ft.h>
class FontCache {
public:
const static unsigned int MAX_NR_OF_CACHE_ENTRIES = 3;
FontCache();
virtual ~FontCache();
bool is_init_ok();
FT_Face get_font(std::string font);
void register_font_file(std::string path);
void clear();
static FontCache * instance();
private:
typedef std::pair<FT_Face, time_t> cache_entry_t;
typedef std::map<std::string, cache_entry_t> cache_t;
static FontCache *self;
bool init_ok;
cache_t cache;
FcConfig *config;
FT_Library library;
void check_cleanup();
void dump_cache(std::string info);
void add_font_dir(std::string path);
FT_Face find_face(std::string font);
FT_Face find_face_fontconfig(std::string font);
FT_Face find_face_in_path_list(std::string font);
FT_Face find_face_in_path(std::string path, std::string font);
};
#endif /* FONTCACHE_H */

216
src/FreetypeRenderer.cc Normal file
View File

@ -0,0 +1,216 @@
/*
* OpenSCAD (www.openscad.org)
* Copyright (C) 2009-2011 Clifford Wolf <clifford@clifford.at> and
* Marius Kintel <marius@kintel.net>
*
* This program 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 of the License, or
* (at your option) any later version.
*
* As a special exception, you have permission to link this program
* with the CGAL library and distribute executables, as long as you
* follow the requirements of the GNU GPL in regard to all of the
* software in the executable aside from CGAL.
*
* This program 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 this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#include <math.h>
#include <stdio.h>
#include <iostream>
#include <fontconfig/fontconfig.h>
#include "printutils.h"
#include "FontCache.h"
#include "DrawingCallback.h"
#include "FreetypeRenderer.h"
#include FT_OUTLINE_H
static inline Vector2d get_scaled_vector(const FT_Vector *ft_vector, double scale) {
return Vector2d(ft_vector->x / scale, ft_vector->y / scale);
}
FreetypeRenderer::FreetypeRenderer()
{
funcs.move_to = outline_move_to_func;
funcs.line_to = outline_line_to_func;
funcs.conic_to = outline_conic_to_func;
funcs.cubic_to = outline_cubic_to_func;
funcs.delta = 0;
funcs.shift = 0;
}
FreetypeRenderer::~FreetypeRenderer()
{
}
int FreetypeRenderer::outline_move_to_func(const FT_Vector *to, void *user)
{
DrawingCallback *cb = reinterpret_cast<DrawingCallback *>(user);
cb->move_to(get_scaled_vector(to, scale));
return 0;
}
int FreetypeRenderer::outline_line_to_func(const FT_Vector *to, void *user)
{
DrawingCallback *cb = reinterpret_cast<DrawingCallback *>(user);
cb->line_to(get_scaled_vector(to, scale));
return 0;
}
int FreetypeRenderer::outline_conic_to_func(const FT_Vector *c1, const FT_Vector *to, void *user)
{
DrawingCallback *cb = reinterpret_cast<DrawingCallback *>(user);
cb->curve_to(get_scaled_vector(c1, scale), get_scaled_vector(to, scale));
return 0;
}
int FreetypeRenderer::outline_cubic_to_func(const FT_Vector *c1, const FT_Vector *c2, const FT_Vector *to, void *user)
{
DrawingCallback *cb = reinterpret_cast<DrawingCallback *>(user);
cb->curve_to(get_scaled_vector(c1, scale), get_scaled_vector(c2, scale), get_scaled_vector(to, scale));
return 0;
}
double FreetypeRenderer::calc_x_offset(std::string halign, double width) const
{
if (halign == "right") {
return -width;
} else if (halign == "center") {
return -width / 2.0;
} else {
if (halign != "left") {
PRINTB("Unknown value for the halign parameter (use \"left\", \"right\" or \"center\"): '%s'", halign);
}
return 0;
}
}
double FreetypeRenderer::calc_y_offset(std::string valign, double ascend, double descend) const
{
if (valign == "top") {
return -ascend;
} else if (valign == "center") {
return descend / 2.0 - ascend / 2.0;
} else if (valign == "bottom") {
return descend;
} else {
if (valign != "baseline") {
PRINTB("Unknown value for the valign parameter (use \"baseline\", \"bottom\", \"top\" or \"center\"): '%s'", valign);
}
return 0;
}
}
std::vector<PolySet *> FreetypeRenderer::render(const FreetypeRenderer::Params &params) const
{
FT_Face face;
FT_Error error;
DrawingCallback callback(params.fn);
FontCache *cache = FontCache::instance();
if (!cache->is_init_ok()) {
return std::vector<PolySet *>();
}
face = cache->get_font(params.font);
if (face == NULL) {
return std::vector<PolySet *>();
}
error = FT_Set_Char_Size(face, 0, params.size * scale, 100, 100);
if (error) {
PRINTB("Can't set font size for font %s", params.font);
return std::vector<PolySet *>();
}
hb_font_t *hb_ft_font = hb_ft_font_create(face, NULL);
hb_buffer_t *hb_buf = hb_buffer_create();
hb_buffer_set_direction(hb_buf, hb_direction_from_string(params.direction.c_str(), -1));
hb_buffer_set_script(hb_buf, hb_script_from_string(params.script.c_str(), -1));
hb_buffer_set_language(hb_buf, hb_language_from_string(params.language.c_str(), -1));
hb_buffer_add_utf8(hb_buf, params.text.c_str(), strlen(params.text.c_str()), 0, strlen(params.text.c_str()));
hb_shape(hb_ft_font, hb_buf, NULL, 0);
unsigned int glyph_count;
hb_glyph_info_t *glyph_info = hb_buffer_get_glyph_infos(hb_buf, &glyph_count);
hb_glyph_position_t *glyph_pos = hb_buffer_get_glyph_positions(hb_buf, &glyph_count);
GlyphArray glyph_array;
for (unsigned int idx = 0;idx < glyph_count;idx++) {
FT_UInt glyph_index = glyph_info[idx].codepoint;
error = FT_Load_Glyph(face, glyph_index, FT_LOAD_DEFAULT);
if (error) {
PRINTB("Could not load glyph %u for char at index %u in text '%s'", glyph_index % idx % params.text);
continue;
}
FT_Glyph glyph;
error = FT_Get_Glyph(face->glyph, &glyph);
if (error) {
PRINTB("Could not get glyph %u for char at index %u in text '%s'", glyph_index % idx % params.text);
continue;
}
const GlyphData *glyph_data = new GlyphData(glyph, idx, &glyph_info[idx], &glyph_pos[idx]);
glyph_array.push_back(glyph_data);
}
double width = 0, ascend = 0, descend = 0;
for (GlyphArray::iterator it = glyph_array.begin();it != glyph_array.end();it++) {
const GlyphData *glyph = (*it);
FT_BBox bbox;
FT_Glyph_Get_CBox(glyph->get_glyph(), FT_GLYPH_BBOX_GRIDFIT, &bbox);
if (HB_DIRECTION_IS_HORIZONTAL(hb_buffer_get_direction(hb_buf))) {
double asc = std::max(0.0, bbox.yMax / 64.0 / 16.0);
double desc = std::max(0.0, -bbox.yMin / 64.0 / 16.0);
width += glyph->get_x_advance() * params.spacing;
ascend = std::max(ascend, asc);
descend = std::max(descend, desc);
} else {
double w_bbox = (bbox.xMax - bbox.xMin) / 64.0 / 16.0;
width = std::max(width, w_bbox);
ascend += glyph->get_y_advance() * params.spacing;
}
}
double x_offset = calc_x_offset(params.halign, width);
double y_offset = calc_y_offset(params.valign, ascend, descend);
for (GlyphArray::iterator it = glyph_array.begin();it != glyph_array.end();it++) {
const GlyphData *glyph = (*it);
callback.start_glyph();
callback.set_glyph_offset(x_offset + glyph->get_x_offset(), y_offset + glyph->get_y_offset());
FT_Outline outline = reinterpret_cast<FT_OutlineGlyph>(glyph->get_glyph())->outline;
FT_Outline_Decompose(&outline, &funcs, &callback);
double adv_x = glyph->get_x_advance() * params.spacing;
double adv_y = glyph->get_y_advance() * params.spacing;
callback.add_glyph_advance(adv_x, adv_y);
callback.finish_glyph();
}
hb_buffer_destroy(hb_buf);
hb_font_destroy(hb_ft_font);
return callback.get_result();
}

141
src/FreetypeRenderer.h Normal file
View File

@ -0,0 +1,141 @@
/*
* OpenSCAD (www.openscad.org)
* Copyright (C) 2009-2011 Clifford Wolf <clifford@clifford.at> and
* Marius Kintel <marius@kintel.net>
*
* This program 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 of the License, or
* (at your option) any later version.
*
* As a special exception, you have permission to link this program
* with the CGAL library and distribute executables, as long as you
* follow the requirements of the GNU GPL in regard to all of the
* software in the executable aside from CGAL.
*
* This program 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 this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#ifndef FREETYPERENDERER_H
#define FREETYPERENDERER_H
#include <string>
#include <vector>
#include <ostream>
#include <hb.h>
#include <ft2build.h>
#include FT_FREETYPE_H
#include FT_GLYPH_H
class FreetypeRenderer {
public:
class Params {
public:
void set_size(double size) {
this->size = size;
}
void set_spacing(double spacing) {
this->spacing = spacing;
}
void set_fn(double fn) {
this->fn = fn;
}
void set_text(std::string text) {
this->text = text;
}
void set_font(std::string font) {
this->font = font;
}
void set_direction(std::string direction) {
this->direction = direction;
}
void set_language(std::string language) {
this->language = language;
}
void set_script(std::string script) {
this->script = script;
}
void set_halign(std::string halign) {
this->halign = halign;
}
void set_valign(std::string valign) {
this->valign = valign;
}
friend std::ostream & operator << (std::ostream &stream, const FreetypeRenderer::Params &params) {
return stream
<< "$fn = " << params.fn
<< ", text = '" << params.text
<< "', size = " << params.size
<< ", spacing = " << params.spacing
<< ", font = '" << params.font
<< "', direction = '" << params.direction
<< "', language = '" << params.language
<< "', script = '" << params.script
<< "', halign = '" << params.halign
<< "', valign = '" << params.valign
<< "'";
}
private:
double size, spacing, fn;
std::string text, font, direction, language, script, halign, valign;
friend FreetypeRenderer;
};
FreetypeRenderer();
virtual ~FreetypeRenderer();
std::vector<PolySet *> render(const FreetypeRenderer::Params &params) const;
private:
const static double scale = 1000;
FT_Outline_Funcs funcs;
class GlyphData {
public:
GlyphData(FT_Glyph glyph, unsigned int idx, hb_glyph_info_t *glyph_info, hb_glyph_position_t *glyph_pos) : glyph(glyph), idx(idx), glyph_pos(glyph_pos), glyph_info(glyph_info) {}
unsigned int get_idx() const { return idx; };
FT_Glyph get_glyph() const { return glyph; };
double get_x_offset() const { return glyph_pos->x_offset / 64.0 / 16.0; };
double get_y_offset() const { return glyph_pos->y_offset / 64.0 / 16.0; };
double get_x_advance() const { return glyph_pos->x_advance / 64.0 / 16.0; };
double get_y_advance() const { return glyph_pos->y_advance / 64.0 / 16.0; };
private:
FT_Glyph glyph;
unsigned int idx;
hb_glyph_position_t *glyph_pos;
hb_glyph_info_t *glyph_info;
};
struct done_glyph : public std::unary_function<const GlyphData *, void> {
void operator() (const GlyphData *glyph_data) {
FT_Done_Glyph(glyph_data->get_glyph());
delete glyph_data;
}
};
class GlyphArray : public std::vector<const GlyphData *> {
public:
virtual ~GlyphArray() {
std::for_each(begin(), end(), done_glyph());
}
};
double calc_x_offset(std::string halign, double width) const;
double calc_y_offset(std::string valign, double ascend, double descend) const;
static int outline_move_to_func(const FT_Vector *to, void *user);
static int outline_line_to_func(const FT_Vector *to, void *user);
static int outline_conic_to_func(const FT_Vector *c1, const FT_Vector *to, void *user);
static int outline_cubic_to_func(const FT_Vector *c1, const FT_Vector *c2, const FT_Vector *to, void *user);
};
#endif /* FREETYPERENDERER_H */

View File

@ -37,6 +37,7 @@ extern void register_builtin_projection();
extern void register_builtin_cgaladv();
extern void register_builtin_dxf_linear_extrude();
extern void register_builtin_dxf_rotate_extrude();
extern void register_builtin_text();
extern void initialize_builtin_dxf_dim();
/*!
@ -62,6 +63,7 @@ void Builtins::initialize()
register_builtin_cgaladv();
register_builtin_dxf_linear_extrude();
register_builtin_dxf_rotate_extrude();
register_builtin_text();
this->deprecations["dxf_linear_extrude"] = "linear_extrude";
this->deprecations["dxf_rotate_extrude"] = "rotate_extrude";

View File

@ -100,6 +100,7 @@
#endif // ENABLE_CGAL
#include "boosty.h"
#include "FontCache.h"
// Global application state
unsigned int GuiLocker::gui_locked = 0;
@ -1699,6 +1700,7 @@ void MainWindow::actionFlushCaches()
dxf_dim_cache.clear();
dxf_cross_cache.clear();
ModuleCache::instance()->clear();
FontCache::instance()->clear();
}
void MainWindow::viewModeActionsUncheck()

View File

@ -37,6 +37,7 @@
#include <boost/filesystem.hpp>
namespace fs = boost::filesystem;
#include "boosty.h"
#include "FontCache.h"
#include <boost/foreach.hpp>
#include <sstream>
#include <sys/stat.h>
@ -56,6 +57,18 @@ AbstractNode *AbstractModule::instantiate(const Context *ctx, const ModuleInstan
return node;
}
double AbstractModule::lookup_double_variable_with_default(Context &c, std::string variable, double def) const
{
const Value v = c.lookup_variable(variable, true);
return (v.type() == Value::NUMBER) ? v.toDouble() : def;
}
std::string AbstractModule::lookup_string_variable_with_default(Context &c, std::string variable, std::string def) const
{
const Value v = c.lookup_variable(variable, true);
return (v.type() == Value::STRING) ? v.toString() : def;
}
std::string AbstractModule::dump(const std::string &indent, const std::string &name) const
{
std::stringstream dump;
@ -222,6 +235,21 @@ std::string Module::dump(const std::string &indent, const std::string &name) con
return dump.str();
}
void FileModule::registerUse(const std::string path) {
std::string extraw = boosty::extension_str(fs::path(path));
std::string ext = boost::algorithm::to_lower_copy(extraw);
if ((ext == ".otf") || (ext == ".ttf")) {
if (fs::is_regular(path)) {
FontCache::instance()->register_font_file(path);
} else {
PRINTB("ERROR: Can't read font with path '%s'", path);
}
} else {
usedlibs.insert(path);
}
}
void FileModule::registerInclude(const std::string &localpath,
const std::string &fullpath)
{

View File

@ -70,6 +70,8 @@ public:
virtual bool is_enabled() const { return (feature == NULL) || feature->is_enabled(); };
virtual class AbstractNode *instantiate(const Context *ctx, const ModuleInstantiation *inst, const class EvalContext *evalctx = NULL) const;
virtual std::string dump(const std::string &indent, const std::string &name) const;
virtual double lookup_double_variable_with_default(Context &c, std::string variable, double def) const;
virtual std::string lookup_string_variable_with_default(Context &c, std::string variable, std::string def) const;
};
class Module : public AbstractModule
@ -102,6 +104,7 @@ public:
void setModulePath(const std::string &path) { this->path = path; }
const std::string &modulePath() const { return this->path; }
void registerUse(const std::string path);
void registerInclude(const std::string &localpath, const std::string &fullpath);
bool includesChanged() const;
bool handleDependencies();

View File

@ -129,7 +129,7 @@ std::string parser_source_path;
input: /* empty */
| TOK_USE
{ rootmodule->usedlibs.insert($1); }
{ rootmodule->registerUse(std::string($1)); }
input
| statement input
;

110
src/text.cc Normal file
View File

@ -0,0 +1,110 @@
/*
* OpenSCAD (www.openscad.org)
* Copyright (C) 2009-2011 Clifford Wolf <clifford@clifford.at> and
* Marius Kintel <marius@kintel.net>
*
* This program 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 of the License, or
* (at your option) any later version.
*
* As a special exception, you have permission to link this program
* with the CGAL library and distribute executables, as long as you
* follow the requirements of the GNU GPL in regard to all of the
* software in the executable aside from CGAL.
*
* This program 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 this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#include "calc.h"
#include "module.h"
#include "evalcontext.h"
#include "printutils.h"
#include "builtin.h"
#include "PolySetEvaluator.h"
#include "textnode.h"
#include <boost/assign/std/vector.hpp>
using namespace boost::assign; // bring 'operator+=()' into scope
class TextModule : public AbstractModule
{
public:
TextModule() { }
virtual AbstractNode *instantiate(const Context *ctx, const ModuleInstantiation *inst, const EvalContext *evalctx) const;
};
AbstractNode *TextModule::instantiate(const Context *ctx, const ModuleInstantiation *inst, const EvalContext *evalctx) const
{
TextNode *node = new TextNode(inst);
AssignmentList args;
Context c(ctx);
c.setVariables(args, evalctx);
double fn = c.lookup_variable("$fn").toDouble();
double fs = c.lookup_variable("$fs").toDouble();
double fa = c.lookup_variable("$fa").toDouble();
double size = lookup_double_variable_with_default(c, "size", 10.0);
int segments = Calc::get_fragments_from_r(size, fn, fs, fa);
// The curved segments of most fonts are relatively short, so
// by using a fraction of the number of full circle segments
// the resolution will be better matching the detail level of
// other objects.
int text_segments = std::max(((int)floor(segments / 6)) + 2, 2);
node->params.set_size(size);
node->params.set_fn(text_segments);
node->params.set_text(lookup_string_variable_with_default(c, "t", ""));
node->params.set_spacing(lookup_double_variable_with_default(c, "spacing", 1.0));
node->params.set_font(lookup_string_variable_with_default(c, "font", ""));
node->params.set_direction(lookup_string_variable_with_default(c, "direction", "ltr"));
node->params.set_language(lookup_string_variable_with_default(c, "language", "en"));
node->params.set_script(lookup_string_variable_with_default(c, "script", "latin"));
node->params.set_halign(lookup_string_variable_with_default(c, "halign", "left"));
node->params.set_valign(lookup_string_variable_with_default(c, "valign", "baseline"));
return node;
}
class PolySet *TextNode::evaluate_polyset(PolySetEvaluator *evaluator) const
{
if (!evaluator) {
PRINTB("WARNING: No suitable PolySetEvaluator found for %s module!", this->name());
return NULL;
}
print_messages_push();
PolySet *ps = evaluator->evaluatePolySet(*this);
print_messages_pop();
return ps;
}
FreetypeRenderer::Params TextNode::get_params() const
{
return params;
}
std::string TextNode::toString() const
{
std::stringstream stream;
stream << "(" << this->params << ")";
return stream.str();
}
void register_builtin_text()
{
Builtins::init("text", new TextModule());
}

32
src/textnode.h Normal file
View File

@ -0,0 +1,32 @@
#ifndef TEXTNODE_H_
#define TEXTNODE_H_
#include "node.h"
#include "visitor.h"
#include "value.h"
#include "FreetypeRenderer.h"
class TextModule;
class TextNode : public AbstractPolyNode
{
public:
TextNode(const ModuleInstantiation *mi) : AbstractPolyNode(mi) {}
virtual Response accept(class State &state, Visitor &visitor) const {
return visitor.visit(state, *this);
}
virtual std::string toString() const;
virtual std::string name() const { return "text"; }
virtual PolySet *evaluate_polyset(class PolySetEvaluator *) const;
virtual FreetypeRenderer::Params get_params() const;
private:
FreetypeRenderer::Params params;
friend TextModule;
};
#endif

View File

@ -435,6 +435,20 @@ find_package(GLIB2 2.2.0 REQUIRED)
add_definitions(${GLIB2_DEFINITIONS})
inclusion(GLIB2_DIR GLIB2_INCLUDE_DIRS)
# find libraries using pkg-config
find_package(PkgConfig REQUIRED)
pkg_search_module(FONTCONFIG REQUIRED fontconfig>=2.11.0)
message(STATUS "fontconfig found: ${FONTCONFIG_VERSION}")
pkg_search_module(FREETYPE REQUIRED freetype2>=2.4.9)
message(STATUS "freetype2 found: ${FREETYPE_VERSION}")
pkg_search_module(HARFBUZZ REQUIRED harfbuzz>=0.9.19)
message(STATUS "harfbuzz found: ${HARFBUZZ_VERSION}")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${FONTCONFIG_CFLAGS} ${FREETYPE_CFLAGS} ${HARFBUZZ_CFLAGS}")
# Imagemagick
if (SKIP_IMAGEMAGICK)
@ -555,10 +569,14 @@ set(CORE_SOURCES
../src/dxfdim.cc
../src/linearextrude.cc
../src/rotateextrude.cc
../src/text.cc
../src/printutils.cc
../src/fileutils.cc
../src/progress.cc
../src/boost-utils.cc
../src/FontCache.cc
../src/DrawingCallback.cc
../src/FreetypeRenderer.cc
${FLEX_OpenSCADlexer_OUTPUTS}
${BISON_OpenSCADparser_OUTPUTS})
@ -659,27 +677,51 @@ set_target_properties(tests-offscreen PROPERTIES COMPILE_FLAGS "${ENABLE_OPENCSG
# modulecachetest
#
add_executable(modulecachetest modulecachetest.cc)
target_link_libraries(modulecachetest tests-nocgal ${TESTS-NOCGAL-LIBRARIES})
target_link_libraries(modulecachetest tests-nocgal ${TESTS-NOCGAL-LIBRARIES} ${Boost_LIBRARIES} ${FONTCONFIG_LDFLAGS} ${FREETYPE_LDFLAGS} ${HARFBUZZ_LDFLAGS})
#
# csgtexttest
#
add_executable(csgtexttest csgtexttest.cc CSGTextRenderer.cc CSGTextCache.cc)
target_link_libraries(csgtexttest tests-nocgal ${TESTS-NOCGAL-LIBRARIES})
target_link_libraries(csgtexttest tests-nocgal ${TESTS-NOCGAL-LIBRARIES} ${FONTCONFIG_LDFLAGS} ${FREETYPE_LDFLAGS} ${HARFBUZZ_LDFLAGS})
#
# cgalcachetest
#
add_executable(cgalcachetest cgalcachetest.cc)
set_target_properties(cgalcachetest PROPERTIES COMPILE_FLAGS "-DENABLE_CGAL ${CGAL_CXX_FLAGS_INIT}")
target_link_libraries(cgalcachetest tests-cgal ${TESTS-CGAL-LIBRARIES} ${CLIPPER_LIBRARY} ${GLEW_LIBRARY} ${COCOA_LIBRARY})
target_link_libraries(cgalcachetest tests-cgal ${TESTS-CGAL-LIBRARIES} ${GLEW_LIBRARY} ${COCOA_LIBRARY} ${FONTCONFIG_LDFLAGS} ${FREETYPE_LDFLAGS} ${HARFBUZZ_LDFLAGS})
#
# openscad no-qt
#
add_executable(openscad_nogui ../src/openscad.cc)
set_target_properties(openscad_nogui PROPERTIES COMPILE_FLAGS "-fno-strict-aliasing -DEIGEN_DONT_ALIGN -DENABLE_CGAL ${ENABLE_OPENCSG_FLAG} ${CGAL_CXX_FLAGS_INIT}")
target_link_libraries(openscad_nogui tests-offscreen tests-cgal tests-nocgal ${TESTS-CORE-LIBRARIES} ${TESTS-CGAL-LIBRARIES} ${GLEW_LIBRARY} ${Boost_LIBRARIES} ${OPENCSG_LIBRARY} ${COCOA_LIBRARY} ${APP_SERVICES_LIBRARY})
set_target_properties(openscad_nogui PROPERTIES COMPILE_FLAGS "-fno-strict-aliasing -DEIGEN_DONT_ALIGN -DENABLE_CGAL -DENABLE_OPENCSG ${CGAL_CXX_FLAGS_INIT}")
target_link_libraries(openscad_nogui tests-offscreen tests-cgal tests-nocgal ${TESTS-CORE-LIBRARIES} ${TESTS-CGAL-LIBRARIES} ${GLEW_LIBRARY} ${Boost_LIBRARIES} ${OPENCSG_LIBRARY} ${COCOA_LIBRARY} ${APP_SERVICES_LIBRARY} ${FONTCONFIG_LDFLAGS} ${FREETYPE_LDFLAGS} ${HARFBUZZ_LDFLAGS})
#
# GUI binary tests
#
#if(APPLE)
# set(OPENSCAD_BINPATH "${CMAKE_CURRENT_SOURCE_DIR}/../OpenSCAD.app/Contents/MacOS/OpenSCAD")
#elseif (MINGW_CROSS_ENV_DIR)
# set(OPENSCAD_BINPATH "${CMAKE_CURRENT_SOURCE_DIR}/../mingw32/release/openscad.exe")
#elseif(WIN32)
# set(OPENSCAD_BINPATH "${CMAKE_CURRENT_SOURCE_DIR}/../Release/openscad.exe")
#else()
# set(OPENSCAD_BINPATH "${CMAKE_CURRENT_SOURCE_DIR}/../openscad")
#endif()
#if(EXISTS "${CMAKE_CURRENT_BINARY_DIR}/openscad")
# set(OPENSCAD_BINPATH "${CMAKE_CURRENT_BINARY_DIR}/openscad")
#endif()
#if(EXISTS "${OPENSCAD_BINPATH}")
# message(STATUS "Found OpenSCAD binary: ${OPENSCAD_BINPATH}")
#else()
# message(STATUS "Couldn't find the OpenSCAD binary: ${OPENSCAD_BINPATH}")
# message(FATAL_ERROR "Please build the OpenSCAD binary and place it here: ${OPENSCAD_BINPATH}" )
#endif()
if(WIN32)
set(OPENSCAD_BINPATH "${CMAKE_CURRENT_BINARY_DIR}/openscad_nogui.exe")