#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION
#include
#include
#include
#include "ft2font.h"
#include "_enums.h"
#include
#include
#include
namespace py = pybind11;
using namespace pybind11::literals;
/**********************************************************************
* Enumerations
* */
const char *Kerning__doc__ = R"""(
Kerning modes for `.FT2Font.get_kerning`.
For more information, see `the FreeType documentation
`_.
.. versionadded:: 3.10
)""";
P11X_DECLARE_ENUM(
"Kerning", "Enum",
{"DEFAULT", FT_KERNING_DEFAULT},
{"UNFITTED", FT_KERNING_UNFITTED},
{"UNSCALED", FT_KERNING_UNSCALED},
);
const char *FaceFlags__doc__ = R"""(
Flags returned by `FT2Font.face_flags`.
For more information, see `the FreeType documentation
`_.
.. versionadded:: 3.10
)""";
#ifndef FT_FACE_FLAG_VARIATION // backcompat: ft 2.9.0.
#define FT_FACE_FLAG_VARIATION (1L Created[0]),
static_cast(t->Created[1])),
"modified"_a=py::make_tuple(static_cast(t->Modified[0]),
static_cast(t->Modified[1])),
"xMin"_a=t->xMin,
"yMin"_a=t->yMin,
"xMax"_a=t->xMax,
"yMax"_a=t->yMax,
"macStyle"_a=t->Mac_Style,
"lowestRecPPEM"_a=t->Lowest_Rec_PPEM,
"fontDirectionHint"_a=t->Font_Direction,
"indexToLocFormat"_a=t->Index_To_Loc_Format,
"glyphDataFormat"_a=t->Glyph_Data_Format);
}
case FT_SFNT_MAXP: {
auto t = static_cast(table);
return py::dict(
"version"_a=py::make_tuple(FIXED_MAJOR(t->version),
FIXED_MINOR(t->version)),
"numGlyphs"_a=t->numGlyphs,
"maxPoints"_a=t->maxPoints,
"maxContours"_a=t->maxContours,
"maxComponentPoints"_a=t->maxCompositePoints,
"maxComponentContours"_a=t->maxCompositeContours,
"maxZones"_a=t->maxZones,
"maxTwilightPoints"_a=t->maxTwilightPoints,
"maxStorage"_a=t->maxStorage,
"maxFunctionDefs"_a=t->maxFunctionDefs,
"maxInstructionDefs"_a=t->maxInstructionDefs,
"maxStackElements"_a=t->maxStackElements,
"maxSizeOfInstructions"_a=t->maxSizeOfInstructions,
"maxComponentElements"_a=t->maxComponentElements,
"maxComponentDepth"_a=t->maxComponentDepth);
}
case FT_SFNT_OS2: {
auto t = static_cast(table);
auto version = t->version;
auto result = py::dict(
"version"_a=version,
"xAvgCharWidth"_a=t->xAvgCharWidth,
"usWeightClass"_a=t->usWeightClass,
"usWidthClass"_a=t->usWidthClass,
"fsType"_a=t->fsType,
"ySubscriptXSize"_a=t->ySubscriptXSize,
"ySubscriptYSize"_a=t->ySubscriptYSize,
"ySubscriptXOffset"_a=t->ySubscriptXOffset,
"ySubscriptYOffset"_a=t->ySubscriptYOffset,
"ySuperscriptXSize"_a=t->ySuperscriptXSize,
"ySuperscriptYSize"_a=t->ySuperscriptYSize,
"ySuperscriptXOffset"_a=t->ySuperscriptXOffset,
"ySuperscriptYOffset"_a=t->ySuperscriptYOffset,
"yStrikeoutSize"_a=t->yStrikeoutSize,
"yStrikeoutPosition"_a=t->yStrikeoutPosition,
"sFamilyClass"_a=t->sFamilyClass,
"panose"_a=py::bytes(reinterpret_cast(t->panose), 10),
"ulUnicodeRange"_a=py::make_tuple(t->ulUnicodeRange1, t->ulUnicodeRange2,
t->ulUnicodeRange3, t->ulUnicodeRange4),
"achVendID"_a=py::bytes(reinterpret_cast(t->achVendID), 4),
"fsSelection"_a=t->fsSelection,
"usFirstCharIndex"_a=t->usFirstCharIndex,
"usLastCharIndex"_a=t->usLastCharIndex,
"sTypoAscender"_a=t->sTypoAscender,
"sTypoDescender"_a=t->sTypoDescender,
"sTypoLineGap"_a=t->sTypoLineGap,
"usWinAscent"_a=t->usWinAscent,
"usWinDescent"_a=t->usWinDescent);
if (version >= 1) {
result["ulCodePageRange"] = py::make_tuple(t->ulCodePageRange1,
t->ulCodePageRange2);
}
if (version >= 2) {
result["sxHeight"] = t->sxHeight;
result["sCapHeight"] = t->sCapHeight;
result["usDefaultChar"] = t->usDefaultChar;
result["usBreakChar"] = t->usBreakChar;
result["usMaxContext"] = t->usMaxContext;
}
if (version >= 5) {
result["usLowerOpticalPointSize"] = t->usLowerOpticalPointSize;
result["usUpperOpticalPointSize"] = t->usUpperOpticalPointSize;
}
return result;
}
case FT_SFNT_HHEA: {
auto t = static_cast(table);
return py::dict(
"version"_a=py::make_tuple(FIXED_MAJOR(t->Version),
FIXED_MINOR(t->Version)),
"ascent"_a=t->Ascender,
"descent"_a=t->Descender,
"lineGap"_a=t->Line_Gap,
"advanceWidthMax"_a=t->advance_Width_Max,
"minLeftBearing"_a=t->min_Left_Side_Bearing,
"minRightBearing"_a=t->min_Right_Side_Bearing,
"xMaxExtent"_a=t->xMax_Extent,
"caretSlopeRise"_a=t->caret_Slope_Rise,
"caretSlopeRun"_a=t->caret_Slope_Run,
"caretOffset"_a=t->caret_Offset,
"metricDataFormat"_a=t->metric_Data_Format,
"numOfLongHorMetrics"_a=t->number_Of_HMetrics);
}
case FT_SFNT_VHEA: {
auto t = static_cast(table);
return py::dict(
"version"_a=py::make_tuple(FIXED_MAJOR(t->Version),
FIXED_MINOR(t->Version)),
"vertTypoAscender"_a=t->Ascender,
"vertTypoDescender"_a=t->Descender,
"vertTypoLineGap"_a=t->Line_Gap,
"advanceHeightMax"_a=t->advance_Height_Max,
"minTopSideBearing"_a=t->min_Top_Side_Bearing,
"minBottomSideBearing"_a=t->min_Bottom_Side_Bearing,
"yMaxExtent"_a=t->yMax_Extent,
"caretSlopeRise"_a=t->caret_Slope_Rise,
"caretSlopeRun"_a=t->caret_Slope_Run,
"caretOffset"_a=t->caret_Offset,
"metricDataFormat"_a=t->metric_Data_Format,
"numOfLongVerMetrics"_a=t->number_Of_VMetrics);
}
case FT_SFNT_POST: {
auto t = static_cast(table);
return py::dict(
"format"_a=py::make_tuple(FIXED_MAJOR(t->FormatType),
FIXED_MINOR(t->FormatType)),
"italicAngle"_a=py::make_tuple(FIXED_MAJOR(t->italicAngle),
FIXED_MINOR(t->italicAngle)),
"underlinePosition"_a=t->underlinePosition,
"underlineThickness"_a=t->underlineThickness,
"isFixedPitch"_a=t->isFixedPitch,
"minMemType42"_a=t->minMemType42,
"maxMemType42"_a=t->maxMemType42,
"minMemType1"_a=t->minMemType1,
"maxMemType1"_a=t->maxMemType1);
}
case FT_SFNT_PCLT: {
auto t = static_cast(table);
return py::dict(
"version"_a=py::make_tuple(FIXED_MAJOR(t->Version),
FIXED_MINOR(t->Version)),
"fontNumber"_a=t->FontNumber,
"pitch"_a=t->Pitch,
"xHeight"_a=t->xHeight,
"style"_a=t->Style,
"typeFamily"_a=t->TypeFamily,
"capHeight"_a=t->CapHeight,
"symbolSet"_a=t->SymbolSet,
"typeFace"_a=py::bytes(reinterpret_cast(t->TypeFace), 16),
"characterComplement"_a=py::bytes(
reinterpret_cast(t->CharacterComplement), 8),
"strokeWeight"_a=t->StrokeWeight,
"widthType"_a=t->WidthType,
"serifStyle"_a=t->SerifStyle);
}
default:
return std::nullopt;
}
}
const char *PyFT2Font_get_path__doc__ = R"""(
Get the path data from the currently loaded glyph.
Returns
-------
vertices : np.ndarray[double]
The (N, 2) array of vertices describing the current glyph.
codes : np.ndarray[np.uint8]
The (N, ) array of codes corresponding to the vertices.
See Also
--------
.get_image
.load_char
.load_glyph
.set_text
)""";
static py::tuple
PyFT2Font_get_path(PyFT2Font *self)
{
std::vector vertices;
std::vector codes;
self->get_path(vertices, codes);
py::ssize_t length = codes.size();
py::ssize_t vertices_dims[2] = { length, 2 };
py::array_t vertices_arr(vertices_dims);
if (length > 0) {
memcpy(vertices_arr.mutable_data(), vertices.data(), vertices_arr.nbytes());
}
py::ssize_t codes_dims[1] = { length };
py::array_t codes_arr(codes_dims);
if (length > 0) {
memcpy(codes_arr.mutable_data(), codes.data(), codes_arr.nbytes());
}
return py::make_tuple(vertices_arr, codes_arr);
}
const char *PyFT2Font_get_image__doc__ = R"""(
Return the underlying image buffer for this font object.
Returns
-------
np.ndarray[int]
See Also
--------
.get_path
)""";
const char *PyFT2Font__get_type1_encoding_vector__doc__ = R"""(
Return a list mapping CharString indices of a Type 1 font to FreeType glyph indices.
Returns
-------
list[int]
)""";
static std::array
PyFT2Font__get_type1_encoding_vector(PyFT2Font *self)
{
auto face = self->get_face();
auto indices = std::array{};
for (auto i = 0u; i < indices.size(); ++i) {
auto len = FT_Get_PS_Font_Value(face, PS_DICT_ENCODING_ENTRY, i, nullptr, 0);
if (len == -1) {
// Explicitly ignore missing entries (mapped to glyph 0 = .notdef).
continue;
}
auto buf = std::make_unique(len);
FT_Get_PS_Font_Value(face, PS_DICT_ENCODING_ENTRY, i, buf.get(), len);
indices[i] = FT_Get_Name_Index(face, buf.get());
}
return indices;
}
/**********************************************************************
* Layout items
* */
struct LayoutItem {
PyFT2Font *ft_object;
std::u32string character;
int glyph_index;
double x;
double y;
double prev_kern;
LayoutItem(PyFT2Font *f, std::u32string c, int i, double x, double y, double k) :
ft_object(f), character(c), glyph_index(i), x(x), y(y), prev_kern(k) {}
};
const char *PyFT2Font_layout__doc__ = R"""(
Layout a string and yield information about each used glyph.
.. warning::
This API uses the fallback list and is both private and provisional: do not use
it directly.
.. versionadded:: 3.11
Parameters
----------
text : str
The characters for which to find fonts.
flags : LoadFlags, default: `.LoadFlags.FORCE_AUTOHINT`
Any bitwise-OR combination of the `.LoadFlags` flags.
features : tuple[str, ...], optional
The font feature tags to use for the font.
Available font feature tags may be found at
https://learn.microsoft.com/en-us/typography/opentype/spec/featurelist
language : str, optional
The language of the text in a format accepted by libraqm, namely `a BCP47
language code `_.
Returns
-------
list[LayoutItem]
)""";
static auto
PyFT2Font_layout(PyFT2Font *self, std::u32string text, LoadFlags flags,
std::optional features = std::nullopt,
std::variant languages_or_str = nullptr)
{
const auto load_flags = static_cast(flags);
FT2Font::LanguageType languages;
if (auto value = std::get_if(&languages_or_str)) {
languages = std::move(*value);
} else if (auto value = std::get_if(&languages_or_str)) {
languages = std::vector{
FT2Font::LanguageRange{*value, 0, text.size()}
};
} else {
// NOTE: this can never happen as pybind11 would have checked the type in the
// Python wrapper before calling this function, but we need to keep the
// std::get_if instead of std::get for macOS 10.12 compatibility.
throw py::type_error("languages must be str or list of tuple");
}
std::set glyph_seen_fonts;
auto glyphs = self->layout(text, load_flags, features, languages, glyph_seen_fonts);
std::set clusters;
for (auto &glyph : glyphs) {
clusters.emplace(glyph.cluster);
}
std::vector items;
double x = 0.0;
double y = 0.0;
std::optional prev_advance = std::nullopt;
double prev_x = 0.0;
for (auto &glyph : glyphs) {
auto ft_object = static_cast(glyph.ftface->generic.data);
ft_object->load_glyph(glyph.index, load_flags);
double prev_kern = 0.0;
if (prev_advance) {
double actual_advance = (x + glyph.x_offset) - prev_x;
prev_kern = actual_advance - *prev_advance;
}
auto next = clusters.upper_bound(glyph.cluster);
auto end = (next != clusters.end()) ? *next : text.size();
auto substr = text.substr(glyph.cluster, end - glyph.cluster);
items.emplace_back(ft_object, substr, glyph.index,
(x + glyph.x_offset) / 64.0, (y + glyph.y_offset) / 64.0,
prev_kern / 64.0);
prev_x = x + glyph.x_offset;
x += glyph.x_advance;
y += glyph.y_advance;
// Note, linearHoriAdvance is a 16.16 instead of 26.6 fixed-point value.
prev_advance = ft_object->get_face()->glyph->linearHoriAdvance / 1024.0;
}
return items;
}
/**********************************************************************
* Deprecations
* */
PYBIND11_MODULE(ft2font, m, py::mod_gil_not_used())
{
if (FT_Init_FreeType(&_ft2Library)) { // initialize library
throw std::runtime_error("Could not initialize the freetype2 library");
}
FT_Int major, minor, patch;
char version_string[64];
FT_Library_Version(_ft2Library, &major, &minor, &patch);
snprintf(version_string, sizeof(version_string), "%d.%d.%d", major, minor, patch);
p11x::bind_enums(m);
p11x::enums["Kerning"].attr("__doc__") = Kerning__doc__;
p11x::enums["LoadFlags"].attr("__doc__") = LoadFlags__doc__;
p11x::enums["RenderMode"].attr("__doc__") = RenderMode__doc__;
p11x::enums["FaceFlags"].attr("__doc__") = FaceFlags__doc__;
p11x::enums["StyleFlags"].attr("__doc__") = StyleFlags__doc__;
py::class_(m, "FT2Image", py::is_final(), py::buffer_protocol(),
PyFT2Image__doc__)
.def(py::init(
[](long width, long height) {
auto warn =
py::module_::import("matplotlib._api").attr("warn_deprecated");
warn("since"_a="3.11", "name"_a="FT2Image", "obj_type"_a="class",
"alternative"_a="a 2D uint8 ndarray");
return new FT2Image(width, height);
}),
"width"_a, "height"_a, PyFT2Image_init__doc__)
.def("draw_rect_filled", &FT2Image::draw_rect_filled,
"x0"_a, "y0"_a, "x1"_a, "y1"_a,
PyFT2Image_draw_rect_filled__doc__)
.def_buffer([](FT2Image &self) -> py::buffer_info {
std::vector shape { self.get_height(), self.get_width() };
std::vector strides { self.get_width(), 1 };
return py::buffer_info(self.get_buffer(), shape, strides);
});
py::class_(m, "_PositionedBitmap", py::is_final())
.def_readonly("left", &PyPositionedBitmap::left)
.def_readonly("top", &PyPositionedBitmap::top)
.def_property_readonly(
"buffer", [](PyPositionedBitmap &self) -> py::array {
return {{self.bitmap.rows, self.bitmap.width},
{self.bitmap.pitch, 1},
self.bitmap.buffer};
})
;
py::class_(m, "Glyph", py::is_final(), PyGlyph__doc__)
.def(py::init([]() -> PyGlyph {
// Glyph is not useful from Python, so mark it as not constructible.
throw std::runtime_error("Glyph is not constructible");
}))
.def_readonly("width", &PyGlyph::width, "The glyph's width.")
.def_readonly("height", &PyGlyph::height, "The glyph's height.")
.def_readonly("horiBearingX", &PyGlyph::horiBearingX,
"Left side bearing for horizontal layout.")
.def_readonly("horiBearingY", &PyGlyph::horiBearingY,
"Top side bearing for horizontal layout.")
.def_readonly("horiAdvance", &PyGlyph::horiAdvance,
"Advance width for horizontal layout.")
.def_readonly("linearHoriAdvance", &PyGlyph::linearHoriAdvance,
"The advance width of the unhinted glyph.")
.def_readonly("vertBearingX", &PyGlyph::vertBearingX,
"Left side bearing for vertical layout.")
.def_readonly("vertBearingY", &PyGlyph::vertBearingY,
"Top side bearing for vertical layout.")
.def_readonly("vertAdvance", &PyGlyph::vertAdvance,
"Advance height for vertical layout.")
.def_property_readonly("bbox", &PyGlyph_get_bbox,
"The control box of the glyph.");
py::class_(m, "LayoutItem", py::is_final())
.def(py::init([]() -> LayoutItem {
// LayoutItem is not useful from Python, so mark it as not constructible.
throw std::runtime_error("LayoutItem is not constructible");
}))
.def_readonly("ft_object", &LayoutItem::ft_object,
"The FT_Face of the item.")
.def_readonly("char", &LayoutItem::character,
"The character code for the item.")
.def_readonly("glyph_index", &LayoutItem::glyph_index,
"The glyph index for the item.")
.def_readonly("x", &LayoutItem::x,
"The x position of the item.")
.def_readonly("y", &LayoutItem::y,
"The y position of the item.")
.def_readonly("prev_kern", &LayoutItem::prev_kern,
"The kerning between this item and the previous one.")
.def("__str__",
[](const LayoutItem& item) {
return
"LayoutItem(ft_object={}, char={!r}, glyph_index={}, "_s
"x={}, y={}, prev_kern={})"_s.format(
PyFT2Font_fname(item.ft_object), item.character,
item.glyph_index, item.x, item.y, item.prev_kern);
});
auto cls = py::class_(m, "FT2Font", py::is_final(), py::buffer_protocol(),
PyFT2Font__doc__)
.def(py::init(&PyFT2Font_init),
"filename"_a, "hinting_factor"_a=py::none(), py::kw_only(),
"face_index"_a=0,
"_fallback_list"_a=py::none(), "_kerning_factor"_a=py::none(),
"_warn_if_used"_a=false,
PyFT2Font_init__doc__)
.def("clear", &PyFT2Font::clear, PyFT2Font_clear__doc__)
.def("set_size", &PyFT2Font::set_size, "ptsize"_a, "dpi"_a,
PyFT2Font_set_size__doc__)
.def("_set_transform", &PyFT2Font::_set_transform, "matrix"_a, "delta"_a,
PyFT2Font__set_transform__doc__)
.def("set_charmap", &PyFT2Font::set_charmap, "i"_a,
PyFT2Font_set_charmap__doc__)
.def("select_charmap", &PyFT2Font::select_charmap, "i"_a,
PyFT2Font_select_charmap__doc__)
.def("get_kerning", &PyFT2Font::get_kerning, "left"_a, "right"_a, "mode"_a,
PyFT2Font_get_kerning__doc__)
.def("_layout", &PyFT2Font_layout, "string"_a, "flags"_a, py::kw_only(),
"features"_a=nullptr, "language"_a=nullptr,
PyFT2Font_layout__doc__)
.def("set_text", &PyFT2Font_set_text,
"string"_a, "angle"_a=0.0, "flags"_a=LoadFlags::FORCE_AUTOHINT, py::kw_only(),
"features"_a=nullptr, "language"_a=nullptr,
PyFT2Font_set_text__doc__)
.def("get_num_glyphs", &PyFT2Font::get_num_glyphs,
PyFT2Font_get_num_glyphs__doc__)
.def("load_char", &PyFT2Font_load_char,
"charcode"_a, "flags"_a=LoadFlags::FORCE_AUTOHINT,
PyFT2Font_load_char__doc__)
.def("load_glyph", &PyFT2Font_load_glyph,
"glyph_index"_a, "flags"_a=LoadFlags::FORCE_AUTOHINT,
PyFT2Font_load_glyph__doc__)
.def("get_width_height", &PyFT2Font::get_width_height,
PyFT2Font_get_width_height__doc__)
.def("get_bitmap_offset", &PyFT2Font::get_bitmap_offset,
PyFT2Font_get_bitmap_offset__doc__)
.def("get_descent", &PyFT2Font::get_descent, PyFT2Font_get_descent__doc__)
.def("draw_glyphs_to_bitmap", &PyFT2Font::draw_glyphs_to_bitmap,
py::kw_only(), "antialiased"_a=true,
PyFT2Font_draw_glyphs_to_bitmap__doc__);
// The generated docstring uses an unqualified "Buffer" as type hint,
// which causes an error in sphinx. This is fixed as of pybind11
// master (since #5566) which now uses "collections.abc.Buffer";
// restore the signature once that version is released.
{
py::options options{};
options.disable_function_signatures();
cls
.def("draw_glyph_to_bitmap", &PyFT2Font_draw_glyph_to_bitmap,
"image"_a, "x"_a, "y"_a, "glyph"_a, py::kw_only(), "antialiased"_a=true,
PyFT2Font_draw_glyph_to_bitmap__doc__);
}
cls
.def("get_glyph_name", &PyFT2Font::get_glyph_name, "index"_a,
PyFT2Font_get_glyph_name__doc__)
.def("get_charmap", &PyFT2Font_get_charmap, PyFT2Font_get_charmap__doc__)
.def("get_char_index", &PyFT2Font::get_char_index,
"codepoint"_a, py::kw_only(), "_fallback"_a=true,
PyFT2Font_get_char_index__doc__)
.def("get_sfnt", &PyFT2Font_get_sfnt, PyFT2Font_get_sfnt__doc__)
.def("get_name_index", &PyFT2Font::get_name_index, "name"_a,
PyFT2Font_get_name_index__doc__)
.def("get_ps_font_info", &PyFT2Font_get_ps_font_info,
PyFT2Font_get_ps_font_info__doc__)
.def("get_sfnt_table", &PyFT2Font_get_sfnt_table, "name"_a,
PyFT2Font_get_sfnt_table__doc__)
.def("get_path", &PyFT2Font_get_path, PyFT2Font_get_path__doc__)
.def("get_image", &PyFT2Font::get_image, PyFT2Font_get_image__doc__)
.def("_get_type1_encoding_vector", &PyFT2Font__get_type1_encoding_vector,
PyFT2Font__get_type1_encoding_vector__doc__)
.def_property_readonly(
"postscript_name", [](PyFT2Font *self) {
if (const char *name = FT_Get_Postscript_Name(self->get_face())) {
return name;
} else {
return "UNAVAILABLE";
}
}, "PostScript name of the font.")
.def_property_readonly(
"num_faces", [](PyFT2Font *self) {
return self->get_face()->num_faces & 0xffff;
}, "Number of faces in file.")
.def_property_readonly(
"face_index", [](PyFT2Font *self) {
return self->get_face()->face_index;
}, "The index of the font in the file.")
.def_property_readonly(
"family_name", [](PyFT2Font *self) {
if (const char *name = self->get_face()->family_name) {
return name;
} else {
return "UNAVAILABLE";
}
}, "Face family name.")
.def_property_readonly(
"style_name", [](PyFT2Font *self) {
if (const char *name = self->get_face()->style_name) {
return name;
} else {
return "UNAVAILABLE";
}
}, "Style name.")
.def_property_readonly(
"face_flags", [](PyFT2Font *self) {
return static_cast(self->get_face()->face_flags);
}, "Face flags; see `.FaceFlags`.")
.def_property_readonly(
"style_flags", [](PyFT2Font *self) {
return static_cast(self->get_face()->style_flags & 0xffff);
}, "Style flags; see `.StyleFlags`.")
.def_property_readonly(
"num_named_instances", [](PyFT2Font *self) {
return (self->get_face()->style_flags & 0x7fff0000) >> 16;
}, "Number of named instances in the face.")
.def_property_readonly(
"num_glyphs", [](PyFT2Font *self) {
return self->get_face()->num_glyphs;
}, "Number of glyphs in the face.")
.def_property_readonly(
"num_fixed_sizes", [](PyFT2Font *self) {
return self->get_face()->num_fixed_sizes;
}, "Number of bitmap in the face.")
.def_property_readonly(
"num_charmaps", [](PyFT2Font *self) {
return self->get_face()->num_charmaps;
}, "Number of charmaps in the face.")
.def_property_readonly(
"scalable", [](PyFT2Font *self) {
return bool(FT_IS_SCALABLE(self->get_face()));
}, "Whether face is scalable; attributes after this one "
"are only defined for scalable faces.")
.def_property_readonly(
"units_per_EM", [](PyFT2Font *self) {
return self->get_face()->units_per_EM;
}, "Number of font units covered by the EM.")
.def_property_readonly(
"bbox", [](PyFT2Font *self) {
FT_BBox bbox = self->get_face()->bbox;
return py::make_tuple(bbox.xMin, bbox.yMin, bbox.xMax, bbox.yMax);
}, "Face global bounding box (xmin, ymin, xmax, ymax).")
.def_property_readonly(
"ascender", [](PyFT2Font *self) {
return self->get_face()->ascender;
}, "Ascender in 26.6 units.")
.def_property_readonly(
"descender", [](PyFT2Font *self) {
return self->get_face()->descender;
}, "Descender in 26.6 units.")
.def_property_readonly(
"height", [](PyFT2Font *self) {
return self->get_face()->height;
}, "Height in 26.6 units; used to compute a default line spacing "
"(baseline-to-baseline distance).")
.def_property_readonly(
"max_advance_width", [](PyFT2Font *self) {
return self->get_face()->max_advance_width;
}, "Maximum horizontal cursor advance for all glyphs.")
.def_property_readonly(
"max_advance_height", [](PyFT2Font *self) {
return self->get_face()->max_advance_height;
}, "Maximum vertical cursor advance for all glyphs.")
.def_property_readonly(
"underline_position", [](PyFT2Font *self) {
return self->get_face()->underline_position;
}, "Vertical position of the underline bar.")
.def_property_readonly(
"underline_thickness", [](PyFT2Font *self) {
return self->get_face()->underline_thickness;
}, "Thickness of the underline bar.")
.def_property_readonly(
"fname", &PyFT2Font_fname,
"The original filename for this object.")
.def_buffer([](PyFT2Font &self) -> py::buffer_info {
return self.get_image().request();
})
.def("_render_glyph",
[](PyFT2Font *self, FT_UInt idx, LoadFlags flags, FT_Render_Mode render_mode) {
auto face = self->get_face();
FT_CHECK(FT_Load_Glyph, face, idx, static_cast(flags));
FT_CHECK(FT_Render_Glyph, face->glyph, render_mode);
return PyPositionedBitmap{face->glyph};
})
;
m.attr("__freetype_version__") = version_string;
m.attr("__freetype_build_type__") = FREETYPE_BUILD_TYPE;
m.attr("__libraqm_version__") = raqm_version_string();
auto py_int = py::module_::import("builtins").attr("int");
m.attr("CharacterCodeType") = py_int;
m.attr("GlyphIndexType") = py_int;
}