Bug 4464

Summary: The cyrillic characters aren't displayed properly
Product: cairo Reporter: Vladimir Moushkov <vlindos>
Component: generalAssignee: Carl Worth <cworth>
Status: RESOLVED WONTFIX QA Contact: cairo-bugs mailing list <cairo-bugs>
Severity: enhancement    
Priority: high    
Version: 1.1.1   
Hardware: x86 (IA32)   
OS: Linux (All)   
Whiteboard:
i915 platform: i915 features:
Attachments: cc -g -O2 `pkg-config --cflags cairo libpng12` `pkg-config --libs cairo libpng12`text-cyrillics.c -o text-cyrillics

Description Vladimir Moushkov 2005-09-15 03:30:06 UTC
The cyrillic characters aren't displayed properly - squares and so on are 
displyed instead.

keywords: text non english cyrillic
Comment 1 Owen Taylor 2005-09-15 04:49:29 UTC
You aren't providing sufficient information here for us to be able to
proceed further. Can you provide a complete standalone test case
that demonstrates the problem?
Comment 2 Vladimir Moushkov 2005-09-19 02:37:35 UTC
Test case is trivial, just try some of text test from cairo-demo with cyrillic 
(cairo-demo/png/text.c for example) test instead ot default one (use 
gedit(unicode mode) to edit the file, otherwise cyrillics may not be saved 
correctly). In pango (pango-cairo tested!) there is similar tests and they are 
working with cyrillics. Here is tested source:


----text.c with cyrillic text---------------------------------------------------
----------
/*
 * Copyright © 2003 USC, Information Sciences Institute
 *
 * Permission to use, copy, modify, distribute, and sell this software
 * and its documentation for any purpose is hereby granted without
 * fee, provided that the above copyright notice appear in all copies
 * and that both that copyright notice and this permission notice
 * appear in supporting documentation, and that the name of the
 * University of Southern California not be used in advertising or
 * publicity pertaining to distribution of the software without
 * specific, written prior permission. The University of Southern
 * California makes no representations about the suitability of this
 * software for any purpose.  It is provided "as is" without express
 * or implied warranty.
 *
 * THE UNIVERSITY OF SOUTHERN CALIFORNIA DISCLAIMS ALL WARRANTIES WITH
 * REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL THE UNIVERSITY OF
 * SOUTHERN CALIFORNIA BE LIABLE FOR ANY SPECIAL, INDIRECT OR
 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
 * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
 * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 *
 * Author: Carl D. Worth <cworth@isi.edu>
 */

#include <cairo.h>
#include <math.h>

#define TEXT "&#1047;&#1076;&#1088;&#1072;&#1074;&#1077;&#1081;, &#1057;&#1074;&#1103;&#1090;!"

#define WIDTH 450
#define HEIGHT 600
#define STRIDE (WIDTH * 4)

#define DOUBLE_TO_26_6(d) ((FT_F26Dot6)((d) * 64.0))
#define DOUBLE_FROM_26_6(t) ((double)(t) / 64.0)
#define DOUBLE_TO_16_16(d) ((FT_Fixed)((d) * 65536.0))
#define DOUBLE_FROM_16_16(t) ((double)(t) / 65536.0)

unsigned char image[STRIDE*HEIGHT];

static void
box_text (cairo_t *cr, const char *utf8, double x, double y)
{
    double line_width;
    cairo_text_extents_t extents;

    cairo_save (cr);

    cairo_text_extents (cr, TEXT, &extents);
    line_width = cairo_get_line_width (cr);
    cairo_rectangle (cr,
		     x + extents.x_bearing - line_width,
		     y + extents.y_bearing - line_width,
		     extents.width  + 2 * line_width,
		     extents.height + 2 * line_width);
    cairo_stroke (cr);

    cairo_move_to (cr, x, y);
    cairo_show_text (cr, utf8);
    cairo_move_to (cr, x, y);
    cairo_text_path (cr, utf8);
    cairo_set_source_rgb (cr, 1, 0, 0);
    cairo_set_line_width (cr, 1.0);
    cairo_stroke (cr);

    cairo_restore (cr);
}

static void
box_glyphs (cairo_t *cr, cairo_glyph_t *glyphs, int num_glyphs,
	    double x, double y)
{
    int i;
    double line_width;
    cairo_text_extents_t extents;

    cairo_save (cr);

    cairo_glyph_extents (cr, glyphs, num_glyphs, &extents);
    line_width = cairo_get_line_width (cr);
    cairo_rectangle (cr,
		     x + extents.x_bearing - line_width,
		     y + extents.y_bearing - line_width,
		     extents.width  + 2 * line_width,
		     extents.height + 2 * line_width);
    cairo_stroke (cr);

    for (i=0; i < num_glyphs; i++) {
	glyphs[i].x += x;
	glyphs[i].y += y;
    }
    cairo_show_glyphs (cr, glyphs, num_glyphs);
    cairo_glyph_path (cr, glyphs, num_glyphs);
    cairo_set_source_rgb (cr, 1, 0, 0);
    cairo_set_line_width (cr, 1.0);
    cairo_stroke (cr);
    for (i=0; i < num_glyphs; i++) {
	glyphs[i].x -= x;
	glyphs[i].y -= y;
    }

    cairo_restore (cr);
}

int
main (void)
{
    int i;
    cairo_surface_t *surface;
    cairo_t *cr;
    cairo_text_extents_t extents;
    cairo_font_extents_t font_extents;
    double dx, dy;
    double height;
#define NUM_GLYPHS 10
    cairo_glyph_t glyphs[NUM_GLYPHS];

    surface = cairo_image_surface_create_for_data (image, CAIRO_FORMAT_ARGB32,
						   WIDTH, HEIGHT, STRIDE);


    cr = cairo_create (surface);

    cairo_set_source_rgb (cr, 0., 0., 0.);
    cairo_set_line_width (cr, 2.0);

    cairo_save (cr);
    cairo_rectangle (cr, 0., 0., WIDTH, HEIGHT);
    cairo_set_source_rgba (cr, 0., 0., 0., 0.);
    cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
    cairo_fill (cr);
    cairo_restore (cr);

    cairo_select_font_face (cr, "sans", 0, 0);
    cairo_set_font_size (cr, 40);
#define XXX_DEMONSTRATE_EXTENTS_BUGS_WHEN_FONT_IS_TRANSFORMED 1
#if XXX_DEMONSTRATE_EXTENTS_BUGS_WHEN_FONT_IS_TRANSFORMED
    {
	cairo_matrix_t matrix;
	cairo_matrix_init_scale (&matrix, 40.0, -40.0);
	cairo_set_font_matrix (cr, &matrix);
    }

    cairo_scale (cr, 1.0, -1.0);
    cairo_translate (cr, 0.0, - HEIGHT);
#endif

    cairo_font_extents (cr, &font_extents);
    height = font_extents.height;

    dx = 0.0;
    dy = 0.0;
    for (i=0; i < NUM_GLYPHS; i++) {
	glyphs[i].index = i + 4;
	glyphs[i].x = dx;
	glyphs[i].y = dy;
	cairo_glyph_extents (cr, &glyphs[i], 1, &extents);
	dx += extents.x_advance;
	dy += extents.y_advance;
    }

    box_text (cr, TEXT, 10, height);

    cairo_translate (cr, 0, height);
    cairo_save (cr);
    {
	cairo_translate (cr, 10, height);
	cairo_rotate (cr, 10 * M_PI / 180.0);
	box_text (cr, TEXT, 0, 0);
    }
    cairo_restore (cr);

    cairo_translate (cr, 0, 2 * height);
    cairo_save (cr);
    {
	cairo_matrix_t matrix;
	cairo_matrix_init_identity (&matrix);
	cairo_matrix_scale (&matrix, 40, -40);
	cairo_matrix_rotate (&matrix, -10 * M_PI / 180.0);
	cairo_set_font_matrix (cr, &matrix);
    }
    box_text (cr, TEXT, 10, height);
    cairo_restore (cr);

    cairo_translate (cr, 0, 2 * height);
    box_glyphs (cr, glyphs, NUM_GLYPHS, 10, height);

    cairo_translate (cr, 10, 2 * height);
    cairo_save (cr);
    {
	cairo_rotate (cr, 10 * M_PI / 180.0);
	box_glyphs (cr, glyphs, NUM_GLYPHS, 0, 0);
    }
    cairo_restore (cr);

    cairo_translate (cr, 0, height);
    for (i=0; i < NUM_GLYPHS; i++)
	glyphs[i].y += i * 5;
    box_glyphs (cr, glyphs, NUM_GLYPHS, 10, height);

    cairo_surface_write_to_png (surface, "text.png");

    cairo_destroy (cr);

    cairo_surface_destroy (surface);

    return 0;
}
----text.c with cyrillic text---------------------------------------------------
----------
Comment 3 Owen Taylor 2005-09-19 04:56:07 UTC
No, really can you attach a 10-15 line program that shows the problem? (as
an attachment.) Your test case below has tons of stuff that's not relevant
here. Thanks!
Comment 4 Vladimir Moushkov 2005-09-19 06:15:06 UTC
Created attachment 3322 [details]
cc -g -O2  `pkg-config --cflags cairo libpng12` `pkg-config --libs cairo libpng12`text-cyrillics.c -o  text-cyrillics

Use for compilation:
cc -g -O2  `pkg-config --cflags cairo libpng12` `pkg-config --libs cairo
libpng12`text-cyrillics.c -o  text-cyrillics
Then run:
./text-cyrillics
It will result an 'text-rotate.png' image.
Comment 5 Owen Taylor 2005-09-19 06:23:33 UTC
With that test case, I get a PNG that looks OK, it says:

 Zdravei, svyat!

Or something like that. Ah, I think maybe I understand your issue -
Cairo (unlike Pango), doesn't have any sort of font fallback mechanism,
so it will just try to use the the best matched font. So if the first
font in the Serif alias doesn't have Cyrillic characters, you'll just
get boxes.

If you explicitely select a font with Cyrillic characters, or give
fontconfig a hint for font selection by setting LC_CTYPE to 
bg_BG.UTF-8, then it probably will work better.
Comment 6 Carl Worth 2005-09-19 09:20:09 UTC
(In reply to comment #5)
> Or something like that. Ah, I think maybe I understand your issue -
> Cairo (unlike Pango), doesn't have any sort of font fallback mechanism,
> so it will just try to use the the best matched font.

And I'd be quite happy to see cairo_show_text extended to display glyphs from
multiple fonts when necessary.

Anyone should feel free to make a feature enhancement bugzilla entry for that,
or start working on what it would take to do it.
Comment 7 Owen Taylor 2005-09-26 06:00:02 UTC
One tricky aspect of fontsetting is metrics; once you start pulling characters
from other fonts, it's clear what you should return from ascent, descent,
etc. If you just return the biggest ascent/descent from any possible font, you'll
get amazingly large line spacings because of Arabic's large descenders.
(etc.)

Because of this, and because of possible performance problems, I'd like to
see any support of fontsetting in Cairo clearly separated out from the current
code and restricted to the toy API in some fashion. It's fine if cairo_show_text()
supports fontsetting, but cairo_scaled_font_t needs to remain clearly a single 
font.

(reopening from NEEDINFO)
Comment 8 Chris Wilson 2008-10-10 08:17:39 UTC
The current sentiment is not to extend the "toy" font API any further, but instead require the application to use the advance text layout libraries, such as Pango, for anything but trivial demonstrations.

Use of freedesktop.org services, including Bugzilla, is subject to our Code of Conduct. How we collect and use information is described in our Privacy Policy.