add a "wrap" option to vips_text() (#3130)
see: https://github.com/libvips/libvips/discussions/2073 https://github.com/libvips/libvips/discussions/3126
This commit is contained in:
parent
144834a6aa
commit
ef2646ef6a
@ -18,6 +18,7 @@ master
|
|||||||
- threadpools size dynamically with load
|
- threadpools size dynamically with load
|
||||||
- operations can hint threadpool size
|
- operations can hint threadpool size
|
||||||
- support for N-colour ICC profiles
|
- support for N-colour ICC profiles
|
||||||
|
- add @wrap to vips_text()
|
||||||
- GIF load supports truncated frames [tlsa]
|
- GIF load supports truncated frames [tlsa]
|
||||||
|
|
||||||
9/11/22 started 8.13.4
|
9/11/22 started 8.13.4
|
||||||
|
@ -63,6 +63,20 @@
|
|||||||
* The im_benchmark() operations are for testing the VIPS SMP system.
|
* The im_benchmark() operations are for testing the VIPS SMP system.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* VipsTextWrap:
|
||||||
|
* @VIPS_TEXT_WRAP_WORD: wrap at word boundaries
|
||||||
|
* @VIPS_TEXT_WRAP_CHAR: wrap at character boundaries
|
||||||
|
* @VIPS_TEXT_WRAP_WORD_CHAR: wrap at word boundaries, but fall back to
|
||||||
|
* character boundaries if there is not enough space for a full word
|
||||||
|
* @VIPS_TEXT_WRAP_NONE: no wrapping
|
||||||
|
*
|
||||||
|
* Sets the word wrapping style for vips_text() when used with a maximum
|
||||||
|
* width.
|
||||||
|
*
|
||||||
|
* See also: vips_text().
|
||||||
|
*/
|
||||||
|
|
||||||
G_DEFINE_ABSTRACT_TYPE( VipsCreate, vips_create, VIPS_TYPE_OPERATION );
|
G_DEFINE_ABSTRACT_TYPE( VipsCreate, vips_create, VIPS_TYPE_OPERATION );
|
||||||
|
|
||||||
static int
|
static int
|
||||||
|
@ -35,6 +35,8 @@
|
|||||||
* 12/4/21
|
* 12/4/21
|
||||||
* - switch to cairo for text rendering
|
* - switch to cairo for text rendering
|
||||||
* - add rgba flag
|
* - add rgba flag
|
||||||
|
* 31/10/22
|
||||||
|
* - add @wrap
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -105,6 +107,7 @@ typedef struct _VipsText {
|
|||||||
int dpi;
|
int dpi;
|
||||||
char *fontfile;
|
char *fontfile;
|
||||||
gboolean rgba;
|
gboolean rgba;
|
||||||
|
VipsTextWrap wrap;
|
||||||
|
|
||||||
PangoFontMap *fontmap;
|
PangoFontMap *fontmap;
|
||||||
PangoContext *context;
|
PangoContext *context;
|
||||||
@ -139,11 +142,13 @@ vips_text_dispose( GObject *gobject )
|
|||||||
static PangoLayout *
|
static PangoLayout *
|
||||||
text_layout_new( PangoContext *context,
|
text_layout_new( PangoContext *context,
|
||||||
const char *text, const char *font, int width, int spacing,
|
const char *text, const char *font, int width, int spacing,
|
||||||
VipsAlign align, gboolean justify )
|
VipsAlign align, VipsTextWrap wrap, gboolean justify )
|
||||||
{
|
{
|
||||||
PangoLayout *layout;
|
PangoLayout *layout;
|
||||||
PangoFontDescription *font_description;
|
PangoFontDescription *font_description;
|
||||||
PangoAlignment palign;
|
PangoAlignment palign;
|
||||||
|
PangoWrapMode pwrap;
|
||||||
|
int pwidth;
|
||||||
|
|
||||||
layout = pango_layout_new( context );
|
layout = pango_layout_new( context );
|
||||||
pango_layout_set_markup( layout, text, -1 );
|
pango_layout_set_markup( layout, text, -1 );
|
||||||
@ -151,9 +156,7 @@ text_layout_new( PangoContext *context,
|
|||||||
font_description = pango_font_description_from_string( font );
|
font_description = pango_font_description_from_string( font );
|
||||||
pango_layout_set_font_description( layout, font_description );
|
pango_layout_set_font_description( layout, font_description );
|
||||||
pango_font_description_free( font_description );
|
pango_font_description_free( font_description );
|
||||||
|
pango_layout_set_justify( layout, justify );
|
||||||
if( width > 0 )
|
|
||||||
pango_layout_set_width( layout, width * PANGO_SCALE );
|
|
||||||
|
|
||||||
if( spacing > 0 )
|
if( spacing > 0 )
|
||||||
pango_layout_set_spacing( layout, spacing * PANGO_SCALE );
|
pango_layout_set_spacing( layout, spacing * PANGO_SCALE );
|
||||||
@ -177,7 +180,33 @@ text_layout_new( PangoContext *context,
|
|||||||
}
|
}
|
||||||
pango_layout_set_alignment( layout, palign );
|
pango_layout_set_alignment( layout, palign );
|
||||||
|
|
||||||
pango_layout_set_justify( layout, justify );
|
switch( wrap ) {
|
||||||
|
case VIPS_TEXT_WRAP_NONE:
|
||||||
|
pwrap = PANGO_WRAP_WORD_CHAR;
|
||||||
|
pwidth = -1;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case VIPS_TEXT_WRAP_CHAR:
|
||||||
|
pwrap = PANGO_WRAP_CHAR;
|
||||||
|
pwidth = width * PANGO_SCALE;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case VIPS_TEXT_WRAP_WORD:
|
||||||
|
pwrap = PANGO_WRAP_WORD;
|
||||||
|
pwidth = width * PANGO_SCALE;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case VIPS_TEXT_WRAP_WORD_CHAR:
|
||||||
|
default:
|
||||||
|
pwrap = PANGO_WRAP_WORD_CHAR;
|
||||||
|
pwidth = width * PANGO_SCALE;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
pango_layout_set_wrap( layout, pwrap );
|
||||||
|
|
||||||
|
if( pwidth > 0 )
|
||||||
|
pango_layout_set_width( layout, width * PANGO_SCALE );
|
||||||
|
|
||||||
return( layout );
|
return( layout );
|
||||||
}
|
}
|
||||||
@ -194,7 +223,8 @@ vips_text_get_extents( VipsText *text, VipsRect *extents )
|
|||||||
VIPS_UNREF( text->layout );
|
VIPS_UNREF( text->layout );
|
||||||
if( !(text->layout = text_layout_new( text->context,
|
if( !(text->layout = text_layout_new( text->context,
|
||||||
text->text, text->font,
|
text->text, text->font,
|
||||||
text->width, text->spacing, text->align, text->justify )) )
|
text->width, text->spacing, text->align, text->wrap,
|
||||||
|
text->justify )) )
|
||||||
return( -1 );
|
return( -1 );
|
||||||
|
|
||||||
pango_layout_get_pixel_extents( text->layout,
|
pango_layout_get_pixel_extents( text->layout,
|
||||||
@ -552,41 +582,48 @@ vips_text_class_init( VipsTextClass *class )
|
|||||||
G_STRUCT_OFFSET( VipsText, justify ),
|
G_STRUCT_OFFSET( VipsText, justify ),
|
||||||
FALSE );
|
FALSE );
|
||||||
|
|
||||||
VIPS_ARG_INT( class, "dpi", 9,
|
VIPS_ARG_INT( class, "dpi", 10,
|
||||||
_( "DPI" ),
|
_( "DPI" ),
|
||||||
_( "DPI to render at" ),
|
_( "DPI to render at" ),
|
||||||
VIPS_ARGUMENT_OPTIONAL_INPUT,
|
VIPS_ARGUMENT_OPTIONAL_INPUT,
|
||||||
G_STRUCT_OFFSET( VipsText, dpi ),
|
G_STRUCT_OFFSET( VipsText, dpi ),
|
||||||
1, 1000000, 72 );
|
1, 1000000, 72 );
|
||||||
|
|
||||||
VIPS_ARG_INT( class, "autofit_dpi", 10,
|
VIPS_ARG_INT( class, "autofit_dpi", 11,
|
||||||
_( "Autofit DPI" ),
|
_( "Autofit DPI" ),
|
||||||
_( "DPI selected by autofit" ),
|
_( "DPI selected by autofit" ),
|
||||||
VIPS_ARGUMENT_OPTIONAL_OUTPUT,
|
VIPS_ARGUMENT_OPTIONAL_OUTPUT,
|
||||||
G_STRUCT_OFFSET( VipsText, dpi ),
|
G_STRUCT_OFFSET( VipsText, dpi ),
|
||||||
1, 1000000, 72 );
|
1, 1000000, 72 );
|
||||||
|
|
||||||
VIPS_ARG_INT( class, "spacing", 11,
|
VIPS_ARG_INT( class, "spacing", 12,
|
||||||
_( "Spacing" ),
|
_( "Spacing" ),
|
||||||
_( "Line spacing" ),
|
_( "Line spacing" ),
|
||||||
VIPS_ARGUMENT_OPTIONAL_INPUT,
|
VIPS_ARGUMENT_OPTIONAL_INPUT,
|
||||||
G_STRUCT_OFFSET( VipsText, spacing ),
|
G_STRUCT_OFFSET( VipsText, spacing ),
|
||||||
0, 1000000, 0 );
|
0, 1000000, 0 );
|
||||||
|
|
||||||
VIPS_ARG_STRING( class, "fontfile", 12,
|
VIPS_ARG_STRING( class, "fontfile", 13,
|
||||||
_( "Font file" ),
|
_( "Font file" ),
|
||||||
_( "Load this font file" ),
|
_( "Load this font file" ),
|
||||||
VIPS_ARGUMENT_OPTIONAL_INPUT,
|
VIPS_ARGUMENT_OPTIONAL_INPUT,
|
||||||
G_STRUCT_OFFSET( VipsText, fontfile ),
|
G_STRUCT_OFFSET( VipsText, fontfile ),
|
||||||
NULL );
|
NULL );
|
||||||
|
|
||||||
VIPS_ARG_BOOL( class, "rgba", 9,
|
VIPS_ARG_BOOL( class, "rgba", 14,
|
||||||
_( "RGBA" ),
|
_( "RGBA" ),
|
||||||
_( "Enable RGBA output" ),
|
_( "Enable RGBA output" ),
|
||||||
VIPS_ARGUMENT_OPTIONAL_INPUT,
|
VIPS_ARGUMENT_OPTIONAL_INPUT,
|
||||||
G_STRUCT_OFFSET( VipsText, rgba ),
|
G_STRUCT_OFFSET( VipsText, rgba ),
|
||||||
FALSE );
|
FALSE );
|
||||||
|
|
||||||
|
VIPS_ARG_ENUM( class, "wrap", 15,
|
||||||
|
_( "Wrap" ),
|
||||||
|
_( "Wrap lines on word or character boundaries" ),
|
||||||
|
VIPS_ARGUMENT_OPTIONAL_INPUT,
|
||||||
|
G_STRUCT_OFFSET( VipsText, wrap ),
|
||||||
|
VIPS_TYPE_TEXT_WRAP, VIPS_TEXT_WRAP_WORD );
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
@ -594,6 +631,7 @@ vips_text_init( VipsText *text )
|
|||||||
{
|
{
|
||||||
text->align = VIPS_ALIGN_LOW;
|
text->align = VIPS_ALIGN_LOW;
|
||||||
text->dpi = 72;
|
text->dpi = 72;
|
||||||
|
text->wrap = VIPS_TEXT_WRAP_WORD;
|
||||||
VIPS_SETSTR( text->font, "sans 12" );
|
VIPS_SETSTR( text->font, "sans 12" );
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -617,6 +655,7 @@ vips_text_init( VipsText *text )
|
|||||||
* * @autofit_dpi: %gint, read out auto-fitted DPI
|
* * @autofit_dpi: %gint, read out auto-fitted DPI
|
||||||
* * @rgba: %gboolean, enable RGBA output
|
* * @rgba: %gboolean, enable RGBA output
|
||||||
* * @spacing: %gint, space lines by this in points
|
* * @spacing: %gint, space lines by this in points
|
||||||
|
* * @wrap: #VipsTextWrap, wrap lines on characters or words
|
||||||
*
|
*
|
||||||
* Draw the string @text to an image. @out is normally a one-band 8-bit
|
* Draw the string @text to an image. @out is normally a one-band 8-bit
|
||||||
* unsigned char image, with 0 for no text and 255 for text. Values between
|
* unsigned char image, with 0 for no text and 255 for text. Values between
|
||||||
@ -635,8 +674,10 @@ vips_text_init( VipsText *text )
|
|||||||
* You can specify a font to load with @fontfile. You'll need to also set the
|
* You can specify a font to load with @fontfile. You'll need to also set the
|
||||||
* name of the font with @font.
|
* name of the font with @font.
|
||||||
*
|
*
|
||||||
* @width is the number of pixels to word-wrap at. Lines of text wider than
|
* @width is the number of pixels to word-wrap at. By default, lines of text
|
||||||
* this will be broken at word boundaries.
|
* wider than this will be broken at word boundaries.
|
||||||
|
* Use @wrap to set lines to wrap on word or character boundaries, or to
|
||||||
|
* disable line breaks.
|
||||||
*
|
*
|
||||||
* Set @justify to turn on line justification.
|
* Set @justify to turn on line justification.
|
||||||
* @align can be used to set the alignment style for multi-line
|
* @align can be used to set the alignment style for multi-line
|
||||||
|
@ -38,6 +38,14 @@
|
|||||||
extern "C" {
|
extern "C" {
|
||||||
#endif /*__cplusplus*/
|
#endif /*__cplusplus*/
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
VIPS_TEXT_WRAP_WORD = 0,
|
||||||
|
VIPS_TEXT_WRAP_CHAR,
|
||||||
|
VIPS_TEXT_WRAP_WORD_CHAR,
|
||||||
|
VIPS_TEXT_WRAP_NONE,
|
||||||
|
VIPS_TEXT_WRAP_LAST
|
||||||
|
} VipsTextWrap;
|
||||||
|
|
||||||
VIPS_API
|
VIPS_API
|
||||||
int vips_black( VipsImage **out, int width, int height, ... )
|
int vips_black( VipsImage **out, int width, int height, ... )
|
||||||
G_GNUC_NULL_TERMINATED;
|
G_GNUC_NULL_TERMINATED;
|
||||||
|
@ -417,6 +417,11 @@ class TestCreate:
|
|||||||
# text rendering systems
|
# text rendering systems
|
||||||
assert abs(im.width - 500) < 50
|
assert abs(im.width - 500) < 50
|
||||||
|
|
||||||
|
# test wrap
|
||||||
|
im1 = pyvips.Image.text("helloworld", width=100, dpi=500)
|
||||||
|
im2 = pyvips.Image.text("helloworld", width=100, dpi=500, wrap="char")
|
||||||
|
assert im1.width > im2.width
|
||||||
|
|
||||||
def test_tonelut(self):
|
def test_tonelut(self):
|
||||||
im = pyvips.Image.tonelut()
|
im = pyvips.Image.tonelut()
|
||||||
assert im.bands == 1
|
assert im.bands == 1
|
||||||
|
Loading…
Reference in New Issue
Block a user