@@ -499,6 +499,7 @@ PyFT2Font_init(py::object filename, long hinting_factor = 8,
499499
500500 self->x = new FT2Font (open_args, hinting_factor, fallback_fonts, ft_glyph_warn,
501501 warn_if_used);
502+ self->x ->set_parent (self);
502503
503504 self->x ->set_kerning_factor (kerning_factor);
504505
@@ -1467,6 +1468,97 @@ PyFT2Font__get_type1_encoding_vector(PyFT2Font *self)
14671468 return indices;
14681469}
14691470
1471+ /* *********************************************************************
1472+ * Layout items
1473+ * */
1474+
1475+ struct LayoutItem {
1476+ PyFT2Font *ft_object;
1477+ std::u32string character;
1478+ int glyph_idx;
1479+ double x;
1480+ double y;
1481+ double prev_kern;
1482+
1483+ LayoutItem (PyFT2Font *f, std::u32string c, int i, double x, double y, double k) :
1484+ ft_object (f), character(c), glyph_idx(i), x(x), y(y), prev_kern(k) {}
1485+
1486+ std::string to_string ()
1487+ {
1488+ std::ostringstream out;
1489+ out << " LayoutItem(ft_object=" << PyFT2Font_fname (ft_object);
1490+ out << " , char=" << character[0 ];
1491+ out << " , glyph_idx=" << glyph_idx;
1492+ out << " , x=" << x;
1493+ out << " , y=" << y;
1494+ out << " , prev_kern=" << prev_kern;
1495+ out << " )" ;
1496+ return out.str ();
1497+ }
1498+ };
1499+
1500+ const char *PyFT2Font_layout__doc__ = R"""(
1501+ Layout a string and yield information about each used glyph.
1502+
1503+ .. warning::
1504+ This API uses the fallback list and is both private and provisional: do not use
1505+ it directly.
1506+
1507+ Parameters
1508+ ----------
1509+ text : str
1510+ The characters for which to find fonts.
1511+
1512+ Returns
1513+ -------
1514+ list[LayoutItem]
1515+ )""" ;
1516+
1517+ static auto
1518+ PyFT2Font_layout (PyFT2Font *self, std::u32string text, LoadFlags flags)
1519+ {
1520+ const auto hinting_factor = self->x ->get_hinting_factor ();
1521+ const auto load_flags = static_cast <FT_Int32>(flags);
1522+
1523+ std::set<FT_String*> glyph_seen_fonts;
1524+ std::vector<raqm_glyph_t > glyphs;
1525+ self->x ->layout (text, load_flags, glyph_seen_fonts, glyphs);
1526+
1527+ std::vector<LayoutItem> items;
1528+
1529+ double x = 0.0 ;
1530+ double y = 0.0 ;
1531+ std::optional<double > prev_advance = std::nullopt ;
1532+ double prev_x = 0.0 ;
1533+ for (auto &glyph : glyphs) {
1534+ auto ft_object = static_cast <FT2Font *>(glyph.ftface ->generic .data );
1535+ auto pyft_object = static_cast <PyFT2Font *>(ft_object->get_parent ());
1536+
1537+ ft_object->load_glyph (glyph.index , load_flags);
1538+
1539+ double prev_kern = 0.0 ;
1540+ if (prev_advance.has_value ()) {
1541+ double actual_advance = (x + glyph.x_offset ) - prev_x;
1542+ prev_kern = actual_advance - prev_advance.value ();
1543+ }
1544+
1545+ items.emplace_back (pyft_object, text.substr (glyph.cluster , 1 ), glyph.index ,
1546+ (x + glyph.x_offset ) / 64.0 , (y + glyph.y_offset ) / 64.0 ,
1547+ prev_kern / 64.0 );
1548+ prev_x = x + glyph.x_offset ;
1549+ x += glyph.x_advance ;
1550+ y += glyph.y_advance ;
1551+ // Note, linearHoriAdvance is a 16.16 instead of 26.6 fixed-point value.
1552+ prev_advance = ft_object->get_face ()->glyph ->linearHoriAdvance / 1024.0 / hinting_factor;
1553+ }
1554+
1555+ return items;
1556+ }
1557+
1558+ /* *********************************************************************
1559+ * Deprecations
1560+ * */
1561+
14701562static py::object
14711563ft2font__getattr__ (std::string name) {
14721564 auto api = py::module_::import (" matplotlib._api" );
@@ -1601,8 +1693,23 @@ PYBIND11_MODULE(ft2font, m, py::mod_gil_not_used())
16011693 .def_property_readonly (" bbox" , &PyGlyph_get_bbox,
16021694 " The control box of the glyph." );
16031695
1604- auto cls = py::class_<PyFT2Font>(m, " FT2Font" , py::is_final (), py::buffer_protocol (),
1605- PyFT2Font__doc__)
1696+ py::class_<LayoutItem>(m, " LayoutItem" , py::is_final ())
1697+ .def_readonly (" ft_object" , &LayoutItem::ft_object,
1698+ " The FT_Face of the item." )
1699+ .def_readonly (" char" , &LayoutItem::character,
1700+ " The character code for the item." )
1701+ .def_readonly (" glyph_idx" , &LayoutItem::glyph_idx,
1702+ " The glyph index for the item." )
1703+ .def_readonly (" x" , &LayoutItem::x,
1704+ " The x position of the item." )
1705+ .def_readonly (" y" , &LayoutItem::y,
1706+ " The y position of the item." )
1707+ .def_readonly (" prev_kern" , &LayoutItem::prev_kern,
1708+ " The kerning between this item and the previous one." )
1709+ .def (" __str__" , &LayoutItem::to_string);
1710+
1711+ auto cls = py::class_<PyFT2Font>(m, " FT2Font" , py::is_final (), py::buffer_protocol (),
1712+ PyFT2Font__doc__)
16061713 .def (py::init (&PyFT2Font_init),
16071714 " filename" _a, " hinting_factor" _a=8 , py::kw_only (),
16081715 " _fallback_list" _a=py::none (), " _kerning_factor" _a=0 ,
@@ -1617,6 +1724,8 @@ PYBIND11_MODULE(ft2font, m, py::mod_gil_not_used())
16171724 PyFT2Font_select_charmap__doc__)
16181725 .def (" get_kerning" , &PyFT2Font_get_kerning, " left" _a, " right" _a, " mode" _a,
16191726 PyFT2Font_get_kerning__doc__)
1727+ .def (" _layout" , &PyFT2Font_layout, " string" _a, " flags" _a,
1728+ PyFT2Font_layout__doc__)
16201729 .def (" set_text" , &PyFT2Font_set_text,
16211730 " string" _a, " angle" _a=0.0 , " flags" _a=LoadFlags::FORCE_AUTOHINT,
16221731 PyFT2Font_set_text__doc__)
0 commit comments