Esempio n. 1
0
/*********************************************************
 *
 * New rendering algorithm (full thickness underlines)
 *
 *********************************************************/
static void
render(FreeTypeInstance *ft, Layout *text, const FontRenderMode *mode,
       const FontColor *fg_color, FontSurface *surface,
       unsigned width, unsigned height, FT_Vector *offset,
       FT_Pos underline_top, FT_Fixed underline_size)
{
    FT_Pos top;
    FT_Pos left;
    int x;
    int y;
    int n;
    int length = text->length;
    GlyphSlot *slots = text->glyphs;
    FT_BitmapGlyph image;
    FontRenderPtr render_gray = surface->render_gray;
    FontRenderPtr render_mono = surface->render_mono;
    int is_underline_gray = 0;

    if (length <= 0) {
        return;
    }
    left = offset->x;
    top = offset->y;
    for (n = 0; n < length; ++n) {
        image = slots[n].glyph->image;
        x = FX6_TRUNC(FX6_CEIL(left + slots[n].posn.x));
        y = FX6_TRUNC(FX6_CEIL(top + slots[n].posn.y));
        if (image->bitmap.pixel_mode == FT_PIXEL_MODE_GRAY) {
            render_gray(x, y, surface, &(image->bitmap), fg_color);
            is_underline_gray = 1;
        }
        else {
            render_mono(x, y, surface, &(image->bitmap), fg_color);
        }
    }

    if (underline_size > 0) {
        if (is_underline_gray) {
            surface->fill(left + text->min_x, top + underline_top,
                          INT_TO_FX6(width), underline_size,
                          surface, fg_color);
        }
        else {
            surface->fill(FX6_CEIL(left + text->min_x),
                          FX6_CEIL(top + underline_top),
                          INT_TO_FX6(width), FX6_CEIL(underline_size),
                          surface, fg_color);
        }
    }
}
Esempio n. 2
0
void
_PGFT_GetRenderMetrics(const FontRenderMode *mode, Layout *text,
                       unsigned *w, unsigned *h, FT_Vector *offset,
                       FT_Pos *underline_top, FT_Fixed *underline_size)
{
    FT_Pos min_x = text->min_x;
    FT_Pos max_x = text->max_x;
    FT_Pos min_y = text->min_y;
    FT_Pos max_y = text->max_y;

    *underline_top = 0;
    *underline_size = 0;
    if (mode->style & FT_STYLE_UNDERLINE) {
        FT_Fixed half_size = (text->underline_size + 1) / 2;
        FT_Fixed adjusted_pos;
        FT_Fixed uline_top;
        FT_Fixed uline_bottom;

        if (mode->underline_adjustment < 0) {
            adjusted_pos = FT_MulFix(text->ascender,
                                     mode->underline_adjustment);
        }
        else {
            adjusted_pos = FT_MulFix(text->underline_pos,
                                     mode->underline_adjustment);
        }
        uline_top = adjusted_pos - half_size;
        uline_bottom = uline_top + text->underline_size;
        if (uline_bottom > max_y) {
            max_y = uline_bottom;
        }
        if (uline_top < min_y) {
            min_y = uline_top;
        }
        *underline_size = text->underline_size;
        *underline_top = uline_top;
    }

    offset->x = -min_x;
    offset->y = -min_y;
    *w = (unsigned)FX6_TRUNC(FX6_CEIL(max_x) - FX6_FLOOR(min_x));
    *h = (unsigned)FX6_TRUNC(FX6_CEIL(max_y) - FX6_FLOOR(min_y));
}
Esempio n. 3
0
long
_PGFT_Face_GetDescenderSized(FreeTypeInstance *ft, PgFaceObject *faceobj,
			    FT_UInt16 ptsize)
{
    FT_Face face = _PGFT_GetFaceSized(ft, faceobj, ptsize);

    if (!face) {
        RAISE(PyExc_RuntimeError, _PGFT_GetError(ft));
        return 0;
    }
    return (long)FX6_TRUNC(FX6_FLOOR(face->size->metrics.descender));
}
Esempio n. 4
0
long
_PGFT_Font_GetDescenderSized(FreeTypeInstance *ft, PgFontObject *fontobj,
                             FT_UInt16 ptsize)
{
    FT_Face font = _PGFT_GetFontSized(ft, fontobj, ptsize);

    if (!font) {
        RAISE(PyExc_RuntimeError, _PGFT_GetError(ft));
        return 0;
    }
    return (long)FX6_TRUNC(FX6_FLOOR(font->size->metrics.descender));
}
Esempio n. 5
0
int
_PGFT_GetTextRect(FreeTypeInstance *ft, PgFaceObject *faceobj,
                  const FaceRenderMode *mode, PGFT_String *text, SDL_Rect *r)
{
    FaceText *face_text;
    unsigned width;
    unsigned height;
    FT_Vector offset;
    FT_Pos underline_size;
    FT_Pos underline_top;

    face_text = _PGFT_LoadFaceText(ft, faceobj, mode, text);
    if (!face_text) {
        return -1;
    }
    _PGFT_GetRenderMetrics(mode, face_text, &width, &height, &offset,
                           &underline_size, &underline_top);
    r->x = -(Sint16)FX6_TRUNC(FX6_FLOOR(offset.x));
    r->y = (Sint16)FX6_TRUNC(FX6_CEIL(offset.y));
    r->w = (Uint16)width;
    r->h = (Uint16)height;
    return 0;
}
Esempio n. 6
0
long
_PGFT_Face_GetGlyphHeightSized(FreeTypeInstance *ft, PgFaceObject *faceobj,
			      FT_UInt16 ptsize)
{
    /*
     * Based on the SDL_ttf height calculation.
     */
    FT_Face face = _PGFT_GetFaceSized(ft, faceobj, ptsize);
    FT_Size_Metrics *metrics;

    if (!face) {
        RAISE(PyExc_RuntimeError, _PGFT_GetError(ft));
        return 0;
    }
    metrics = &face->size->metrics;
    return (long)FX6_TRUNC(FX6_CEIL(metrics->ascender) -
                           FX6_FLOOR(metrics->descender)) + /* baseline */ 1;
}
Esempio n. 7
0
int
_PGFT_Render_Array(FreeTypeInstance *ft, PgFontObject *fontobj,
                   const FontRenderMode *mode, PyObject *arrayobj,
                   PGFT_String *text, int invert,
                   int x, int y, SDL_Rect *r)
{
    static int view_init = 0;

    Pg_buffer pg_view;
    Py_buffer *view_p = (Py_buffer *)&pg_view;

    unsigned width;
    unsigned height;
    int itemsize;
    FT_Vector offset;
    FT_Vector array_offset;
    FT_Pos underline_top;
    FT_Fixed underline_size;

    FontSurface font_surf;
    SDL_PixelFormat format;
    Layout *font_text;

    /* Get target buffer */
    if (!view_init) {
        import_pygame_base();
        if (PyErr_Occurred()) {
            return -1;
        }
    }
    if (PgObject_GetBuffer(arrayobj, &pg_view, PyBUF_RECORDS)) {
        return -1;
    }
    if (view_p->ndim != 2) {
        PyErr_Format(PyExc_ValueError,
                     "expecting a 2d target array: got %id array instead",
                     (int)view_p->ndim);
        PgBuffer_Release(&pg_view);
        return -1;
    }
    if (_validate_view_format(view_p->format)) {
        PgBuffer_Release(&pg_view);
        return -1;
    }

    width = (unsigned)view_p->shape[0];
    height = (unsigned)view_p->shape[1];
    itemsize = (unsigned)view_p->itemsize;

    /* build font text */
    font_text = _PGFT_LoadLayout(ft, fontobj, mode, text);
    if (!font_text) {
        PgBuffer_Release(&pg_view);
        return -1;
    }

    /* if empty string, then nothing more to do */
    if (font_text->length == 0) {
        PgBuffer_Release(&pg_view);
        r->x = 0;
        r->y = 0;
        r->w = 0;
        r->h = _PGFT_Font_GetHeightSized(ft, fontobj, mode->face_size);
        return 0;
    }

    _PGFT_GetRenderMetrics(mode, font_text, &width, &height, &offset,
                           &underline_top, &underline_size);
    if (width == 0 || height == 0) {
        /* Nothing more to do. */
        PgBuffer_Release(&pg_view);
        r->x = 0;
        r->y = 0;
        r->w = 0;
        r->h = _PGFT_Font_GetHeightSized(ft, fontobj, mode->face_size);
        return 0;
    }
    array_offset.x = INT_TO_FX6(x);
    array_offset.y = INT_TO_FX6(y);
    if (mode->render_flags & FT_RFLAG_ORIGIN) {
        x -= FX6_TRUNC(FX6_CEIL(offset.x));
        y -= FX6_TRUNC(FX6_CEIL(offset.y));
    }
    else {
        array_offset.x += offset.x;
        array_offset.y += offset.y;
    }

    /*
     * Setup target surface struct
     */
    format.BytesPerPixel = itemsize;
#if SDL_BYTEORDER == SDL_LIL_ENDIAN
    format.Ashift = _is_swapped(view_p) ? (itemsize - 1) * 8 : 0;
#else
    format.Ashift = _is_swapped(view_p) ? 0 : (itemsize - 1) * 8;
#endif
    font_surf.buffer = view_p->buf;
    font_surf.width = (unsigned)view_p->shape[0];
    font_surf.height = (unsigned)view_p->shape[1];
    font_surf.item_stride = (unsigned)view_p->strides[0];
    font_surf.pitch = (unsigned)view_p->strides[1];
    font_surf.format = &format;
    font_surf.render_gray = __render_glyph_INT;
    font_surf.render_mono = __render_glyph_MONO_as_INT;
    font_surf.fill = __fill_glyph_INT;

    render(ft, font_text, mode, invert ? &mono_transparent : &mono_opaque,
           &font_surf, width, height, &array_offset, underline_top,
           underline_size);

    PgBuffer_Release(&pg_view);
    r->x = -(Sint16)FX6_TRUNC(FX6_FLOOR(offset.x));
    r->y = (Sint16)FX6_TRUNC(FX6_CEIL(offset.y));
    r->w = (Uint16)width;
    r->h = (Uint16)height;

    return 0;
}
Esempio n. 8
0
SDL_Surface *_PGFT_Render_NewSurface(FreeTypeInstance *ft,
                                     PgFontObject *fontobj,
                                     const FontRenderMode *mode,
                                     PGFT_String *text,
                                     FontColor *fgcolor, FontColor *bgcolor,
                                     SDL_Rect *r)
{
#if SDL_BYTEORDER == SDL_BIG_ENDIAN
    FT_UInt32 rmask = 0xff000000;
    FT_UInt32 gmask = 0x00ff0000;
    FT_UInt32 bmask = 0x0000ff00;
    FT_UInt32 amask = 0x000000ff;
#else
    FT_UInt32 rmask = 0x000000ff;
    FT_UInt32 gmask = 0x0000ff00;
    FT_UInt32 bmask = 0x00ff0000;
    FT_UInt32 amask = 0xff000000;
#endif
    int locked = 0;
    FT_UInt32 fillcolor;
    SDL_Surface *surface = 0;
    int bits_per_pixel =
        (bgcolor || mode->render_flags & FT_RFLAG_ANTIALIAS) ? 32 : 8;
    FT_UInt32 surface_flags = SDL_SWSURFACE;

    FontSurface font_surf;
    Layout *font_text;
    unsigned width;
    unsigned height;
    FT_Vector offset;
    FT_Pos underline_top;
    FT_Fixed underline_size;
    FontColor mono_fgcolor = {0, 0, 0, 1};
    FontColor mono_bgcolor = {0, 0, 0, 0};

    /* build font text */
    font_text = _PGFT_LoadLayout(ft, fontobj, mode, text);
    if (!font_text) {
        return 0;
    }

    if (font_text->length > 0) {
        _PGFT_GetRenderMetrics(mode, font_text, &width, &height, &offset,
                               &underline_top, &underline_size);
    }
    else {
        width = 1;
        height = _PGFT_Font_GetHeightSized(ft, fontobj, mode->face_size);
        offset.x = -font_text->min_x;
        offset.y = -font_text->min_y;
    }

    surface = SDL_CreateRGBSurface(surface_flags, width, height,
                   bits_per_pixel, rmask, gmask, bmask,
                   bits_per_pixel == 32 ? amask : 0);
    if (!surface) {
        PyErr_SetString(PyExc_SDLError, SDL_GetError());
        return 0;
    }

    if (SDL_MUSTLOCK(surface)) {
        if (SDL_LockSurface(surface) == -1) {
            PyErr_SetString(PyExc_SDLError, SDL_GetError());
            SDL_FreeSurface(surface);
            return 0;
        }
        locked = 1;
    }

    font_surf.buffer = surface->pixels;
    font_surf.width = surface->w;
    font_surf.height = surface->h;
    font_surf.pitch = surface->pitch;
    font_surf.format = surface->format;
    if (bits_per_pixel == 32) {
        font_surf.render_gray = __render_glyph_RGB4;
        font_surf.render_mono = __render_glyph_MONO4;
        font_surf.fill = __fill_glyph_RGB4;
        /*
         * Fill our texture with the required bg color
         */
        if (bgcolor) {
            fillcolor = SDL_MapRGBA(
                surface->format,
                bgcolor->r, bgcolor->g, bgcolor->b, bgcolor->a);
        }
        else {
            fillcolor = SDL_MapRGBA(surface->format,
                                    0, 0, 0, SDL_ALPHA_TRANSPARENT);
        }
        SDL_FillRect(surface, 0, fillcolor);
    }
    else {
        SDL_Color colors[2];

        colors[1].r = fgcolor->r;  /* Foreground */
        colors[1].g = fgcolor->g;
        colors[1].b = fgcolor->b;
        colors[0].r = ~colors[1].r;  /* Background */
        colors[0].g = ~colors[1].g;
        colors[0].b = ~colors[1].b;
        if (!SDL_SetColors(surface, colors, 0, 2)) {
            PyErr_SetString(PyExc_SystemError,
                            "Pygame bug in _PGFT_Render_NewSurface: "
                            "SDL_SetColors failed");
            SDL_FreeSurface(surface);
            return 0;
        }
        SDL_SetColorKey(surface, SDL_SRCCOLORKEY, (FT_UInt32)0);
        if (fgcolor->a != SDL_ALPHA_OPAQUE) {
            SDL_SetAlpha(surface, SDL_SRCALPHA, fgcolor->a);
        }
        fgcolor = &mono_fgcolor;
        bgcolor = &mono_bgcolor;
        font_surf.render_gray = __render_glyph_GRAY_as_MONO1;
        font_surf.render_mono = __render_glyph_MONO_as_GRAY1;
        font_surf.fill = __fill_glyph_GRAY1;
        /*
         * Fill our texture with the required bg color
         */
        SDL_FillRect(surface, 0, 0);
    }

    /*
     * Render the text!
     */
    render(ft, font_text, mode, fgcolor, &font_surf,
           width, height, &offset, underline_top, underline_size);

    r->x = -(Sint16)FX6_TRUNC(FX6_FLOOR(offset.x));
    r->y = (Sint16)FX6_TRUNC(FX6_CEIL(offset.y));
    r->w = (Uint16)width;
    r->h = (Uint16)height;

    if (locked) {
        SDL_UnlockSurface(surface);
    }

    return surface;
}
Esempio n. 9
0
int
_PGFT_Render_ExistingSurface(FreeTypeInstance *ft, PgFontObject *fontobj,
                             const FontRenderMode *mode, PGFT_String *text,
                             SDL_Surface *surface, int x, int y,
                             FontColor *fgcolor, FontColor *bgcolor,
                             SDL_Rect *r)
{
    static const FontRenderPtr __SDLrenderFuncs[] = {
        0,
        __render_glyph_RGB1,
        __render_glyph_RGB2,
        __render_glyph_RGB3,
        __render_glyph_RGB4
    };

    static const FontRenderPtr __MONOrenderFuncs[] = {
        0,
        __render_glyph_MONO1,
        __render_glyph_MONO2,
        __render_glyph_MONO3,
        __render_glyph_MONO4
    };

    static const FontFillPtr __RGBfillFuncs[] = {
        0,
        __fill_glyph_RGB1,
        __fill_glyph_RGB2,
        __fill_glyph_RGB3,
        __fill_glyph_RGB4
    };

    int locked = 0;
    unsigned width;
    unsigned height;
    FT_Vector offset;
    FT_Vector surf_offset;
    FT_Pos underline_top;
    FT_Fixed underline_size;

    FontSurface font_surf;
    Layout *font_text;

    if (SDL_MUSTLOCK(surface)) {
        if (SDL_LockSurface(surface) == -1) {
            SDL_FreeSurface(surface);
            PyErr_SetString(PyExc_SDLError, SDL_GetError());
            return -1;
        }
        locked = 1;
    }

    /* build font text */
    font_text = _PGFT_LoadLayout(ft, fontobj, mode, text);
    if (!font_text) {
        if (locked) {
            SDL_UnlockSurface(surface);
        }
        return -1;
    }
    if (font_text->length == 0) {
        /* Nothing to rendering */
        r->x = 0;
        r->y = 0;
        r->w = 0;
        r->h = _PGFT_Font_GetHeightSized(ft, fontobj, mode->face_size);
        return 0;
    }

    _PGFT_GetRenderMetrics(mode, font_text, &width, &height, &offset,
                           &underline_top, &underline_size);
    if (width == 0 || height == 0) {
        /* Nothing more to do. */
        if (locked) {
            SDL_UnlockSurface(surface);
        }
        r->x = 0;
        r->y = 0;
        r->w = 0;
        r->h = _PGFT_Font_GetHeightSized(ft, fontobj, mode->face_size);
        return 0;
    }
    surf_offset.x = INT_TO_FX6(x);
    surf_offset.y = INT_TO_FX6(y);
    if (mode->render_flags & FT_RFLAG_ORIGIN) {
        x -= FX6_TRUNC(FX6_CEIL(offset.x));
        y -= FX6_TRUNC(FX6_CEIL(offset.y));
    }
    else {
        surf_offset.x += offset.x;
        surf_offset.y += offset.y;
    }

    /*
     * Setup target surface struct
     */
    font_surf.buffer = surface->pixels;
    font_surf.width = surface->w;
    font_surf.height = surface->h;
    font_surf.pitch = surface->pitch;
    font_surf.format = surface->format;
    font_surf.render_gray = __SDLrenderFuncs[surface->format->BytesPerPixel];
    font_surf.render_mono = __MONOrenderFuncs[surface->format->BytesPerPixel];
    font_surf.fill = __RGBfillFuncs[surface->format->BytesPerPixel];

    /*
     * if bg color exists, paint background
     */
    if (bgcolor) {
        if (bgcolor->a == SDL_ALPHA_OPAQUE) {
            SDL_Rect    bg_fill;
            FT_UInt32   fillcolor;

            fillcolor = SDL_MapRGBA(surface->format,
                    bgcolor->r, bgcolor->g, bgcolor->b, bgcolor->a);

            bg_fill.x = (FT_Int16)x;
            bg_fill.y = (FT_Int16)y;
            bg_fill.w = (FT_UInt16)width;
            bg_fill.h = (FT_UInt16)height;

            SDL_FillRect(surface, &bg_fill, fillcolor);
        }
        else {
            font_surf.fill(INT_TO_FX6(x), INT_TO_FX6(y),
                           INT_TO_FX6(width), INT_TO_FX6(height),
                           &font_surf, bgcolor);
        }
    }

    /*
     * Render!
     */
    render(ft, font_text, mode, fgcolor, &font_surf,
           width, height, &surf_offset, underline_top, underline_size);

    r->x = -(Sint16)FX6_TRUNC(FX6_FLOOR(offset.x));
    r->y = (Sint16)FX6_TRUNC(FX6_CEIL(offset.y));
    r->w = (Uint16)width;
    r->h = (Uint16)height;

    if (locked) {
        SDL_UnlockSurface(surface);
    }

    return 0;
}
Esempio n. 10
0
FontText *
_PGFT_LoadFontText(FreeTypeInstance *ft, PgFontObject *fontobj,
                   const FontRenderMode *mode, PGFT_String *text)
{
    Py_ssize_t  string_length = PGFT_String_GET_LENGTH(text);

    PGFT_char * buffer = PGFT_String_GET_DATA(text);
    PGFT_char * buffer_end;
    PGFT_char * ch;

    FontText    *ftext = &(PGFT_INTERNALS(fontobj)->active_text);
    FontGlyph   *glyph = 0;
    FontGlyph   **glyph_array = 0;
    FontMetrics *metrics;
    FT_BitmapGlyph image;
    TextContext context;

    FT_Face     font;
    FT_Size_Metrics *sz_metrics;

    FT_Vector   pen = {0, 0};                /* untransformed origin  */
    FT_Vector   pen1 = {0, 0};
    FT_Vector   pen2;

    FT_Vector   *next_pos;

    int         vertical = mode->render_flags & FT_RFLAG_VERTICAL;
    int         use_kerning = mode->render_flags & FT_RFLAG_KERNING;
    int         pad = mode->render_flags & FT_RFLAG_PAD;
    FT_UInt     prev_glyph_index = 0;

    /* All these are 16.16 precision */
    FT_Angle    rotation_angle = mode->rotation_angle;

    /* All these are 26.6 precision */
    FT_Vector   kerning;
    FT_Pos      min_x = FX6_MAX;
    FT_Pos      max_x = FX6_MIN;
    FT_Pos      min_y = FX6_MAX;
    FT_Pos      max_y = FX6_MIN;
    FT_Pos      glyph_width;
    FT_Pos      glyph_height;
    FT_Pos      top = FX6_MIN;
    FT_Fixed    y_scale;

    FT_Error    error = 0;

    /* load our sized font */
    font = _PGFT_GetFontSized(ft, fontobj, mode->pt_size);
    if (!font) {
        PyErr_SetString(PyExc_SDLError, _PGFT_GetError(ft));
        return 0;
    }
    sz_metrics = &font->size->metrics;
    y_scale = sz_metrics->y_scale;

    /* cleanup the cache */
    _PGFT_Cache_Cleanup(&ftext->glyph_cache);

    /* create the text struct */
    if (string_length > ftext->buffer_size) {
        _PGFT_free(ftext->glyphs);
        ftext->glyphs = (FontGlyph **)
                        _PGFT_malloc((size_t)string_length * sizeof(FontGlyph *));
        if (!ftext->glyphs) {
            PyErr_NoMemory();
            return 0;
        }

        _PGFT_free(ftext->posns);
        ftext->posns = (FT_Vector *)
                       _PGFT_malloc((size_t)string_length * sizeof(FT_Vector));
        if (!ftext->posns) {
            PyErr_NoMemory();
            return 0;
        }
        ftext->buffer_size = string_length;
    }
    ftext->length = string_length;
    ftext->ascender = sz_metrics->ascender;
    ftext->underline_pos = -FT_MulFix(font->underline_position, y_scale);
    ftext->underline_size = FT_MulFix(font->underline_thickness, y_scale);
    if (mode->style & FT_STYLE_STRONG) {
        FT_Fixed bold_str = mode->strength * sz_metrics->x_ppem;

        ftext->underline_size = FT_MulFix(ftext->underline_size,
                                          FX16_ONE + bold_str / 4);
    }

    /* fill it with the glyphs */
    fill_context(&context, ft, fontobj, mode, font);
    glyph_array = ftext->glyphs;
    next_pos = ftext->posns;

    for (ch = buffer, buffer_end = ch + string_length; ch < buffer_end; ++ch) {
        pen2.x = pen1.x;
        pen2.y = pen1.y;
        pen1.x = pen.x;
        pen1.y = pen.y;
        /*
         * Load the corresponding glyph from the cache
         */
        glyph = _PGFT_Cache_FindGlyph(*((FT_UInt32 *)ch), mode,
                                      &ftext->glyph_cache, &context);

        if (!glyph) {
            --ftext->length;
            continue;
        }
        image = glyph->image;
        glyph_width = glyph->width;
        glyph_height = glyph->height;

        /*
         * Do size calculations for all the glyphs in the text
         */
        if (use_kerning && prev_glyph_index) {
            error = FT_Get_Kerning(font, prev_glyph_index,
                                   glyph->glyph_index,
                                   FT_KERNING_UNFITTED, &kerning);
            if (error) {
                _PGFT_SetError(ft, "Loading glyphs", error);
                PyErr_SetString(PyExc_SDLError, _PGFT_GetError(ft));
                return 0;
            }
            if (rotation_angle != 0) {
                FT_Vector_Rotate(&kerning, rotation_angle);
            }
            pen.x += FX6_ROUND(kerning.x);
            pen.y += FX6_ROUND(kerning.y);
            if (FT_Vector_Length(&pen2) > FT_Vector_Length(&pen)) {
                pen.x = pen2.x;
                pen.y = pen2.y;
            }
        }

        prev_glyph_index = glyph->glyph_index;
        metrics = vertical ? &glyph->v_metrics : &glyph->h_metrics;
        if (metrics->bearing_rotated.y > top) {
            top = metrics->bearing_rotated.y;
        }
        if (pen.x + metrics->bearing_rotated.x < min_x) {
            min_x = pen.x + metrics->bearing_rotated.x;
        }
        if (pen.x + metrics->bearing_rotated.x + glyph_width > max_x) {
            max_x = pen.x + metrics->bearing_rotated.x + glyph_width;
        }
        next_pos->x = pen.x + metrics->bearing_rotated.x;
        pen.x += metrics->advance_rotated.x;
        if (vertical) {
            if (pen.y + metrics->bearing_rotated.y < min_y) {
                min_y = pen.y + metrics->bearing_rotated.y;
            }
            if (pen.y + metrics->bearing_rotated.y + glyph_height > max_y) {
                max_y = pen.y + metrics->bearing_rotated.y + glyph_height;
            }
            next_pos->y = pen.y + metrics->bearing_rotated.y;
            pen.y += metrics->advance_rotated.y;
        }
        else {
            if (pen.y - metrics->bearing_rotated.y < min_y) {
                min_y = pen.y - metrics->bearing_rotated.y;
            }
            if (pen.y - metrics->bearing_rotated.y + glyph_height > max_y) {
                max_y = pen.y - metrics->bearing_rotated.y + glyph_height;
            }
            next_pos->y = pen.y - metrics->bearing_rotated.y;
            pen.y -= metrics->advance_rotated.y;
        }
        *glyph_array++ = glyph;
        ++next_pos;
    }

    if (ftext->length == 0) {
        min_x = 0;
        max_x = 0;
        if (vertical) {
            ftext->min_y = 0;
            max_y = sz_metrics->height;
        }
        else {
            FT_Size_Metrics *sz_metrics = &font->size->metrics;

            min_y = -sz_metrics->ascender;
            max_y = -sz_metrics->descender;
        }
    }

    if (pad) {
        FT_Size_Metrics *sz_metrics = &font->size->metrics;

        if (pen.x > max_x) {
            max_x = pen.x;
        }
        else if (pen.x < min_x) {
            min_x = pen.x;
        }
        if (pen.y > max_y) {
            max_y = pen.y;
        }
        else if (pen.y < min_y) {
            min_y = pen.y;
        }
        if (vertical) {
            FT_Fixed right = sz_metrics->max_advance / 2;

            if (max_x < right) {
                max_x = right;
            }
            if (min_x > -right) {
                min_x = -right;
            }
            if (min_y > 0) {
                min_y = 0;
            }
            else if (max_y < pen.y) {
                max_y = pen.y;
            }
        }
        else {
            FT_Fixed ascender = sz_metrics->ascender;
            FT_Fixed descender = sz_metrics->descender;

            if (min_x > 0) {
                min_x = 0;
            }
            if (max_x < pen.x) {
                max_x = pen.x;
            }
            if (min_y > -ascender) {
                min_y = -ascender;
            }
            if (max_y <= -descender) {
                max_y = -descender + /* baseline */ FX6_ONE;
            }
        }
    }

    ftext->left = FX6_TRUNC(FX6_FLOOR(min_x));
    ftext->top = FX6_TRUNC(FX6_CEIL(top));
    ftext->min_x = min_x;
    ftext->max_x = max_x;
    ftext->min_y = min_y;
    ftext->max_y = max_y;
    ftext->advance.x = pen.x;
    ftext->advance.y = pen.y;

    return ftext;
}