Hi,
When I looked at the code, I saw that FT_LOAD_NO_RECURSE implies
FT_LOAD_NO_SCALE
and FT_LOAD_IGNORE_TRANSFORM which in turn imply FT_LOAD_NO_HINTING,
FT_LOAD_NO_BITMAP
and kills FT_LOAD_RENDER. This is documented. However, that glyph metrics
are not loaded with FT_LOAD_NO_RECURSE is buried behind the function
pointer of the font-format driver, driver->clazz->load_glyph. Also, on
DejaVuSans.ttf, loading the glyph with FT_LOAD_NO_RECURSE gives the same
metrics value as without it. Where as for NotoSansCJK-Regular.ttc the
metrics are all 0 with FT_LOAD_NO_RECURSE. I am thinking that whether or
not glyph metrics are loaded with FT_LOAD_NO_RECURSE is dependent on font
driver, i.e font format.
I've attached a some-what silly scanner whose base flags are
FT_LOAD_NO_SCALE | FT_LOAD_NO_HINTING | FT_LOAD_NO_BITMAP |
FT_LOAD_IGNORE_TRANSFORM | FT_LOAD_LINEAR_DESIGN and I load the glyph with
these flags 3 times: once just with those, then once with those and
FT_LOAD_NO_RECURSE and once with those and FT_LOAD_VERTICAL for the purpose
of comparing the results for glyphs of fonts.
Best Regards,
-Kevin Rogvin
On Wed, Sep 19, 2018 at 6:14 AM Alexei Podtelezhnikov <[email protected]>
wrote:
> Kevin,
>
> To understand relationships between the flags, please study just a few
> lines of code here:
>
> http://git.savannah.gnu.org/cgit/freetype/freetype2.git/tree/src/base/ftobjs.c#n818
>
> FT_LOAD_NO_RECURSE basically cancels everything including metrics
> calculations. It only gives you a list of glyph components for a
> composite glyph if any. It is also the only way to get that
> information. So if you really need both metrics and components, you
> have to load the glyph twice.
>
> Alexei
>
#include <iostream>
#include <iomanip>
#include <vector>
#include <utility>
#include <cstdlib>
#include <assert.h>
#include <ft2build.h>
#include FT_OUTLINE_H
enum type_t
{
type_point,
type_control_pt,
};
enum path_different_t
{
SAME,
TRANSLATE,
DIFFERENT
};
bool
operator==(const FT_Vector a, const FT_Vector b)
{
return a.x == b.x && a.y == b.y;
}
class BoundingBox
{
public:
BoundingBox(void):
m_pts_added(false)
{}
BoundingBox(const BoundingBox &p, int dx, int dy):
m_pts_added(p.m_pts_added),
m_minX(p.m_minX + dx),
m_maxX(p.m_maxX + dx),
m_minY(p.m_minY + dy),
m_maxY(p.m_maxY + dy)
{}
void
update_min_max(const FT_Vector *pt)
{
if (!m_pts_added)
{
m_minX = m_maxX = pt->x;
m_minY = m_maxY = pt->y;
m_pts_added = true;
}
else
{
m_minX = std::min(m_minX, pt->x);
m_minY = std::min(m_minY, pt->y);
m_maxX = std::max(m_maxX, pt->x);
m_maxY = std::max(m_maxY, pt->y);
}
}
bool m_pts_added;
FT_Pos m_minX, m_maxX, m_minY, m_maxY;
};
std::ostream&
operator<<(std::ostream &ostr, const BoundingBox &obj)
{
if (obj.m_pts_added)
{
ostr << "[" << obj.m_minX << ", " << obj.m_maxX << "]x["
<< obj.m_minY << ", " << obj.m_maxY << "]";
}
else
{
ostr << "EMPTY";
}
return ostr;
}
typedef std::pair<type_t, FT_Vector> Point;
typedef std::vector<Point> Contour;
class Path
{
public:
Path(FT_Outline *outline)
{
FT_Outline_Funcs funcs;
funcs.move_to = &ft_outline_move_to;
funcs.line_to = &ft_outline_line_to;
funcs.conic_to = &ft_outline_conic_to;
funcs.cubic_to = &ft_outline_cubic_to;
funcs.shift = 0;
funcs.delta = 0;
FT_Outline_Decompose(outline, &funcs, this);
}
Path(const Path &p, int dx, int dy):
m_contours(p.m_contours),
m_box(p.m_box, dx, dy)
{
for(auto &C : m_contours)
{
for (auto &P : C)
{
P.second.x += dx;
P.second.y += dy;
}
}
}
std::vector<Contour> m_contours;
BoundingBox m_box;
private:
static
int
ft_outline_move_to(const FT_Vector *pt, void *user)
{
Path *p;
Point P(type_point, *pt);
p = static_cast<Path*>(user);
p->m_contours.push_back(Contour());
p->m_contours.back().push_back(P);
p->m_box.update_min_max(pt);
return 0;
}
static
int
ft_outline_line_to(const FT_Vector *pt, void *user)
{
Path *p;
Point P(type_point, *pt);
p = static_cast<Path*>(user);
p->m_contours.back().push_back(P);
p->m_box.update_min_max(pt);
return 0;
}
static
int
ft_outline_conic_to(const FT_Vector *control_pt,
const FT_Vector *pt, void *user)
{
Path *p;
Point CP(type_control_pt, *control_pt);
Point P(type_point, *pt);
p = static_cast<Path*>(user);
p->m_contours.back().push_back(CP);
p->m_contours.back().push_back(P);
p->m_box.update_min_max(pt);
p->m_box.update_min_max(control_pt);
return 0;
}
static
int
ft_outline_cubic_to(const FT_Vector *control_pt0,
const FT_Vector *control_pt1,
const FT_Vector *pt, void *user)
{
Path *p;
Point CP0(type_control_pt, *control_pt0);
Point CP1(type_control_pt, *control_pt1);
Point P(type_point, *pt);
p = static_cast<Path*>(user);
p->m_contours.back().push_back(CP0);
p->m_contours.back().push_back(CP1);
p->m_contours.back().push_back(P);
p->m_box.update_min_max(pt);
p->m_box.update_min_max(control_pt0);
p->m_box.update_min_max(control_pt1);
return 0;
}
};
std::ostream&
operator<<(std::ostream &ostr, const Path &path)
{
for(const auto &C : path.m_contours)
{
std::cout << "[";
for (const auto &P : C)
{
ostr << "(" << P.second.x
<< ", " << P.second.y
<< "):" << P.first
<< " ";
}
ostr << "] ";
}
return ostr;
}
std::ostream&
operator<<(std::ostream &ostr, enum path_different_t t)
{
switch (t)
{
#define C(X) case X: ostr << #X; break
C(SAME);
C(TRANSLATE);
C(DIFFERENT);
default:
ostr << "UNDEFINED(" << (int)t << ")";
}
return ostr;
}
enum path_different_t
path_difference(const Path &P, const Path &Q)
{
if (P.m_contours.empty() != Q.m_contours.empty())
{
return DIFFERENT;
}
if (P.m_contours.empty())
{
return SAME;
}
int dx, dy;
dx = P.m_contours.front().front().second.x
- Q.m_contours.front().front().second.x;
dy = P.m_contours.front().front().second.y
- Q.m_contours.front().front().second.y;
if (P.m_contours == Q.m_contours)
{
assert(dx == 0 && dy == 0);
return SAME;
}
Path Qmoved(Q, dx, dy);
if (P.m_contours == Qmoved.m_contours)
{
return TRANSLATE;
}
return DIFFERENT;
}
std::ostream&
operator<<(std::ostream &ostr, const FT_Glyph_Metrics &m)
{
ostr << "size = " << m.width << "x" << m.height
<< ", hBearing = (" << m.horiBearingX
<< ", " << m.horiBearingY
<< "), vBearing = (" << m.vertBearingX
<< ", " << m.vertBearingY
<< ")";
}
class SubGlyph
{
public:
FT_Int m_glyph_index, m_arg1, m_arg2;
FT_UInt m_flags;
FT_Matrix m_matrix;
};
const FT_Int32 base_flags =
(FT_LOAD_NO_SCALE | FT_LOAD_NO_HINTING
| FT_LOAD_NO_BITMAP | FT_LOAD_IGNORE_TRANSFORM
| FT_LOAD_LINEAR_DESIGN);
void
explore_subglyphs(unsigned int recurse,
const std::vector<SubGlyph> &input,
FT_Face face)
{
std::string prefix(recurse, '\t');
std::cout << prefix << "SubGlyphs:\n";
for(const auto &S : input)
{
int error_code;
error_code = FT_Load_Glyph(face, S.m_glyph_index, base_flags | FT_LOAD_NO_RECURSE);
std::cout << prefix << "\t#" << S.m_glyph_index
<< ", matrix = {"
<< S.m_matrix.xx << ", "
<< S.m_matrix.xy << ", "
<< S.m_matrix.yx << ", "
<< S.m_matrix.yy << "}\n";
if (face->glyph->format == FT_GLYPH_FORMAT_COMPOSITE)
{
std::vector<SubGlyph> sub_glyphs(face->glyph->num_subglyphs);
for (unsigned int s = 0; s < sub_glyphs.size(); ++s)
{
FT_Get_SubGlyph_Info(face->glyph, s,
&sub_glyphs[s].m_glyph_index,
&sub_glyphs[s].m_flags,
&sub_glyphs[s].m_arg1,
&sub_glyphs[s].m_arg2,
&sub_glyphs[s].m_matrix);
}
explore_subglyphs(1 + recurse, sub_glyphs, face);
}
}
}
void
examine_glyph(FT_Face face, int n)
{
FT_Glyph_Metrics hm, vm;
int h_nc, h_np, v_nc, v_np;
int error_code;
error_code = FT_Load_Glyph(face, n, base_flags);
std::cout << "#" << std::setw(4) << n << ": ";
FT_Load_Glyph(face, n, base_flags);
Path HP(&face->glyph->outline);
hm = face->glyph->metrics;
h_nc = face->glyph->outline.n_contours;
h_np = face->glyph->outline.n_points;
FT_Load_Glyph(face, n, base_flags | FT_LOAD_VERTICAL_LAYOUT);
Path VP(&face->glyph->outline);
vm = face->glyph->metrics;
v_nc = face->glyph->outline.n_contours;
v_np = face->glyph->outline.n_points;
std::cout << path_difference(HP, VP)
<< "\n\tHP = " << hm << ", box = " << HP.m_box << ", nc = " << h_nc << ", np = " << h_np
<< "\n\tVP = " << vm << ", box = " << VP.m_box << ", nc = " << v_nc << ", np = " << v_np
<< "\n";
FT_Load_Glyph(face, n, base_flags | FT_LOAD_NO_RECURSE | FT_LOAD_VERTICAL_LAYOUT);
FT_Glyph_Metrics sm(face->glyph->metrics);
if (face->glyph->format == FT_GLYPH_FORMAT_COMPOSITE)
{
std::vector<SubGlyph> sub_glyphs(face->glyph->num_subglyphs);
for (unsigned int s = 0; s < sub_glyphs.size(); ++s)
{
FT_Get_SubGlyph_Info(face->glyph, s,
&sub_glyphs[s].m_glyph_index,
&sub_glyphs[s].m_flags,
&sub_glyphs[s].m_arg1,
&sub_glyphs[s].m_arg2,
&sub_glyphs[s].m_matrix);
}
explore_subglyphs(1, sub_glyphs, face);
std::cout << "\tSP = " << sm << "\n";
}
else
{
Path SP(&face->glyph->outline);
std::cout << "\tNon-composite: ";
std::cout << path_difference(HP, SP)
<< "\tSP = " << sm << ", box = " << SP.m_box << "\n";
}
}
int
main(int argc, char **argv)
{
if (argc != 2 && argc != 3)
{
return -1;
}
FT_Library lib(NULL);
FT_Face face(NULL);
int error_code;
int return_value;
error_code = FT_Init_FreeType(&lib);
if (error_code != 0)
{
std::cerr << "Failed to open FreeType library\n";
return -1;
}
error_code = FT_New_Face(lib, argv[1], 0, &face);
if (error_code == 0 && face)
{
if (argc == 3)
{
int n;
n = std::atoi(argv[2]);
examine_glyph(face, n);
}
else
{
for (unsigned int n = 0; n < face->num_glyphs; ++n)
{
examine_glyph(face, n);
}
}
}
if (face)
{
FT_Done_Face(face);
}
if (lib)
{
FT_Done_FreeType(lib);
}
return 0;
}
_______________________________________________
Freetype mailing list
[email protected]
https://lists.nongnu.org/mailman/listinfo/freetype