2017-01-17 20:06:45 -06:00
// 17 january 2017
2016-05-05 17:23:54 -05:00
# include "uipriv_unix.h"
# include "draw.h"
2017-01-21 09:21:39 -06:00
// TODO
// - if the RTL override is at the beginning of a line, the preceding space is included?
2017-01-17 20:06:45 -06:00
struct uiDrawTextLayout {
PangoLayout * layout ;
2017-01-20 15:36:44 -06:00
uiDrawTextLayoutLineMetrics * lineMetrics ;
2016-05-05 17:23:54 -05:00
} ;
2017-01-17 20:06:45 -06:00
// See https://developer.gnome.org/pango/1.30/pango-Cairo-Rendering.html#pango-Cairo-Rendering.description
// For the conversion, see https://developer.gnome.org/pango/1.30/pango-Glyph-Storage.html#pango-units-to-double and https://developer.gnome.org/pango/1.30/pango-Glyph-Storage.html#pango-units-from-double
# define pangoToCairo(pango) (pango_units_to_double(pango))
# define cairoToPango(cairo) (pango_units_from_double(cairo))
2016-05-05 17:23:54 -05:00
2017-01-17 20:06:45 -06:00
// we need a context for a few things
// the documentation suggests creating cairo_t-specific, GdkScreen-specific, or even GtkWidget-specific contexts, but we can't really do that because we want our uiDrawTextFonts and uiDrawTextLayouts to be context-independent
// we could use pango_font_map_create_context(pango_cairo_font_map_get_default()) but that will ignore GDK-specific settings
// so let's use gdk_pango_context_get() instead; even though it's for the default screen only, it's good enough for us
# define mkGenericPangoCairoContext() (gdk_pango_context_get())
2016-05-05 17:23:54 -05:00
static const PangoStyle pangoItalics [ ] = {
[ uiDrawTextItalicNormal ] = PANGO_STYLE_NORMAL ,
[ uiDrawTextItalicOblique ] = PANGO_STYLE_OBLIQUE ,
[ uiDrawTextItalicItalic ] = PANGO_STYLE_ITALIC ,
} ;
static const PangoStretch pangoStretches [ ] = {
[ uiDrawTextStretchUltraCondensed ] = PANGO_STRETCH_ULTRA_CONDENSED ,
[ uiDrawTextStretchExtraCondensed ] = PANGO_STRETCH_EXTRA_CONDENSED ,
[ uiDrawTextStretchCondensed ] = PANGO_STRETCH_CONDENSED ,
[ uiDrawTextStretchSemiCondensed ] = PANGO_STRETCH_SEMI_CONDENSED ,
[ uiDrawTextStretchNormal ] = PANGO_STRETCH_NORMAL ,
[ uiDrawTextStretchSemiExpanded ] = PANGO_STRETCH_SEMI_EXPANDED ,
[ uiDrawTextStretchExpanded ] = PANGO_STRETCH_EXPANDED ,
[ uiDrawTextStretchExtraExpanded ] = PANGO_STRETCH_EXTRA_EXPANDED ,
[ uiDrawTextStretchUltraExpanded ] = PANGO_STRETCH_ULTRA_EXPANDED ,
} ;
2017-01-20 15:36:44 -06:00
// TODO neither these nor the overall extents seem to include trailing whitespace... we need to figure that out too
static void computeLineMetrics ( uiDrawTextLayout * tl )
{
PangoLayoutIter * iter ;
PangoLayoutLine * pll ;
PangoRectangle lineStartPos , lineExtents ;
int i , n ;
uiDrawTextLayoutLineMetrics * m ;
n = pango_layout_get_line_count ( tl - > layout ) ;
tl - > lineMetrics = ( uiDrawTextLayoutLineMetrics * ) uiAlloc ( n * sizeof ( uiDrawTextLayoutLineMetrics ) , " uiDrawTextLayoutLineMetrics[] (text layout) " ) ;
iter = pango_layout_get_iter ( tl - > layout ) ;
m = tl - > lineMetrics ;
for ( i = 0 ; i < n ; i + + ) {
int baselineY ;
// TODO we use this instead of _get_yrange() because of the block of text in that function's description about how line spacing is distributed in Pango; we have to worry about this when we start adding line spacing...
baselineY = pango_layout_iter_get_baseline ( iter ) ;
pll = pango_layout_iter_get_line_readonly ( iter ) ;
pango_layout_index_to_pos ( tl - > layout , pll - > start_index , & lineStartPos ) ;
pango_layout_line_get_extents ( pll , NULL , & lineExtents ) ;
// TODO unref pll?
// TODO is this correct for RTL glyphs?
m - > X = pangoToCairo ( lineStartPos . x ) ;
// TODO fix the whole combined not being updated shenanigans in the static build (here because ugh)
m - > Y = pangoToCairo ( baselineY - PANGO_ASCENT ( lineExtents ) ) ;
m - > Width = pangoToCairo ( lineExtents . width ) ;
m - > Height = pangoToCairo ( lineExtents . height ) ;
m - > BaselineY = pangoToCairo ( baselineY ) ;
m - > Ascent = pangoToCairo ( PANGO_ASCENT ( lineExtents ) ) ;
m - > Descent = pangoToCairo ( PANGO_DESCENT ( lineExtents ) ) ;
m - > Leading = 0 ; // TODO
m - > ParagraphSpacingBefore = 0 ; // TODO
m - > LineHeightSpace = 0 ; // TODO
m - > LineSpacing = 0 ; // TODO
m - > ParagraphSpacing = 0 ; // TODO
// don't worry about the return value; we're not using this after the last line
pango_layout_iter_next_line ( iter ) ;
m + + ;
}
pango_layout_iter_free ( iter ) ;
}
2017-01-17 20:06:45 -06:00
uiDrawTextLayout * uiDrawNewTextLayout ( uiAttributedString * s , uiDrawFontDescriptor * defaultFont , double width )
2016-05-05 17:23:54 -05:00
{
2017-01-17 20:06:45 -06:00
uiDrawTextLayout * tl ;
2016-05-05 17:23:54 -05:00
PangoContext * context ;
2017-01-17 20:06:45 -06:00
PangoFontDescription * desc ;
int pangoWidth ;
tl = uiNew ( uiDrawTextLayout ) ;
2016-05-05 17:23:54 -05:00
2017-01-17 20:06:45 -06:00
// in this case, the context is necessary to create the layout
// the layout takes a ref on the context so we can unref it afterward
2016-05-05 17:23:54 -05:00
context = mkGenericPangoCairoContext ( ) ;
2017-01-17 20:06:45 -06:00
tl - > layout = pango_layout_new ( context ) ;
2016-05-05 17:23:54 -05:00
g_object_unref ( context ) ;
2017-01-17 20:06:45 -06:00
// this is safe; pango_layout_set_text() copies the string
pango_layout_set_text ( tl - > layout , uiAttributedStringString ( s ) , - 1 ) ;
desc = pango_font_description_new ( ) ;
pango_font_description_set_family ( desc , defaultFont - > Family ) ;
pango_font_description_set_style ( desc , pangoItalics [ defaultFont - > Italic ] ) ;
// for the most part, pango weights correlate to ours
// the differences:
// - Book — libui: 350, Pango: 380
// - Ultra Heavy — libui: 950, Pango: 1000
// TODO figure out what to do about this misalignment
pango_font_description_set_weight ( desc , defaultFont - > Weight ) ;
pango_font_description_set_stretch ( desc , pangoStretches [ defaultFont - > Stretch ] ) ;
// see https://developer.gnome.org/pango/1.30/pango-Fonts.html#pango-font-description-set-size and https://developer.gnome.org/pango/1.30/pango-Glyph-Storage.html#pango-units-from-double
pango_font_description_set_size ( desc , pango_units_from_double ( defaultFont - > Size ) ) ;
pango_layout_set_font_description ( tl - > layout , desc ) ;
// this is safe; the description is copied
pango_font_description_free ( desc ) ;
2016-05-05 17:23:54 -05:00
2017-01-17 20:06:45 -06:00
pangoWidth = cairoToPango ( width ) ;
if ( width < 0 )
pangoWidth = - 1 ;
pango_layout_set_width ( tl - > layout , pangoWidth ) ;
2016-05-05 17:23:54 -05:00
2017-01-17 20:06:45 -06:00
// TODO attributes
2016-05-05 17:23:54 -05:00
2017-01-20 15:36:44 -06:00
computeLineMetrics ( tl ) ;
2017-01-17 20:06:45 -06:00
return tl ;
2016-05-05 17:23:54 -05:00
}
2017-01-17 20:06:45 -06:00
void uiDrawFreeTextLayout ( uiDrawTextLayout * tl )
2016-05-05 17:23:54 -05:00
{
2017-01-20 15:36:44 -06:00
uiFree ( tl - > lineMetrics ) ;
2017-01-17 20:06:45 -06:00
g_object_unref ( tl - > layout ) ;
uiFree ( tl ) ;
2016-05-05 17:23:54 -05:00
}
2017-01-17 20:06:45 -06:00
void uiDrawText ( uiDrawContext * c , uiDrawTextLayout * tl , double x , double y )
2016-05-05 17:23:54 -05:00
{
2017-01-17 20:06:45 -06:00
cairo_move_to ( c - > cr , x , y ) ;
pango_cairo_show_layout ( c - > cr , tl - > layout ) ;
2016-05-05 17:23:54 -05:00
}
2017-01-17 20:06:45 -06:00
void uiDrawTextLayoutExtents ( uiDrawTextLayout * tl , double * width , double * height )
2016-05-05 17:23:54 -05:00
{
2017-01-17 20:06:45 -06:00
PangoRectangle logical ;
2016-05-05 17:23:54 -05:00
2017-01-17 20:06:45 -06:00
pango_layout_get_extents ( tl - > layout , NULL , & logical ) ;
* width = pangoToCairo ( logical . width ) ;
* height = pangoToCairo ( logical . height ) ;
2016-05-05 17:23:54 -05:00
}
2017-01-17 20:06:45 -06:00
int uiDrawTextLayoutNumLines ( uiDrawTextLayout * tl )
2016-05-05 17:23:54 -05:00
{
2017-01-17 20:06:45 -06:00
return pango_layout_get_line_count ( tl - > layout ) ;
2016-05-05 17:23:54 -05:00
}
2017-01-17 20:06:45 -06:00
void uiDrawTextLayoutLineByteRange ( uiDrawTextLayout * tl , int line , size_t * start , size_t * end )
2016-05-05 17:23:54 -05:00
{
2017-01-17 20:06:45 -06:00
PangoLayoutLine * pll ;
pll = pango_layout_get_line_readonly ( tl - > layout , line ) ;
* start = pll - > start_index ;
* end = pll - > start_index + pll - > length ;
2017-01-20 15:36:44 -06:00
// TODO unref pll?
2016-05-05 17:23:54 -05:00
}
2017-01-17 20:06:45 -06:00
void uiDrawTextLayoutLineGetMetrics ( uiDrawTextLayout * tl , int line , uiDrawTextLayoutLineMetrics * m )
2016-05-05 17:23:54 -05:00
{
2017-01-20 15:36:44 -06:00
* m = tl - > lineMetrics [ line ] ;
2016-05-05 17:23:54 -05:00
}
2017-01-17 20:06:45 -06:00
// TODO
#if 0
2016-05-05 17:23:54 -05:00
{
2017-01-17 20:06:45 -06:00
PangoLayoutLine * pll ;
2016-05-05 17:23:54 -05:00
2017-01-17 20:06:45 -06:00
pll = pango_layout_get_line_readonly ( tl - > layout , line ) ;
// TODO unref?
2016-05-05 17:23:54 -05:00
}
2017-01-17 20:06:45 -06:00
# endif
2016-05-05 17:23:54 -05:00
2017-01-17 20:06:45 -06:00
void uiDrawTextLayoutHitTest ( uiDrawTextLayout * tl , double x , double y , uiDrawTextLayoutHitTestResult * result )
2016-05-05 17:23:54 -05:00
{
}
2017-01-17 20:06:45 -06:00
void uiDrawTextLayoutByteRangeToRectangle ( uiDrawTextLayout * tl , size_t start , size_t end , uiDrawTextLayoutByteRangeRectangle * r )
2016-05-05 17:23:54 -05:00
{
}