26 #if defined (HAVE_CONFIG_H)
33 #if defined (HAVE_FREETYPE)
35 #if defined (HAVE_PRAGMA_GCC_DIAGNOSTIC)
36 # pragma GCC diagnostic push
37 # pragma GCC diagnostic ignored "-Wold-style-cast"
41 #include FT_FREETYPE_H
44 #if defined (HAVE_FONTCONFIG)
45 # include <fontconfig/fontconfig.h>
48 #if defined (HAVE_PRAGMA_GCC_DIAGNOSTIC)
49 # pragma GCC diagnostic pop
77 "text_renderer: skipping missing glyph for character '%lx'", c);
84 "text_renderer: unable to render glyph for character '%lx'", c);
87 #if defined (_MSC_VER)
106 : m_library (), m_freetype_initialized (false),
107 m_fontconfig_initialized (false)
109 if (FT_Init_FreeType (&m_library))
110 error (
"unable to initialize FreeType library");
112 m_freetype_initialized =
true;
114 #if defined (HAVE_FONTCONFIG)
116 error (
"unable to initialize fontconfig library");
118 m_fontconfig_initialized =
true;
134 if (m_freetype_initialized)
135 FT_Done_FreeType (m_library);
137 #if defined (HAVE_FONTCONFIG)
163 {
delete m_instance; m_instance =
nullptr; }
165 static FT_Face
get_font (
const std::string& name,
const std::string& weight,
166 const std::string& angle,
double size,
169 return (instance_ok ()
170 ? m_instance->do_get_font (name, weight, angle, size, c)
176 return (instance_ok ()
177 ? m_instance->do_get_system_fonts ()
184 m_instance->do_font_destroyed (face);
189 typedef std::pair<std::string, double>
ft_key;
198 #if defined (HAVE_FONTCONFIG)
199 FcConfig *config = FcConfigGetCurrent();
200 FcPattern *pat = FcPatternCreate ();
201 FcObjectSet *os = FcObjectSetBuild (FC_FAMILY, FC_SLANT, FC_WEIGHT,
202 FC_CHARSET,
nullptr);
203 FcFontSet *fs = FcFontList (config, pat, os);
208 FcCharSet *minimal_charset = FcCharSetCreate ();
209 for (
int i = 32; i < 127; i++)
210 FcCharSetAddChar (minimal_charset,
static_cast<FcChar32
> (i));
213 fields(0) =
"family";
215 fields(2) =
"weight";
216 fields(3) =
"suitable";
224 unsigned char *family;
226 for (
int i = 0; fs && i < fs->nfont; i++)
228 FcPattern *font = fs->fonts[i];
229 if (FcPatternGetString (font, FC_FAMILY, 0, &family)
231 families(i) = std::string (
reinterpret_cast<char *
> (family));
233 families(i) =
"unknown";
235 if (FcPatternGetInteger (font, FC_SLANT, 0, &val)
237 angles(i) = (val == FC_SLANT_ITALIC
238 || val == FC_SLANT_OBLIQUE)
239 ?
"italic" :
"normal";
241 angles(i) =
"unknown";
243 if (FcPatternGetInteger (font, FC_WEIGHT, 0, &val)
245 weights(i) = (val == FC_WEIGHT_BOLD
246 || val == FC_WEIGHT_DEMIBOLD)
249 weights(i) =
"unknown";
252 if (FcPatternGetCharSet (font, FC_CHARSET, 0, &cset)
254 suitable(i) = (FcCharSetIsSubset (minimal_charset, cset)
262 font_map.
assign (
"family", families);
263 font_map.
assign (
"angle", angles);
264 font_map.
assign (
"weight", weights);
265 font_map.
assign (
"suitable", suitable);
268 FcFontSetDestroy (fs);
276 FT_Face
do_get_font (
const std::string& name,
const std::string& weight,
277 const std::string& angle,
double size,
278 FT_ULong search_code_point)
280 FT_Face retval =
nullptr;
282 #if defined (HAVE_FT_REFERENCE_FACE)
286 ft_key key (name +
':' + weight +
':' + angle +
':'
287 + std::to_string (search_code_point), size);
288 ft_cache::const_iterator it = m_cache.find (key);
290 if (it != m_cache.end ())
292 FT_Reference_Face (it->second);
297 static std::string fonts_dir;
299 if (fonts_dir.empty ())
301 fonts_dir = sys::env::getenv (
"OCTAVE_FONTS_DIR");
303 if (fonts_dir.empty ())
304 #if defined (SYSTEM_FREEFONT_DIR)
305 fonts_dir = SYSTEM_FREEFONT_DIR;
315 if (! fonts_dir.empty ())
319 if (weight ==
"bold")
322 if (angle ==
"italic" || angle ==
"oblique")
328 #if defined (HAVE_FONTCONFIG)
329 if ((search_code_point != 0 || name !=
"*") && m_fontconfig_initialized)
331 int fc_weight, fc_angle;
333 if (weight ==
"bold")
334 fc_weight = FC_WEIGHT_BOLD;
336 fc_weight = FC_WEIGHT_NORMAL;
338 if (angle ==
"italic")
339 fc_angle = FC_SLANT_ITALIC;
340 else if (angle ==
"oblique")
341 fc_angle = FC_SLANT_OBLIQUE;
343 fc_angle = FC_SLANT_ROMAN;
345 FcPattern *pat = FcPatternCreate ();
347 FcPatternAddString (pat, FC_FAMILY,
348 (
reinterpret_cast<const FcChar8 *
>
351 FcPatternAddInteger (pat, FC_WEIGHT, fc_weight);
352 FcPatternAddInteger (pat, FC_SLANT, fc_angle);
353 FcPatternAddDouble (pat, FC_PIXEL_SIZE, size);
355 if (search_code_point > 0)
357 FcCharSet *minimal_charset = FcCharSetCreate ();
358 FcCharSetAddChar (minimal_charset,
359 static_cast<FcChar32
> (search_code_point));
360 FcPatternAddCharSet (pat, FC_CHARSET, minimal_charset);
363 if (FcConfigSubstitute (
nullptr, pat, FcMatchPattern))
368 FcDefaultSubstitute (pat);
370 match = FcFontMatch (
nullptr, pat, &res);
378 FcPatternGetString (match, FC_FILE, 0, &tmp);
379 file =
reinterpret_cast<char *
> (tmp);
382 ::warning (
"could not match any font: %s-%s-%s-%g, using default font",
383 name.c_str (), weight.c_str (), angle.c_str (),
387 FcPatternDestroy (match);
390 FcPatternDestroy (pat);
395 ::warning (
"unable to find default font files");
400 if (FT_New_Face (m_library, ascii_file.c_str (), 0, &retval))
401 ::warning (
"ft_manager: unable to load font: %s", file.c_str ());
402 #if defined (HAVE_FT_REFERENCE_FACE)
409 retval->generic.data =
new ft_key (key);
413 if (FT_Reference_Face (retval) == 0)
414 m_cache[key] = retval;
424 if (face->generic.data)
426 ft_key *pkey =
reinterpret_cast<ft_key *
> (face->generic.data);
428 m_cache.erase (*pkey);
430 face->generic.data =
nullptr;
473 m_xoffset (0), m_line_yoffset (0), m_yoffset (0), m_mode (MODE_BBOX),
474 m_color (
dim_vector (1, 3), 0), m_do_strlist (false), m_strlist (),
475 m_line_xoffset (0), m_ymin (0), m_ymax (0), m_deltax (0),
476 m_max_fontsize (0), m_antialias (true)
514 int rotation = ROTATION_0);
517 Matrix get_extent (
const std::string& txt,
double rotation,
522 void set_font (
const std::string& name,
const std::string& weight,
523 const std::string& angle,
double size);
527 void set_color (
const Matrix& c);
529 void set_mode (
int m);
531 void text_to_pixels (
const std::string& txt,
533 int halign,
int valign,
double rotation,
535 bool handle_rotation);
549 ft_font (
const std::string& nm,
const std::string& wt,
550 const std::string& ang,
double sz, FT_Face
f =
nullptr)
559 FT_Done_Face (m_face);
566 FT_Face get_face (
void)
const;
573 void push_new_line (
void);
575 void update_line_bbox (
void);
577 void compute_bbox (
void);
579 int compute_line_xoffset (
const Matrix& lb)
const;
581 FT_UInt process_character (FT_ULong code, FT_UInt previous,
582 std::string& sub_font);
586 void text_to_strlist (
const std::string& txt,
587 std::list<text_renderer::string>& lst,
Matrix& bbox,
588 int halign,
int valign,
double rotation,
655 const std::string& weight,
656 const std::string& angle,
double size)
702 + (new_bbox(3) + new_bbox(1)));
720 return (
m_bbox(2) - lb(2)) / 2;
722 return (
m_bbox(2) - lb(2));
801 ::error (
"ft_text_renderer: invalid bounding box, cannot render");
818 error (
"ft_text_renderer: invalid mode '%d'",
m_mode);
823 bool is_opaque (
const FT_GlyphSlot& glyph,
const int x,
const int y)
827 int pitch =
std::abs (glyph->bitmap.pitch);
828 unsigned char *row = &glyph->bitmap.buffer[pitch * y];
829 char cvalue = row[
x >> 3];
831 return ((cvalue & (128 >> (
x & 7))) != 0);
836 std::string& sub_name)
840 sub_name = face->family_name;
842 FT_UInt glyph_index = 0;
846 glyph_index = FT_Get_Char_Index (face, code);
848 if (code !=
'\n' && code !=
'\t'
850 || FT_Load_Glyph (face, glyph_index, FT_LOAD_DEFAULT)))
852 #if defined (HAVE_FONTCONFIG)
864 glyph_index = FT_Get_Char_Index (sub_face, code);
867 && (FT_Load_Glyph (sub_face, glyph_index, FT_LOAD_DEFAULT)
870 static std::string prev_sub_name;
872 if (prev_sub_name.empty ()
873 || prev_sub_name != std::string (sub_face->family_name))
875 prev_sub_name = sub_face->family_name;
877 "text_renderer: substituting font to '%s' for some characters",
878 sub_face->family_name);
907 else if ((code ==
'\n') || (code ==
'\t'))
909 glyph_index = FT_Get_Char_Index (face,
' ');
911 || FT_Load_Glyph (face, glyph_index, FT_LOAD_DEFAULT))
916 else if (code ==
'\n')
922 int x_tab = 4 * (face->glyph->advance.x >> 6);
932 ? FT_RENDER_MODE_NORMAL
933 : FT_RENDER_MODE_MONO)))
940 FT_Bitmap& bitmap = face->glyph->bitmap;
947 FT_Get_Kerning (face, previous, glyph_index,
948 FT_KERNING_DEFAULT, &delta);
953 x0 =
m_xoffset + face->glyph->bitmap_left;
955 + (face->glyph->bitmap_top - 1);
964 for (
int r = 0;
static_cast<unsigned int> (
r) < bitmap.rows;
r++)
965 for (
int c = 0;
static_cast<unsigned int> (c) < bitmap.width; c++)
969 ? bitmap.buffer[
r*bitmap.width+c]
978 else if (
m_pixels(3, x0+c, y0-
r).value () == 0)
987 m_xoffset += (face->glyph->advance.x >> 6);
1001 FT_Get_Kerning (face, previous, glyph_index,
1002 FT_KERNING_DEFAULT, &delta);
1010 m_xoffset += (face->glyph->advance.x >> 6);
1015 if (FT_Get_Glyph (face->glyph, &glyph))
1020 FT_Glyph_Get_CBox (glyph, FT_GLYPH_BBOX_UNSCALED,
1022 m_deltax = (glyph_bbox.xMax - face->glyph->advance.x) >> 6;
1027 FT_Done_Glyph (glyph);
1040 std::list<text_renderer::string>& lst,
1042 int ha,
int va,
double rot,
1049 m_strlist = std::list<text_renderer::string> ();
1068 FT_UInt glyph_index, previous = 0;
1071 const uint8_t *c =
reinterpret_cast<const uint8_t *
> (str.c_str ());
1074 std::size_t
n = str.size ();
1075 std::size_t icurr = 0;
1076 std::size_t ibegin = 0;
1083 if (fname.find (
" ") != std::string::npos)
1084 fname =
"'" + fname +
"'";
1088 std::vector<double> xdata;
1089 std::string sub_name;
1114 std::string s = str.substr (ibegin, icurr - ibegin);
1135 if (tmp_family.find (sub_name) == std::string::npos)
1137 if (sub_name.find (
" ") != std::string::npos)
1138 sub_name =
"'" + sub_name +
"'";
1140 fs.
set_family (tmp_family +
", " + sub_name);
1158 previous = glyph_index;
1313 std::vector<double> xdata (1,
m_xoffset);
1318 std::string sub_name;
1324 if (! sub_name.empty ())
1329 if (tmp_family.find (sub_name) == std::string::npos)
1331 if (sub_name.find (
" ") != std::string::npos)
1332 sub_name =
"'" + sub_name +
"'";
1334 fs.
set_family (tmp_family +
", " + sub_name);
1360 for (
auto *txt_elt : e)
1363 txt_elt->accept (*
this);
1375 m_strlist = std::list<text_renderer::string> ();
1381 if (c.
numel () == 3)
1383 m_color(0) =
static_cast<uint8_t
> (c(0)*255);
1384 m_color(1) =
static_cast<uint8_t
> (c(1)*255);
1385 m_color(2) =
static_cast<uint8_t
> (c(2)*255);
1388 ::warning (
"ft_text_renderer::set_color: invalid color");
1423 Matrix extent (1, 2, 0.0);
1456 int _halign,
int valign,
double rotation,
1458 bool handle_rotation)
1479 #if defined (HAVE_FT_REFERENCE_FACE)
1482 if (ft_face && FT_Reference_Face (ft_face) == 0)
1496 FT_Done_Face (m_face);
1500 #if defined (HAVE_FT_REFERENCE_FACE)
1503 if (ft_face && FT_Reference_Face (ft_face) == 0)
1514 if (! m_face && ! m_name.empty ())
1520 if (FT_Set_Char_Size (m_face, 0, m_size*64, 0, 0))
1521 ::warning (
"ft_text_renderer: unable to set font size to %g", m_size);
1524 ::warning (
"ft_text_renderer: unable to load appropriate font");
1539 #if defined (HAVE_FREETYPE)
charNDArray max(char d, const charNDArray &m)
charNDArray min(char d, const charNDArray &m)
OCTARRAY_OVERRIDABLE_FUNC_API bool isempty(void) const
Size of the specified dimension.
OCTARRAY_OVERRIDABLE_FUNC_API octave_idx_type dim2(void) const
OCTARRAY_OVERRIDABLE_FUNC_API octave_idx_type numel(void) const
Number of elements in the array.
OCTARRAY_OVERRIDABLE_FUNC_API octave_idx_type dim3(void) const
Size of the specified dimension.
OCTAVE_API Matrix extract(octave_idx_type r1, octave_idx_type c1, octave_idx_type r2, octave_idx_type c2) const
void fix_bbox_anchor(Matrix &bbox, int halign, int valign, int rot_mode, bool handle_rotation) const
void rotate_pixels(uint8NDArray &pixels, int rot_mode) const
int rotation_to_mode(double rotation) const
Vector representing the dimensions (size) of an Array.
bool m_fontconfig_initialized
static void cleanup_instance(void)
bool m_freetype_initialized
static ft_manager * m_instance
ft_manager(const ft_manager &)=delete
void do_font_destroyed(FT_Face face)
static octave_map get_system_fonts(void)
std::pair< std::string, double > ft_key
static void font_destroyed(FT_Face face)
std::map< ft_key, FT_Face > ft_cache
static bool instance_ok(void)
FT_Face do_get_font(const std::string &name, const std::string &weight, const std::string &angle, double size, FT_ULong search_code_point)
static FT_Face get_font(const std::string &name, const std::string &weight, const std::string &angle, double size, FT_ULong c=0)
static octave_map do_get_system_fonts(void)
bool is_valid(void) const
ft_font(const std::string &nm, const std::string &wt, const std::string &ang, double sz, FT_Face f=nullptr)
ft_font & operator=(const ft_font &ft)
FT_Face get_face(void) const
void set_color(const Matrix &c)
int compute_line_xoffset(const Matrix &lb) const
void text_to_strlist(const std::string &txt, std::list< text_renderer::string > &lst, Matrix &bbox, int halign, int valign, double rotation, const caseless_str &interp)
ft_text_renderer(const ft_text_renderer &)=delete
void update_line_bbox(void)
~ft_text_renderer(void)=default
uint8NDArray get_pixels(void) const
std::list< text_renderer::string > m_strlist
void set_anti_aliasing(bool val)
void visit(text_element_string &e)
FT_UInt process_character(FT_ULong code, FT_UInt previous, std::string &sub_font)
std::list< Matrix > m_line_bbox
uint8NDArray render(text_element *elt, Matrix &box, int rotation=ROTATION_0)
void text_to_pixels(const std::string &txt, uint8NDArray &pxls, Matrix &bbox, int halign, int valign, double rotation, const caseless_str &interpreter, bool handle_rotation)
void set_font(const std::string &name, const std::string &weight, const std::string &angle, double size)
Matrix get_extent(text_element *elt, double rotation=0.0)
octave_map get_system_fonts(void)
Matrix get_boundingbox(void) const
void assign(const std::string &k, const Cell &val)
const std::string & get_fontname(void) const
double get_fontsize(void) const
fontstyle get_fontstyle(void) const
std::string string_value(void) const
int get_symbol(void) const
uint32_t get_symbol_code(void) const
virtual void accept(text_processor &p)=0
virtual text_element * parse(const std::string &s)=0
virtual void visit(text_element_string &)
std::string get_angle(void) const
font & operator=(const font &ft)
std::string get_name(void) const
double get_size(void) const
std::string get_weight(void) const
void set_string(const std::string &s)
void set_xdata(const std::vector< double > &x)
void set_y(const double y)
std::string get_family(void) const
std::string get_string(void) const
void set_family(const std::string &nm)
uint32_t get_code(void) const
void set_code(const uint32_t code)
void set_color(const uint8NDArray &c)
OCTAVE_BEGIN_NAMESPACE(octave) static octave_value daspk_fcn
std::string oct_fonts_dir(void)
void warning(const char *fmt,...)
void warning_with_id(const char *id, const char *fmt,...)
void error(const char *fmt,...)
std::string dir_sep_str(void)
static void ft_face_destroyed(void *object)
bool is_opaque(const FT_GlyphSlot &glyph, const int x, const int y)
static void warn_glyph_render(FT_ULong c)
base_text_renderer * make_ft_text_renderer(void)
static void warn_missing_glyph(FT_ULong c)
std::complex< T > floor(const std::complex< T > &x)
std::complex< T > ceil(const std::complex< T > &x)
F77_RET_T const F77_DBLE const F77_DBLE F77_DBLE * d
F77_RET_T const F77_DBLE * x
F77_RET_T const F77_DBLE const F77_DBLE * f
std::string get_ASCII_filename(const std::string &orig_file_name, const bool allow_locale)
static octave_value box(JNIEnv *jni_env, void *jobj, void *jcls_arg=nullptr)
intNDArray< octave_uint8 > uint8NDArray
int octave_u8_strmbtouc_wrapper(uint32_t *puc, const uint8_t *src)