FazBrowse GitHub Viewer | Trending |
URL:
| Home
Tools: [Download Repo ZIP]   [Original HTTPS Page]

GitHub Viewer

#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION #include #include #include #include #ifdef PYBIND11_HAS_SUBINTERPRETER_SUPPORT #include #endif #include "ft2font.h" #include "mplutils.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 )"""; 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 left}, top{bg->top}, owning{true} { FT_Bitmap_Init(&bitmap); FT_CHECK(FT_Bitmap_Convert, _ft2Library, &bg->bitmap, &bitmap, 1); } PyPositionedBitmap(PyPositionedBitmap& other) = delete; // Non-copyable. PyPositionedBitmap(PyPositionedBitmap&& other) : _ft2Library{other._ft2Library}, left{other.left}, top{other.top}, owning{true}, bitmap{other.bitmap} { other.owning = false; // Prevent double deletion. } ~PyPositionedBitmap() { if (owning) { FT_Bitmap_Done(_ft2Library, &bitmap); } } }; /********************************************************************** * Glyph * */ typedef struct { size_t glyphInd; long width; long height; long horiBearingX; long horiBearingY; long horiAdvance; long linearHoriAdvance; long vertBearingX; long vertBearingY; long vertAdvance; FT_BBox bbox; } PyGlyph; const char *PyGlyph__doc__ = R"""( Information about a single glyph. You cannot create instances of this object yourself, but must use `.FT2Font.load_char` or `.FT2Font.load_glyph` to generate one. This object may be used in a call to `.FT2Font.draw_glyph_to_bitmap`. For more information on the various metrics, see `the FreeType documentation `_. )"""; static PyGlyph * PyGlyph_from_FT2Font(const FT2Font *font) { if (font == nullptr || font->get_num_glyphs() == 0) { throw std::runtime_error("No glyphs have been loaded."); } const FT_Face &face = font->get_face(); const FT_Glyph &glyph = font->get_last_glyph(); PyGlyph *self = new PyGlyph(); self->glyphInd = font->get_last_glyph_index(); FT_Glyph_Get_CBox(glyph, ft_glyph_bbox_subpixels, &self->bbox); self->width = face->glyph->metrics.width; self->height = face->glyph->metrics.height; self->horiBearingX = face->glyph->metrics.horiBearingX; self->horiBearingY = face->glyph->metrics.horiBearingY; self->horiAdvance = face->glyph->metrics.horiAdvance; self->linearHoriAdvance = face->glyph->linearHoriAdvance; self->vertBearingX = face->glyph->metrics.vertBearingX; self->vertBearingY = face->glyph->metrics.vertBearingY; self->vertAdvance = face->glyph->metrics.vertAdvance; return self; } static py::tuple PyGlyph_get_bbox(PyGlyph *self) { return py::make_tuple(self->bbox.xMin, self->bbox.yMin, self->bbox.xMax, self->bbox.yMax); } /********************************************************************** * FT2Font * */ class PyFT2Font final : public FT2Font { public: using FT2Font::FT2Font; py::object py_file; py::buffer_info mem; // mmap of the font file, if it can be mapped FT_StreamRec stream; py::list fallbacks; bool from_path = false; ~PyFT2Font() { // Because destructors are called from subclass up to base class, we need to // explicitly close the font here. Otherwise, the instance attributes here will // be destroyed before the font itself, but those are referenced by FreeType. close(); } void ft_glyph_warn(FT_ULong charcode, std::set family_names) { std::set::iterator it = family_names.begin(); std::stringstream ss; ss count) { // A well-behaved read() never returns more than the requested // number of bytes. FreeType only ever sized `buffer` for `count` // bytes, so honoring an over-long read would overflow it. Rather // than silently truncate (which would feed FreeType corrupt data), // signal a failed read so that FT_Open_Face -- and in turn the // FT2Font constructor -- raises. n_read = 0; } memcpy(buffer, tmpbuf, n_read); } catch (py::error_already_set &eas) { eas.discard_as_unraisable(__func__); if (!count) { return 1; // Non-zero signals error, when count == 0. } } return (unsigned long)n_read; } static void close_file_callback(FT_Stream stream) { PyObject *type, *value, *traceback; PyErr_Fetch(&type, &value, &traceback); PyFT2Font *self = (PyFT2Font *)stream->descriptor.pointer; try { self->py_file.attr("close")(); } catch (py::error_already_set &eas) { eas.discard_as_unraisable(__func__); } PyErr_Restore(type, value, traceback); } const char *PyFT2Font_init__doc__ = R"""( Parameters ---------- filename : str, bytes, os.PathLike, or io.BinaryIO The source of the font data in a format (ttf or ttc) that FreeType can read. face_index : int, optional The index of the face in the font file to load. _fallback_list : list of FT2Font, optional A list of FT2Font objects used to find missing glyphs. .. warning:: This API is both private and provisional: do not use it directly. _warn_if_used : bool, optional Used to trigger missing glyph warnings. .. warning:: This API is private: do not use it directly. )"""; static PyFT2Font * PyFT2Font_init(FT_Library ft2Library, py::object filename, std::optional hinting_factor = std::nullopt, FT_Long face_index = 0, std::optional fallback_list = std::nullopt, std::optional kerning_factor = std::nullopt, bool warn_if_used = false) { if (hinting_factor) { auto api = py::module_::import("matplotlib._api"); auto warn = api.attr("warn_deprecated"); warn("since"_a="3.11", "name"_a="hinting_factor", "obj_type"_a="parameter"); } if (kerning_factor) { auto api = py::module_::import("matplotlib._api"); auto warn = api.attr("warn_deprecated"); warn("since"_a="3.11", "name"_a="_kerning_factor", "obj_type"_a="parameter"); } else { kerning_factor = 0; } if (face_index < 0 || face_index > 0xffff) { throw std::range_error("face_index must be between 0 and 65535, inclusive"); } std::vector fallback_fonts; if (fallback_list) { // go through fallbacks to add them to our lists std::copy(fallback_list->begin(), fallback_list->end(), std::back_inserter(fallback_fonts)); } auto self = new PyFT2Font(fallback_fonts, warn_if_used); self->set_kerning_factor(*kerning_factor); if (fallback_list) { // go through fallbacks to add them to our lists for (auto item : *fallback_list) { self->fallbacks.append(item); } } memset(&self->stream, 0, sizeof(FT_StreamRec)); FT_Open_Args open_args; memset((void *)&open_args, 0, sizeof(FT_Open_Args)); // Stream font data through the Python file object. `close` is set for a // file we opened, and nullptr for a caller-owned one. auto stream_font_via_python = [&](FT_Stream_CloseFunc close) { self->stream.size = 0x7fffffff; // Unknown size. self->stream.descriptor.pointer = self; self->stream.read = &read_from_file_callback; self->stream.close = close; open_args.flags = FT_OPEN_STREAM; open_args.stream = &self->stream; }; auto PathLike = py::module_::import("os").attr("PathLike"); if (py::isinstance(filename) || py::isinstance(filename) || py::isinstance(filename, PathLike)) { // Open with Python so path errors raise the usual exceptions. self->py_file = py::module_::import("io").attr("open")(filename, "rb"); self->from_path = true; // Try to mmap the file so that glyph loads skip the Python layer. py::object data; py::object mmap_module; try { mmap_module = py::module_::import("mmap"); } catch (py::error_already_set &eas) { if (!eas.matches(PyExc_ImportError)) { throw; } // Some platforms (e.g. WASI) don't provide the mmap module. } if (mmap_module) { try { data = mmap_module.attr("mmap")( self->py_file.attr("fileno")(), 0, "access"_a=mmap_module.attr("ACCESS_READ")); } catch (py::error_already_set &eas) { if (!eas.matches(PyExc_ValueError) && !eas.matches(PyExc_OSError)) { throw; } // Zero-length or otherwise unmappable file. } } if (data) { self->py_file.attr("close")(); self->mem = py::buffer(data).request(); open_args.flags = FT_OPEN_MEMORY; open_args.memory_base = static_cast(self->mem.ptr); open_args.memory_size = static_cast(self->mem.size); } else { // Fall back to streaming reads, closing the file we opened. stream_font_via_python(&close_file_callback); } } else { try { // This will catch various issues: // 1. `read` not being an attribute. // 2. `read` raising an error. // 3. `read` returning something other than `bytes`. auto data = filename.attr("read")(0).cast(); } catch (const std::exception&) { throw py::type_error( "First argument must be a path to a font file or a binary-mode file object"); } self->py_file = filename; stream_font_via_python(nullptr); // Don't close the caller's file object. } self->open(ft2Library, open_args, face_index); return self; } static py::object PyFT2Font_fname(PyFT2Font *self) { if (self->from_path) { return self->py_file.attr("name"); } else { // User passed a file-like object to the constructor. return self->py_file; } } const char *PyFT2Font_clear__doc__ = "Clear all the glyphs, reset for a new call to `.set_text`."; const char *PyFT2Font_set_size__doc__ = R"""( Set the size of the text. Parameters ---------- ptsize : float The size of the text in points. dpi : float The DPI used for rendering the text. )"""; const char *PyFT2Font__set_transform__doc__ = R"""( Set the transform of the text. This is a low-level function, where *matrix* and *delta* are directly in 16.16 and 26.6 formats respectively. Refer to the FreeType docs of FT_Set_Transform for further description. Note, every call to `.font_manager.get_font` will reset the transform to the default to ensure consistency across cache accesses. Parameters ---------- matrix : (2, 2) array of int delta : (2,) array of int )"""; const char *PyFT2Font_set_charmap__doc__ = R"""( Make the i-th charmap current. For more details on character mapping, see the `FreeType documentation `_. Parameters ---------- i : int The charmap number in the range [0, `.num_charmaps`). See Also -------- .num_charmaps .select_charmap .get_charmap )"""; const char *PyFT2Font_select_charmap__doc__ = R"""( Select a charmap by its FT_Encoding number. For more details on character mapping, see the `FreeType documentation `_. Parameters ---------- i : int The charmap in the form defined by FreeType: https://freetype.org/freetype2/docs/reference/ft2-character_mapping.html#ft_encoding See Also -------- .set_charmap .get_charmap )"""; const char *PyFT2Font_get_kerning__doc__ = R"""( Get the kerning between two glyphs. Parameters ---------- left, right : int The glyph indices. Note these are not characters nor character codes. Use `.get_char_index` to convert character codes to glyph indices. mode : Kerning A kerning mode constant: - ``DEFAULT`` - Return scaled and grid-fitted kerning distances. - ``UNFITTED`` - Return scaled but un-grid-fitted kerning distances. - ``UNSCALED`` - Return the kerning vector in original font units. .. versionchanged:: 3.10 This now takes a `.ft2font.Kerning` value instead of an `int`. Returns ------- int The kerning adjustment between the two glyphs. )"""; const char *PyFT2Font_set_text__doc__ = R"""( Set the text *string* and *angle*. You must call this before `.draw_glyphs_to_bitmap`. Parameters ---------- string : str The text to prepare rendering information for. angle : float The angle at which to render the supplied text. flags : LoadFlags, default: `.LoadFlags.FORCE_AUTOHINT` Any bitwise-OR combination of the `.LoadFlags` flags. .. versionchanged:: 3.10 This now takes an `.ft2font.LoadFlags` instead of an int. features : tuple[str, ...] 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 .. versionadded:: 3.11 Returns ------- np.ndarray[double] A sequence of x,y glyph positions in 26.6 subpixels; divide by 64 for pixels. )"""; static py::array_t PyFT2Font_set_text(PyFT2Font *self, std::u32string_view text, double angle = 0.0, LoadFlags flags = LoadFlags::FORCE_AUTOHINT, std::optional features = std::nullopt, std::variant languages_or_str = nullptr) { std::vector xys; FT2Font::LanguageType languages = std::visit(overloaded { [](FT2Font::LanguageType languages) { return languages; }, [&](std::string value) { return FT2Font::LanguageType{{ FT2Font::LanguageRange{value, 0, text.size()}}}; } }, languages_or_str); self->set_text(text, angle, static_cast(flags), features, languages, xys); py::ssize_t dims[] = { static_cast(xys.size()) / 2, 2 }; py::array_t result(dims); if (xys.size() > 0) { memcpy(result.mutable_data(), xys.data(), result.nbytes()); } return result; } const char *PyFT2Font_get_num_glyphs__doc__ = "Return the number of loaded glyphs."; const char *PyFT2Font_load_char__doc__ = R"""( Load character in current fontfile and set glyph. Parameters ---------- charcode : int The character code to prepare rendering information for. This code must be in the charmap, or else a ``.notdef`` glyph may be returned instead. flags : LoadFlags, default: `.LoadFlags.FORCE_AUTOHINT` Any bitwise-OR combination of the `.LoadFlags` flags. .. versionchanged:: 3.10 This now takes an `.ft2font.LoadFlags` instead of an int. Returns ------- Glyph The glyph information corresponding to the specified character. See Also -------- .load_glyph .select_charmap .set_charmap )"""; static PyGlyph * PyFT2Font_load_char(PyFT2Font *self, long charcode, LoadFlags flags = LoadFlags::FORCE_AUTOHINT) { bool fallback = true; FT2Font *ft_object = nullptr; self->load_char(charcode, static_cast(flags), ft_object, fallback); return PyGlyph_from_FT2Font(ft_object); } const char *PyFT2Font_load_glyph__doc__ = R"""( Load glyph index in current fontfile and set glyph. Note that the glyph index is specific to a font, and not universal like a Unicode code point. Parameters ---------- glyph_index : int The glyph index to prepare rendering information for. flags : LoadFlags, default: `.LoadFlags.FORCE_AUTOHINT` Any bitwise-OR combination of the `.LoadFlags` flags. .. versionchanged:: 3.10 This now takes an `.ft2font.LoadFlags` instead of an int. Returns ------- Glyph The glyph information corresponding to the specified index. See Also -------- .load_char )"""; static PyGlyph * PyFT2Font_load_glyph(PyFT2Font *self, FT_UInt glyph_index, LoadFlags flags = LoadFlags::FORCE_AUTOHINT) { self->load_glyph(glyph_index, static_cast(flags)); return PyGlyph_from_FT2Font(self); } const char *PyFT2Font_get_width_height__doc__ = R"""( Get the dimensions of the current string set by `.set_text`. The rotation of the string is accounted for. Returns ------- width, height : float The width and height in 26.6 subpixels of the current string. To get width and height in pixels, divide these values by 64. See Also -------- .get_bitmap_offset .get_descent )"""; const char *PyFT2Font_get_bitmap_offset__doc__ = R"""( Get the (x, y) offset for the bitmap if ink hangs left or below (0, 0). Since Matplotlib only supports left-to-right text, y is always 0. Returns ------- x, y : float The x and y offset in 26.6 subpixels of the bitmap. To get x and y in pixels, divide these values by 64. See Also -------- .get_width_height .get_descent )"""; const char *PyFT2Font_get_descent__doc__ = R"""( Get the descent of the current string set by `.set_text`. The rotation of the string is accounted for. Returns ------- int The descent in 26.6 subpixels of the bitmap. To get the descent in pixels, divide these values by 64. See Also -------- .get_bitmap_offset .get_width_height )"""; const char *PyFT2Font_draw_glyphs_to_bitmap__doc__ = R"""( Draw the glyphs that were loaded by `.set_text` to the bitmap. The bitmap size will be automatically set to include the glyphs. Parameters ---------- antialiased : bool, default: True Whether to render glyphs 8-bit antialiased or in pure black-and-white. See Also -------- .draw_glyph_to_bitmap )"""; const char *PyFT2Font_draw_glyph_to_bitmap__doc__ = R"""( Draw a single glyph to the bitmap at pixel locations x, y. Note it is your responsibility to create the image manually with the correct size before this call is made. If you want automatic layout, use `.set_text` in combinations with `.draw_glyphs_to_bitmap`. This function is instead intended for people who want to render individual glyphs (e.g., returned by `.load_char`) at precise locations. Parameters ---------- image : 2d array of uint8 The image buffer on which to draw the glyph. x, y : int The position of the glyph's top left corner. glyph : Glyph The glyph to draw. antialiased : bool, default: True Whether to render glyphs 8-bit antialiased or in pure black-and-white. See Also -------- .draw_glyphs_to_bitmap )"""; static void PyFT2Font_draw_glyph_to_bitmap(PyFT2Font *self, py::buffer &image, int xd, int yd, PyGlyph *glyph, bool antialiased = true) { self->draw_glyph_to_bitmap( py::array_t{image}, xd, yd, glyph->glyphInd, antialiased); } const char *PyFT2Font_get_glyph_name__doc__ = R"""( Retrieve the ASCII name of a given glyph *index* in a face. Due to Matplotlib's internal design, for fonts that do not contain glyph names (per ``FT_FACE_FLAG_GLYPH_NAMES``), this returns a made-up name which does *not* roundtrip through `.get_name_index`. Parameters ---------- index : int The glyph number to query. Returns ------- str The name of the glyph, or if the font does not contain names, a name synthesized by Matplotlib. See Also -------- .get_name_index )"""; const char *PyFT2Font_get_charmap__doc__ = R"""( Return a mapping of character codes to glyph indices in the font. The charmap is Unicode by default, but may be changed by `.set_charmap` or `.select_charmap`. Returns ------- dict[int, int] A dictionary of the selected charmap mapping character codes to their corresponding glyph indices. )"""; static py::dict PyFT2Font_get_charmap(PyFT2Font *self) { py::dict charmap; FT_UInt index; FT_ULong code = FT_Get_First_Char(self->get_face(), &index); while (index != 0) { charmap[py::cast(code)] = py::cast(index); code = FT_Get_Next_Char(self->get_face(), code, &index); } return charmap; } const char *PyFT2Font_get_char_index__doc__ = R"""( Return the glyph index corresponding to a character code point. Parameters ---------- codepoint : int A character code point in the current charmap (which defaults to Unicode.) _fallback : bool Whether to enable fallback fonts while searching for a character. Returns ------- int The corresponding glyph index. See Also -------- .set_charmap .select_charmap .get_glyph_name .get_name_index )"""; const char *PyFT2Font_get_sfnt__doc__ = R"""( Load the entire SFNT names table. Returns ------- dict[tuple[int, int, int, int], bytes] The SFNT names table; the dictionary keys are tuples of: (platform-ID, ISO-encoding-scheme, language-code, description) and the values are the direct information from the font table. )"""; static py::dict PyFT2Font_get_sfnt(PyFT2Font *self) { if (!(self->get_face()->face_flags & FT_FACE_FLAG_SFNT)) { throw py::value_error("No SFNT name table"); } size_t count = FT_Get_Sfnt_Name_Count(self->get_face()); py::dict names; for (FT_UInt j = 0; j < count; ++j) { FT_SfntName sfnt; FT_Error error = FT_Get_Sfnt_Name(self->get_face(), j, &sfnt); if (error) { throw py::value_error("Could not get SFNT name"); } auto key = py::make_tuple( sfnt.platform_id, sfnt.encoding_id, sfnt.language_id, sfnt.name_id); auto val = py::bytes(reinterpret_cast(sfnt.string), sfnt.string_len); names[key] = val; } return names; } const char *PyFT2Font_get_name_index__doc__ = R"""( Return the glyph index of a given glyph *name*. Parameters ---------- name : str The name of the glyph to query. Returns ------- int The corresponding glyph index; 0 means 'undefined character code'. See Also -------- .get_char_index .get_glyph_name )"""; const char *PyFT2Font_get_ps_font_info__doc__ = R"""( Return the information in the PS Font Info structure. For more information, see the `FreeType documentation on this structure `_. Returns ------- version : str notice : str full_name : str family_name : str weight : str italic_angle : int is_fixed_pitch : bool underline_position : int underline_thickness : int )"""; static py::tuple PyFT2Font_get_ps_font_info(PyFT2Font *self) { PS_FontInfoRec fontinfo; FT_Error error = FT_Get_PS_Font_Info(self->get_face(), &fontinfo); if (error) { throw py::value_error("Could not get PS font info"); } return py::make_tuple( fontinfo.version ? fontinfo.version : "", fontinfo.notice ? fontinfo.notice : "", fontinfo.full_name ? fontinfo.full_name : "", fontinfo.family_name ? fontinfo.family_name : "", fontinfo.weight ? fontinfo.weight : "", fontinfo.italic_angle, fontinfo.is_fixed_pitch, fontinfo.underline_position, fontinfo.underline_thickness); } const char *PyFT2Font_get_sfnt_table__doc__ = R"""( Return one of the SFNT tables. Parameters ---------- name : {"head", "maxp", "OS/2", "hhea", "vhea", "post", "pclt"} Which table to return. Returns ------- dict[str, Any] The corresponding table; for more information, see `the FreeType documentation `_. )"""; static std::optional PyFT2Font_get_sfnt_table(PyFT2Font *self, std::string tagname) { FT_Sfnt_Tag tag; const std::unordered_map names = { {"head", FT_SFNT_HEAD}, {"maxp", FT_SFNT_MAXP}, {"OS/2", FT_SFNT_OS2}, {"hhea", FT_SFNT_HHEA}, {"vhea", FT_SFNT_VHEA}, {"post", FT_SFNT_POST}, {"pclt", FT_SFNT_PCLT}, }; try { tag = names.at(tagname); } catch (const std::out_of_range&) { return std::nullopt; } void *table = FT_Get_Sfnt_Table(self->get_face(), tag); if (!table) { return std::nullopt; } switch (tag) { case FT_SFNT_HEAD: { auto t = static_cast(table); return py::dict( "version"_a=py::make_tuple(FIXED_MAJOR(t->Table_Version), FIXED_MINOR(t->Table_Version)), "fontRevision"_a=py::make_tuple(FIXED_MAJOR(t->Font_Revision), FIXED_MINOR(t->Font_Revision)), "checkSumAdjustment"_a=t->CheckSum_Adjust, "magicNumber"_a=t->Magic_Number, "flags"_a=t->Flags, "unitsPerEm"_a=t->Units_Per_EM, // FreeType 2.6.1 defines these two timestamps as FT_Long, but they should // be unsigned (fixed in 2.10.0): // https://gitlab.freedesktop.org/freetype/freetype/-/commit/3e8ec291ffcfa03c8ecba1cdbfaa55f5577f5612 // It's actually read from the file structure as two 32-bit values, so we // need to cast down in size to prevent sign extension from producing huge // 64-bit values. "created"_a=py::make_tuple(static_cast(t->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 = std::visit(overloaded { [](FT2Font::LanguageType languages) { return languages; }, [&](std::string value) { return FT2Font::LanguageType{{ FT2Font::LanguageRange{value, 0, text.size()}}}; } }, languages_or_str); 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; } #ifdef PYBIND11_HAS_SUBINTERPRETER_SUPPORT PYBIND11_MODULE(ft2font, m, py::mod_gil_not_used(), py::multiple_interpreters::per_interpreter_gil()) #else PYBIND11_MODULE(ft2font, m, py::mod_gil_not_used()) #endif { FT_Library ft2Library = nullptr; 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); py::native_enum(m, "Kerning", "enum.Enum", Kerning__doc__) .value("DEFAULT", FT_KERNING_DEFAULT) .value("UNFITTED", FT_KERNING_UNFITTED) .value("UNSCALED", FT_KERNING_UNSCALED) .finalize(); py::native_enum(m, "LoadFlags", "enum.Flag", LoadFlags__doc__) .value("DEFAULT", LoadFlags::DEFAULT) .value("NO_SCALE", LoadFlags::NO_SCALE) .value("NO_HINTING", LoadFlags::NO_HINTING) .value("RENDER", LoadFlags::RENDER) .value("NO_BITMAP", LoadFlags::NO_BITMAP) .value("VERTICAL_LAYOUT", LoadFlags::VERTICAL_LAYOUT) .value("FORCE_AUTOHINT", LoadFlags::FORCE_AUTOHINT) .value("CROP_BITMAP", LoadFlags::CROP_BITMAP) .value("PEDANTIC", LoadFlags::PEDANTIC) .value("IGNORE_GLOBAL_ADVANCE_WIDTH", LoadFlags::IGNORE_GLOBAL_ADVANCE_WIDTH) .value("NO_RECURSE", LoadFlags::NO_RECURSE) .value("IGNORE_TRANSFORM", LoadFlags::IGNORE_TRANSFORM) .value("MONOCHROME", LoadFlags::MONOCHROME) .value("LINEAR_DESIGN", LoadFlags::LINEAR_DESIGN) .value("NO_AUTOHINT", LoadFlags::NO_AUTOHINT) .value("COLOR", LoadFlags::COLOR) .value("COMPUTE_METRICS", LoadFlags::COMPUTE_METRICS) .value("BITMAP_METRICS_ONLY", LoadFlags::BITMAP_METRICS_ONLY) .value("NO_SVG", LoadFlags::NO_SVG) // These must be unique, but the others can be OR'd together; I don't know if // there's any way to really enforce that. .value("TARGET_NORMAL", LoadFlags::TARGET_NORMAL) .value("TARGET_LIGHT", LoadFlags::TARGET_LIGHT) .value("TARGET_MONO", LoadFlags::TARGET_MONO) .value("TARGET_LCD", LoadFlags::TARGET_LCD) .value("TARGET_LCD_V", LoadFlags::TARGET_LCD_V) .finalize(); py::native_enum(m, "FaceFlags", "enum.Flag", FaceFlags__doc__) .value("SCALABLE", FaceFlags::SCALABLE) .value("FIXED_SIZES", FaceFlags::FIXED_SIZES) .value("FIXED_WIDTH", FaceFlags::FIXED_WIDTH) .value("SFNT", FaceFlags::SFNT) .value("HORIZONTAL", FaceFlags::HORIZONTAL) .value("VERTICAL", FaceFlags::VERTICAL) .value("KERNING", FaceFlags::KERNING) .value("FAST_GLYPHS", FaceFlags::FAST_GLYPHS) .value("MULTIPLE_MASTERS", FaceFlags::MULTIPLE_MASTERS) .value("GLYPH_NAMES", FaceFlags::GLYPH_NAMES) .value("EXTERNAL_STREAM", FaceFlags::EXTERNAL_STREAM) .value("HINTER", FaceFlags::HINTER) .value("CID_KEYED", FaceFlags::CID_KEYED) .value("TRICKY", FaceFlags::TRICKY) .value("COLOR", FaceFlags::COLOR) .value("VARIATION", FaceFlags::VARIATION) .value("SVG", FaceFlags::SVG) .value("SBIX", FaceFlags::SBIX) .value("SBIX_OVERLAY", FaceFlags::SBIX_OVERLAY) .finalize(); py::native_enum(m, "RenderMode", "enum.Enum", RenderMode__doc__) .value("NORMAL", FT_RENDER_MODE_NORMAL) .value("LIGHT", FT_RENDER_MODE_LIGHT) .value("MONO", FT_RENDER_MODE_MONO) .value("LCD", FT_RENDER_MODE_LCD) .value("LCD_V", FT_RENDER_MODE_LCD_V) .value("SDF", FT_RENDER_MODE_SDF) .finalize(); py::native_enum(m, "StyleFlags", "enum.Flag", StyleFlags__doc__) .value("NORMAL", StyleFlags::NORMAL) .value("ITALIC", StyleFlags::ITALIC) .value("BOLD", StyleFlags::BOLD) .finalize(); py::classh(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::classh(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::classh(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::classh(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); }); py::classh(m, "FT2Font", py::is_final(), py::buffer_protocol(), PyFT2Font__doc__) .def(py::init( [ft2Library]( py::object filename, std::optional hinting_factor = std::nullopt, FT_Long face_index = 0, std::optional fallback_list = std::nullopt, std::optional kerning_factor = std::nullopt, bool warn_if_used = false) -> PyFT2Font * { return PyFT2Font_init(ft2Library, filename, hinting_factor, face_index, fallback_list, kerning_factor, warn_if_used); }), "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__) .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__) .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", [ft2Library](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{ft2Library, face->glyph}; }) ; // Ensure FreeType library is closed after all instances of FT2Font are gone by // tying a weak ref to the class itself. // https://pybind11.readthedocs.io/en/stable/advanced/misc.html#module-destructors (void)py::weakref( m.attr("FT2Font"), py::cpp_function( [ft2Library](py::handle weakref) { FT_Done_FreeType(ft2Library); weakref.dec_ref(); } ) ).release(); 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; }

Back | FazBrowse Home | New Git URL