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

Reply via email to