Mercurial > octave
diff libinterp/corefcn/txt-eng-ft.cc @ 17272:8ce6cdd272eb
Support TeX elements in FreeType renderer.
* libinterp/corefcn/txt-eng.cc: New file. Contains mapping from symbol
name to character code, in Unicode and MS symbol.
* libinterp/corefcn/modules.mk (COREFCN_SRC): Add txt-eng.cc.
* libinterp/corefcn/oct-tex-lexer.ll: Add "\n" to rules applicable to
".", as
the latter does not include new line characters.
* libinterp/corefcn/oct-tex-parser.yy: Remove debug statements.
* libinterp/corefcn/txt-eng.ft.cc (gripe_missing_glyph,
gripe_glyph_render): Change signature from char to FT_ULong.
(ft_render::ft_render): Adapt to new/removed members.
(ft_render::~ft_render): Remove use of fonts member.
(ft_render::set_font): Likewise. Use font instead.
(ft_render::push_new_line): Likewise. Change meaning of yoffset and
initialize line_yoffset.
(ft_render::update_line_bbox): New method.
(ft_render::set_mode): Change meaning of yoffset and initialize
line_yoffset.
(ft_render::process_character): New method.
(ft_render::visit(text_element_string)): Use it.
(ft_render::visit(text_element_list),
ft_render::visit(text_element_subscript),
ft_render::visit(text_element_superscript),
ft_render::visit(text_element_color),
ft_render::visit(text_element_fontsize),
ft_render::visit(text_element_fontname),
ft_render::visit(text_element_fontstyle),
ft_render::visit(text_element_symbol)): New methods.
(ft_render::set_color): Use color member instead of red/green/blue.
* libinterp/corefcn/txt-eng-ft.h (ft_render::visit(text_element_list),
ft_render::visit(text_element_subscript),
ft_render::visit(text_element_superscript),
ft_render::visit(text_element_color),
ft_render::visit(text_element_fontsize),
ft_render::visit(text_element_fontname),
ft_render::visit(text_element_fontstyle),
ft_render::visit(text_element_symbol)): New methods.
(ft_render::update_line_bbox, ft_render::process_character): New
methods.
(ft_render::current_face): Removed method.i
(ft_render::font): New member, replaces obsolete ft_render::fonts.
(ft_render::line_yoffset): New member.
(ft_render::color): New member, replaces obsolete red, green and blue.
(ft_render::ft_font::ft_font()): Implement default constructor.
(ft_render::ft_font::operator=): Fix incorrect use of FT_Reference_Face
return value.
(ft_render::ft_font::is_valid): New method.
* libinterp/corefcn/txt-eng.h (class text_element_symbol, class
text_element_fontname, class text_element_fontsize, class
text_element_fontname, class text_element_fontstyle, class
text_element_color): Add forward definition.
(text_element_symbol::invalid_code): New enum.
(text_element_symbol::code): New member.
(text_element_symbol::text_element_symbol): Initialize it.
(text_element_symbol::get_symbol_code): New method.
(text_element_fontstyle::get_fontstyle): New method.
(text_element_fontname::get_fontname): Renamed from fontname.
(text_element_fontsize::get_fontsize): Renamed from fontsize.
author | Michael Goffioul <michael.goffioul@gmail.com> |
---|---|
date | Sun, 18 Aug 2013 16:36:46 -0400 |
parents | cb7233cfbf43 |
children | 0a09d4b40767 |
line wrap: on
line diff
--- a/libinterp/corefcn/txt-eng-ft.cc Sun Aug 18 16:36:44 2013 -0400 +++ b/libinterp/corefcn/txt-eng-ft.cc Sun Aug 18 16:36:46 2013 -0400 @@ -44,18 +44,18 @@ // combination. static void -gripe_missing_glyph (char c) +gripe_missing_glyph (FT_ULong c) { warning_with_id ("Octave:missing-glyph", - "ft_render: skipping missing glyph for character '%c'", + "ft_render: skipping missing glyph for character '%x'", c); } static void -gripe_glyph_render (char c) +gripe_glyph_render (FT_ULong c) { warning_with_id ("Octave:glyph-render", - "ft_render: unable to render glyph for character '%c'", + "ft_render: unable to render glyph for character '%x'", c); } @@ -302,40 +302,29 @@ // --------------------------------------------------------------------------- ft_render::ft_render (void) - : text_processor (), fonts (), bbox (1, 4, 0.0), halign (0), xoffset (0), - yoffset (0), mode (MODE_BBOX), red (0), green (0), blue (0) + : text_processor (), font (), bbox (1, 4, 0.0), halign (0), xoffset (0), + line_yoffset (0), yoffset (0), mode (MODE_BBOX), + color (dim_vector (1, 3), 0) { } ft_render::~ft_render (void) { - fonts.clear (); } void ft_render::set_font (const std::string& name, const std::string& weight, const std::string& angle, double size) { - if (fonts.size () > 1) - ::warning ("ft_render: resetting font parameters while the font stack " - "contains more than 1 element."); - - // In all cases, we only replace the first/bottom font in the stack, if any. - // Calling this method while there's more than 1 font in the stack does - // not make sense: we're not gonna reconstruct the entire font stack. - - if (fonts.size ()) - fonts.pop_front (); - // FIXME: take "fontunits" into account FT_Face face = ft_manager::get_font (name, weight, angle, size); if (face) { if (FT_Set_Char_Size (face, 0, size*64, 0, 0)) - ::warning ("ft_render: unable to set font size to %d", size); + ::warning ("ft_render: unable to set font size to %g", size); - fonts.push_front (ft_font (name, weight, angle, size, face)); + font = ft_font (name, weight, angle, size, face); } else ::warning ("ft_render: unable to load appropriate font"); @@ -350,7 +339,7 @@ { // Create a new bbox entry based on the current font. - FT_Face face = current_face (); + FT_Face face = font.face; if (face) { @@ -379,7 +368,8 @@ Matrix new_bbox = line_bbox.front (); xoffset = compute_line_xoffset (new_bbox); - yoffset += (old_bbox(1) - (new_bbox(1) + new_bbox(3))); + line_yoffset += (old_bbox(1) - (new_bbox(1) + new_bbox(3))); + yoffset = 0; } break; } @@ -437,6 +427,42 @@ } void +ft_render::update_line_bbox (void) +{ + // Called after a font change, when in MODE_BBOX mode, to update the + // current line bbox with the new font metrics. This also includes the + // current yoffset, that is the offset of the current glyph's baseline + // the line's baseline. + + if (mode == MODE_BBOX) + { + int asc = font.face->size->metrics.ascender >> 6; + int desc = font.face->size->metrics.descender >> 6; + + Matrix& bb = line_bbox.front (); + + if ((yoffset + desc) < bb(1)) + { + // The new font goes below the bottom of the current bbox. + + int delta = bb(1) - (yoffset + desc); + + bb(1) -= delta; + bb(3) += delta; + } + + if ((yoffset + asc) > (bb(1) + bb(3))) + { + // The new font goes above the top of the current bbox. + + int delta = (yoffset + asc) - (bb(1) + bb(3)); + + bb(3) += delta; + } + } +} + +void ft_render::set_mode (int m) { mode = m; @@ -444,7 +470,7 @@ switch (mode) { case MODE_BBOX: - xoffset = yoffset = 0; + xoffset = line_yoffset = yoffset = 0; bbox = Matrix (1, 4, 0.0); line_bbox.clear (); push_new_line (); @@ -454,7 +480,7 @@ { ::warning ("ft_render: invalid bounding box, cannot render"); - xoffset = yoffset = 0; + xoffset = line_yoffset = yoffset = 0; pixels = uint8NDArray (); } else @@ -462,7 +488,8 @@ pixels = uint8NDArray (dim_vector (4, bbox(2), bbox(3)), static_cast<uint8_t> (0)); xoffset = compute_line_xoffset (line_bbox.front ()); - yoffset = -bbox(1)-1; + line_yoffset = -bbox(1)-1; + yoffset = 0; } break; default: @@ -471,135 +498,289 @@ } } +FT_UInt +ft_render::process_character (FT_ULong code, FT_UInt previous) +{ + FT_Face face = font.face; + FT_UInt glyph_index = 0; + + if (face) + { + glyph_index = FT_Get_Char_Index (face, code); + + if (code != '\n' + && (! glyph_index + || FT_Load_Glyph (face, glyph_index, FT_LOAD_DEFAULT))) + { + glyph_index = 0; + gripe_missing_glyph (code); + } + else + { + switch (mode) + { + case MODE_RENDER: + if (code == '\n') + { + glyph_index = FT_Get_Char_Index (face, ' '); + if (!glyph_index || FT_Load_Glyph (face, glyph_index, FT_LOAD_DEFAULT)) + { + glyph_index = 0; + gripe_missing_glyph (' '); + } + else + push_new_line (); + } + else if (FT_Render_Glyph (face->glyph, FT_RENDER_MODE_NORMAL)) + { + glyph_index = 0; + gripe_glyph_render (code); + } + else + { + FT_Bitmap& bitmap = face->glyph->bitmap; + int x0, y0; + + if (previous) + { + FT_Vector delta; + + FT_Get_Kerning (face, previous, glyph_index, FT_KERNING_DEFAULT, &delta); + xoffset += (delta.x >> 6); + } + + x0 = xoffset + face->glyph->bitmap_left; + y0 = line_yoffset + yoffset + face->glyph->bitmap_top; + + // 'w' seems to have a negative -1 + // face->glyph->bitmap_left, this is so we don't + // index out of bound, and assumes we we allocated + // the right amount of horizontal space in the bbox. + if (x0 < 0) + x0 = 0; + + for (int r = 0; r < bitmap.rows; r++) + for (int c = 0; c < bitmap.width; c++) + { + unsigned char pix = bitmap.buffer[r*bitmap.width+c]; + if (x0+c < 0 || x0+c >= pixels.dim2 () + || y0-r < 0 || y0-r >= pixels.dim3 ()) + { + //::warning ("ft_render: pixel out of bound (char=%d, (x,y)=(%d,%d), (w,h)=(%d,%d)", + // str[i], x0+c, y0-r, pixels.dim2 (), pixels.dim3 ()); + } + else if (pixels(3, x0+c, y0-r).value () == 0) + { + pixels(0, x0+c, y0-r) = color(0); + pixels(1, x0+c, y0-r) = color(1); + pixels(2, x0+c, y0-r) = color(2); + pixels(3, x0+c, y0-r) = pix; + } + } + + xoffset += (face->glyph->advance.x >> 6); + } + break; + + case MODE_BBOX: + if (code == '\n') + { + glyph_index = FT_Get_Char_Index (face, ' '); + if (! glyph_index + || FT_Load_Glyph (face, glyph_index, FT_LOAD_DEFAULT)) + { + glyph_index = 0; + gripe_missing_glyph (' '); + } + else + push_new_line (); + } + else + { + Matrix& bb = line_bbox.back (); + + // If we have a previous glyph, use kerning information. + // This usually means moving a bit backward before adding + // the next glyph. That is, "delta.x" is usually < 0. + if (previous) + { + FT_Vector delta; + + FT_Get_Kerning (face, previous, glyph_index, + FT_KERNING_DEFAULT, &delta); + + bb(2) += (delta.x >> 6); + } + + // Extend current line bounding box by the width of the + // current glyph. + bb(2) += (face->glyph->advance.x >> 6); + } + break; + } + } + } + + return glyph_index; +} + void ft_render::visit (text_element_string& e) { - FT_Face face = current_face (); - - if (face) + if (font.is_valid ()) { std::string str = e.string_value (); FT_UInt glyph_index, previous = 0; for (size_t i = 0; i < str.length (); i++) { - glyph_index = FT_Get_Char_Index (face, str[i]); - - if (str[i] != '\n' - && (! glyph_index - || FT_Load_Glyph (face, glyph_index, FT_LOAD_DEFAULT))) - gripe_missing_glyph (str[i]); - else - { - switch (mode) - { - case MODE_RENDER: - if (str[i] == '\n') - { - glyph_index = FT_Get_Char_Index (face, ' '); - if (!glyph_index || FT_Load_Glyph (face, glyph_index, FT_LOAD_DEFAULT)) - { - gripe_missing_glyph (' '); - } - else - push_new_line (); - } - else if (FT_Render_Glyph (face->glyph, FT_RENDER_MODE_NORMAL)) - { - gripe_glyph_render (str[i]); - } - else - { - FT_Bitmap& bitmap = face->glyph->bitmap; - int x0, y0; - - if (previous) - { - FT_Vector delta; - - FT_Get_Kerning (face, previous, glyph_index, FT_KERNING_DEFAULT, &delta); - xoffset += (delta.x >> 6); - } - - x0 = xoffset+face->glyph->bitmap_left; - y0 = yoffset+face->glyph->bitmap_top; - - // 'w' seems to have a negative -1 - // face->glyph->bitmap_left, this is so we don't - // index out of bound, and assumes we we allocated - // the right amount of horizontal space in the bbox. - if (x0 < 0) - x0 = 0; + glyph_index = process_character (str[i], previous); - for (int r = 0; r < bitmap.rows; r++) - for (int c = 0; c < bitmap.width; c++) - { - unsigned char pix = bitmap.buffer[r*bitmap.width+c]; - if (x0+c < 0 || x0+c >= pixels.dim2 () - || y0-r < 0 || y0-r >= pixels.dim3 ()) - { - //::warning ("ft_render: pixel out of bound (char=%d, (x,y)=(%d,%d), (w,h)=(%d,%d)", - // str[i], x0+c, y0-r, pixels.dim2 (), pixels.dim3 ()); - } - else if (pixels(3, x0+c, y0-r).value () == 0) - { - pixels(0, x0+c, y0-r) = red; - pixels(1, x0+c, y0-r) = green; - pixels(2, x0+c, y0-r) = blue; - pixels(3, x0+c, y0-r) = pix; - } - } - - xoffset += (face->glyph->advance.x >> 6); - } - break; - - case MODE_BBOX: - if (str[i] == '\n') - { - glyph_index = FT_Get_Char_Index (face, ' '); - if (! glyph_index - || FT_Load_Glyph (face, glyph_index, FT_LOAD_DEFAULT)) - { - gripe_missing_glyph (' '); - } - else - push_new_line (); - } - else - { - Matrix& bb = line_bbox.back (); - - // If we have a previous glyph, use kerning information. - // This usually means moving a bit backward before adding - // the next glyph. That is, "delta.x" is usually < 0. - if (previous) - { - FT_Vector delta; - - FT_Get_Kerning (face, previous, glyph_index, - FT_KERNING_DEFAULT, &delta); - - bb(2) += (delta.x >> 6); - } - - // Extend current line bounding box by the width of the - // current glyph. - bb(2) += (face->glyph->advance.x >> 6); - } - break; - } - - if (str[i] == '\n') - previous = 0; - else - previous = glyph_index; - } + if (str[i] == '\n') + previous = 0; + else + previous = glyph_index; } } } void +ft_render::visit (text_element_list& e) +{ + // Save and restore (after processing the list) the current font and color. + + ft_font saved_font (font); + uint8NDArray saved_color (color); + + text_processor::visit (e); + + font = saved_font; + color = saved_color; +} + +void +ft_render::visit (text_element_subscript& e) +{ + ft_font saved_font (font); + int saved_line_yoffset = line_yoffset; + int saved_yoffset = yoffset; + + set_font (font.name, font.weight, font.angle, font.size - 2); + if (font.is_valid ()) + { + int h = font.face->size->metrics.height >> 6; + + // Shifting the baseline by 2/3 the font height seems to produce + // decent result. + yoffset -= (h * 2) / 3; + + if (mode == MODE_BBOX) + update_line_bbox (); + } + + text_processor::visit (e); + + font = saved_font; + // If line_yoffset changed, this means we moved to a new line; hence yoffset + // cannot be restored, because the saved value is not relevant anymore. + if (line_yoffset == saved_line_yoffset) + yoffset = saved_yoffset; +} + +void +ft_render::visit (text_element_superscript& e) +{ + ft_font saved_font (font); + int saved_line_yoffset = line_yoffset; + int saved_yoffset = yoffset; + + set_font (font.name, font.weight, font.angle, font.size - 2); + if (saved_font.is_valid () && font.is_valid ()) + { + int s_asc = saved_font.face->size->metrics.ascender >> 6; + + // Shifting the baseline by 2/3 base font ascender seems to produce + // decent result. + yoffset += (s_asc * 2) / 3; + + if (mode == MODE_BBOX) + update_line_bbox (); + } + + text_processor::visit (e); + + font = saved_font; + // If line_yoffset changed, this means we moved to a new line; hence yoffset + // cannot be restored, because the saved value is not relevant anymore. + if (line_yoffset == saved_line_yoffset) + yoffset = saved_yoffset; +} + +void +ft_render::visit (text_element_color& e) +{ + if (mode == MODE_RENDER) + set_color (e.get_color ()); +} + +void +ft_render::visit (text_element_fontsize& e) +{ + double sz = e.get_fontsize (); + + // FIXME: Matlab documentation says that the font size is expressed + // in the text object FontUnit. + + set_font (font.name, font.weight, font.angle, sz); + + if (mode == MODE_BBOX) + update_line_bbox (); +} + +void +ft_render::visit (text_element_fontname& e) +{ + set_font (e.get_fontname (), font.weight, font.angle, font.size); + + if (mode == MODE_BBOX) + update_line_bbox (); +} + +void +ft_render::visit (text_element_fontstyle& e) +{ + switch (e.get_fontstyle ()) + { + case text_element_fontstyle::normal: + set_font (font.name, "normal", "normal", font.size); + break; + case text_element_fontstyle::bold: + set_font (font.name, "bold", "normal", font.size); + break; + case text_element_fontstyle::italic: + set_font (font.name, "normal", "italic", font.size); + break; + case text_element_fontstyle::oblique: + set_font (font.name, "normal", "oblique", font.size); + break; + } + + if (mode == MODE_BBOX) + update_line_bbox (); +} + +void +ft_render::visit (text_element_symbol& e) +{ + uint32_t code = e.get_symbol_code (); + + if (code != text_element_symbol::invalid_code && font.is_valid ()) + process_character (code); + else if (font.is_valid ()) + ::warning ("ignoring unknown symbol: %s", e.string_value ().c_str ()); +} + +void ft_render::reset (void) { set_mode (MODE_BBOX); @@ -611,9 +792,9 @@ { if (c.numel () == 3) { - red = static_cast<uint8_t> (c(0)*255); - green = static_cast<uint8_t> (c(1)*255); - blue = static_cast<uint8_t> (c(2)*255); + color(0) = static_cast<uint8_t> (c(0)*255); + color(1) = static_cast<uint8_t> (c(1)*255); + color(2) = static_cast<uint8_t> (c(2)*255); } else ::warning ("ft_render::set_color: invalid color");