~ubuntu-branches/ubuntu/hardy/libgdiplus/hardy

« back to all changes in this revision

Viewing changes to src/text-pango.c

  • Committer: Bazaar Package Importer
  • Author(s): Emilio Pozuelo Monfort
  • Date: 2007-12-18 13:08:10 UTC
  • mfrom: (1.1.15 upstream)
  • Revision ID: james.westby@ubuntu.com-20071218130810-hlmitxfddf6h511j
Tags: 1.2.6-1ubuntu1
* Sync with Debian:
  - debian/control:
    + Add lpia and sparc to the architectures. We support them.
    + Change Maintainer to Ubuntu Mono Team.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * Copyright (C) 2007 Novell, Inc (http://www.novell.com)
 
3
 * 
 
4
 * Permission is hereby granted, free of charge, to any person obtaining a copy of this software
 
5
 * and associated documentation files (the "Software"), to deal in the Software without restriction,
 
6
 * including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
 
7
 * and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so,
 
8
 * subject to the following conditions:
 
9
 * 
 
10
 * The above copyright notice and this permission notice shall be included in all copies or substantial
 
11
 * portions of the Software.
 
12
 * 
 
13
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT
 
14
 * NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
 
15
 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
 
16
 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
 
17
 * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 
18
 * 
 
19
 * Authors:
 
20
 *   Sebastien Pouliot  <sebastien@ximian.com>
 
21
 */
 
22
 
 
23
#include "gdiplus-private.h"
 
24
 
 
25
#ifdef USE_PANGO_RENDERING
 
26
 
 
27
#include "text-pango-private.h"
 
28
#include "graphics-private.h"
 
29
#include "graphics-cairo-private.h"
 
30
#include "brush-private.h"
 
31
#include "font-private.h"
 
32
 
 
33
 
 
34
/*
 
35
 * NOTE: all parameter's validations are done inside text.c
 
36
 */
 
37
 
 
38
static PangoAttrList*
 
39
gdip_get_layout_attributes (PangoLayout *layout)
 
40
{
 
41
        PangoAttrList *list = pango_layout_get_attributes (layout);
 
42
        if (!list)
 
43
                list = pango_attr_list_new ();
 
44
        else
 
45
                pango_attr_list_ref (list);
 
46
        return list;
 
47
}
 
48
 
 
49
static void
 
50
gdip_process_accelerators (gchar *text, int length, PangoAttrList *list)
 
51
{
 
52
        int i;
 
53
        for (i = 0; i < length; i++) {
 
54
                if (*(text + i) == GDIP_WINDOWS_ACCELERATOR) {
 
55
                        /* don't show the prefix character */
 
56
                        *(text + i) = GDIP_PANGOHACK_ACCELERATOR;
 
57
                        /* if the next character is an accelerator then skip over it (&& == &) */
 
58
                        if ((i < length - 1) && (*(text + i + 1) == GDIP_WINDOWS_ACCELERATOR)) {
 
59
                                i++;
 
60
                        } else if (list) {
 
61
                                /* add an attribute on the next character */
 
62
                                PangoAttribute *attr = pango_attr_underline_new (PANGO_UNDERLINE_LOW);
 
63
                                attr->start_index = i + 1;
 
64
                                attr->end_index = i + 2;
 
65
                                pango_attr_list_insert (list, attr);
 
66
                        }
 
67
                }
 
68
        }
 
69
}
 
70
 
 
71
PangoLayout*
 
72
gdip_pango_setup_layout (cairo_t *ct, GDIPCONST WCHAR *stringUnicode, int length, GDIPCONST GpFont *font, 
 
73
        GDIPCONST RectF *rc, RectF *box, GDIPCONST GpStringFormat *format)
 
74
{
 
75
        GpStringFormat *fmt;
 
76
        PangoLayout *layout;
 
77
        PangoContext *context;
 
78
        PangoMatrix matrix = PANGO_MATRIX_INIT;
 
79
        PangoRectangle logical;
 
80
        PangoAttrList *list = NULL;
 
81
 
 
82
        gchar *text = ucs2_to_utf8 (stringUnicode, length);
 
83
        if (!text)
 
84
                return NULL;
 
85
 
 
86
//g_warning ("layout >%s< (%d) [x %g, y %g, w %g, h %g] [font %s, %g points]", text, length, rc->X, rc->Y, rc->Width, rc->Height, font->face, font->emSize);
 
87
 
 
88
        /* a NULL format is valid, it means get the generic default values (and free them later) */
 
89
        if (!format) {
 
90
                GpStatus status = GdipStringFormatGetGenericDefault ((GpStringFormat **)&fmt);
 
91
                if (status != Ok) {
 
92
                        GdipFree (text);
 
93
                        return NULL;
 
94
                }
 
95
        } else {
 
96
                fmt = (GpStringFormat *)format;
 
97
        }
 
98
 
 
99
        /* unless specified we don't consider the trailing spaces, unless there is just one space (#80680) */
 
100
        if ((fmt->formatFlags & StringFormatFlagsMeasureTrailingSpaces) == 0) {
 
101
                while ((length > 0) && (isspace (*(text + length - 1))))
 
102
                        length--;
 
103
                if (length == 0)
 
104
                        length = 1;
 
105
        }
 
106
 
 
107
        layout = pango_cairo_create_layout (ct);
 
108
 
 
109
        /* context is owned by Pango (i.e. not referenced counted) do not free */
 
110
        context = pango_layout_get_context (layout);
 
111
 
 
112
        pango_layout_set_font_description (layout, gdip_get_pango_font_description ((GpFont*) font));
 
113
 
 
114
        if ((rc->Width <= 0.0) || (fmt->formatFlags & StringFormatFlagsNoWrap)) {
 
115
                pango_layout_set_width (layout, -1);
 
116
        } else {
 
117
                /* minus one to deal with our AA offset */
 
118
                int width = rc->Width - 1;
 
119
                /* TODO incomplete (missing height adjustment) */
 
120
                if ((fmt->formatFlags & StringFormatFlagsNoFitBlackBox) == 0)
 
121
                        width -= 2;
 
122
                pango_layout_set_width (layout, width * PANGO_SCALE);
 
123
        }
 
124
        
 
125
        if (fmt->formatFlags & StringFormatFlagsDirectionRightToLeft) {
 
126
                /* with GDI+ the API not the renderer makes the direction decision */
 
127
                pango_layout_set_auto_dir (layout, FALSE);
 
128
                pango_context_set_base_dir (context, PANGO_DIRECTION_RTL);
 
129
                pango_layout_context_changed (layout);
 
130
 
 
131
                /* horizontal alignment */
 
132
                switch (fmt->alignment) {
 
133
                case StringAlignmentNear:
 
134
                        pango_layout_set_alignment (layout, PANGO_ALIGN_RIGHT);
 
135
                        break;
 
136
                case StringAlignmentCenter:
 
137
                        pango_layout_set_alignment (layout, PANGO_ALIGN_CENTER);
 
138
                        break;
 
139
                case StringAlignmentFar:
 
140
                        pango_layout_set_alignment (layout, PANGO_ALIGN_LEFT);
 
141
                        break;
 
142
                }
 
143
        } else {
 
144
                /* horizontal alignment */
 
145
                switch (fmt->alignment) {
 
146
                case StringAlignmentNear:
 
147
                        pango_layout_set_alignment (layout, PANGO_ALIGN_LEFT);
 
148
                        break;
 
149
                case StringAlignmentCenter:
 
150
                        pango_layout_set_alignment (layout, PANGO_ALIGN_CENTER);
 
151
                        break;
 
152
                case StringAlignmentFar:
 
153
                        pango_layout_set_alignment (layout, PANGO_ALIGN_RIGHT);
 
154
                        break;
 
155
                }
 
156
        }
 
157
 
 
158
#ifdef PANGO_VERSION_CHECK
 
159
#if PANGO_VERSION_CHECK(1,16,0)
 
160
        if (fmt->formatFlags & StringFormatFlagsDirectionVertical) {
 
161
                /* only since Pango 1.16 */
 
162
                pango_context_set_base_gravity (context, PANGO_GRAVITY_EAST);
 
163
                pango_context_set_gravity_hint (context, PANGO_GRAVITY_HINT_STRONG);
 
164
                pango_layout_context_changed (layout);
 
165
        }
 
166
#endif
 
167
#endif
 
168
        
 
169
        /* TODO - StringFormatFlagsDisplayFormatControl
 
170
                scan and replace them ??? */
 
171
 
 
172
        /* TODO - StringFormatFlagsLineLimit */
 
173
 
 
174
        if ((rc->Width != 0) && (rc->Height != 0) && ((fmt->formatFlags & StringFormatFlagsNoClip) == 0)) {
 
175
//g_warning ("\tclip [%g %g %g %g]", rc->X, rc->Y, rc->Width, rc->Height);
 
176
                /* We do not call cairo_reset_clip because we want to take previous clipping into account */
 
177
                cairo_rectangle (ct, rc->X, rc->Y, rc->Width + 0.5, rc->Height + 0.5);
 
178
                cairo_clip (ct);
 
179
        }
 
180
 
 
181
        switch (fmt->trimming) {
 
182
        case StringTrimmingNone:
 
183
                pango_layout_set_wrap (layout, PANGO_WRAP_WORD_CHAR);
 
184
                pango_layout_set_ellipsize (layout, PANGO_ELLIPSIZE_NONE);
 
185
                break;
 
186
        case StringTrimmingCharacter:
 
187
                pango_layout_set_wrap (layout, PANGO_WRAP_CHAR);
 
188
                pango_layout_set_ellipsize (layout, PANGO_ELLIPSIZE_NONE);
 
189
                break;
 
190
        case StringTrimmingWord:
 
191
                pango_layout_set_wrap (layout, PANGO_WRAP_WORD);
 
192
                pango_layout_set_ellipsize (layout, PANGO_ELLIPSIZE_NONE);
 
193
                break;
 
194
        case StringTrimmingEllipsisCharacter:
 
195
                pango_layout_set_wrap (layout, PANGO_WRAP_CHAR);
 
196
                pango_layout_set_ellipsize (layout, PANGO_ELLIPSIZE_END);
 
197
                break;
 
198
        case StringTrimmingEllipsisWord:
 
199
                pango_layout_set_wrap (layout, PANGO_WRAP_WORD);
 
200
                pango_layout_set_ellipsize (layout, PANGO_ELLIPSIZE_END);
 
201
                break;
 
202
        case StringTrimmingEllipsisPath:
 
203
                pango_layout_set_wrap (layout, PANGO_WRAP_WORD_CHAR);
 
204
                pango_layout_set_ellipsize (layout, PANGO_ELLIPSIZE_MIDDLE);
 
205
                break;
 
206
        }
 
207
 
 
208
        /* some stuff can only be done by manipulating the attributes (but we can avoid this most of the time) */
 
209
        if ((fmt->formatFlags & StringFormatFlagsNoFontFallback) || (font->style & (FontStyleUnderline | FontStyleStrikeout))) {
 
210
 
 
211
                list = gdip_get_layout_attributes (layout);
 
212
 
 
213
                /* StringFormatFlagsNoFontFallback */
 
214
                if (fmt->formatFlags & StringFormatFlagsNoFontFallback) {
 
215
                        PangoAttribute *attr = pango_attr_fallback_new (FALSE);
 
216
                        attr->start_index = 0;
 
217
                        attr->end_index = length;
 
218
                        pango_attr_list_insert (list, attr);
 
219
                }
 
220
 
 
221
                if (font->style & FontStyleUnderline) {
 
222
                        PangoAttribute *attr = pango_attr_underline_new (PANGO_UNDERLINE_SINGLE);
 
223
                        attr->start_index = 0;
 
224
                        attr->end_index = length;
 
225
                        pango_attr_list_insert (list, attr);
 
226
                }
 
227
 
 
228
                if (font->style & FontStyleStrikeout) {
 
229
                        PangoAttribute *attr = pango_attr_strikethrough_new (TRUE);
 
230
                        attr->start_index = 0;
 
231
                        attr->end_index = length;
 
232
                        pango_attr_list_insert (list, attr);
 
233
                }
 
234
        }
 
235
 
 
236
        switch (fmt->hotkeyPrefix) {
 
237
        case HotkeyPrefixHide:
 
238
                /* we need to remove any accelerator from the string */
 
239
                gdip_process_accelerators (text, length, NULL);
 
240
                break;
 
241
        case HotkeyPrefixShow:
 
242
                /* optimization: is seems that we never see the hotkey when using an underline font */
 
243
                if (font->style & FontStyleUnderline) {
 
244
                        /* so don't bother drawing it (and simply add the '&' character) */
 
245
                        gdip_process_accelerators (text, length, NULL);
 
246
                } else {
 
247
                        /* find accelerator and add attribute to the next character (unless it's the prefix too) */
 
248
                        if (!list)
 
249
                                list = gdip_get_layout_attributes (layout);
 
250
                        gdip_process_accelerators (text, length, list);
 
251
                }
 
252
                break;
 
253
        default:
 
254
                break;
 
255
        }
 
256
 
 
257
        if (list) {
 
258
                pango_layout_set_attributes (layout, list);
 
259
                pango_attr_list_unref (list);
 
260
        }
 
261
 
 
262
        pango_layout_set_text (layout, text, length);
 
263
        GdipFree (text);
 
264
 
 
265
        pango_layout_get_pixel_extents (layout, NULL, &logical);
 
266
//g_warning ("\tlogical\t[x %d, y %d, w %d, h %d]", logical.x, logical.y, logical.width, logical.height);
 
267
 
 
268
        box->X = rc->X;
 
269
        box->Y = rc->Y;
 
270
        box->Height = logical.height;
 
271
        /* add an extra pixel for our AA hack + 2 more if we don't draw on the box itself */
 
272
        box->Width = logical.width + (fmt->formatFlags & StringFormatFlagsNoFitBlackBox) ? 1 : 3;
 
273
//g_warning ("\tbox\t[x %g, y %g, w %g, h %g]", box->X, box->Y, box->Width, box->Height);
 
274
 
 
275
        /* vertical alignment*/
 
276
        switch (fmt->lineAlignment) {
 
277
        case StringAlignmentNear:
 
278
                break;
 
279
        case StringAlignmentCenter:
 
280
                box->Y += (rc->Height - logical.height) / 2;
 
281
                break;
 
282
        case StringAlignmentFar:
 
283
                box->Y += (rc->Height - logical.height);
 
284
                break;
 
285
        }
 
286
//g_warning ("va-box\t[x %g, y %g, w %g, h %g]", box->X, box->Y, box->Width, box->Height);
 
287
 
 
288
        pango_cairo_update_layout (ct, layout);
 
289
 
 
290
        return layout;
 
291
}
 
292
 
 
293
GpStatus
 
294
pango_DrawString (GpGraphics *graphics, GDIPCONST WCHAR *stringUnicode, int length, GDIPCONST GpFont *font, GDIPCONST RectF *rc, 
 
295
        GDIPCONST GpStringFormat *format, GpBrush *brush)
 
296
{
 
297
        PangoLayout *layout;
 
298
        RectF box;
 
299
 
 
300
        cairo_save (graphics->ct);
 
301
 
 
302
        layout = gdip_pango_setup_layout (graphics->ct, stringUnicode, length, font, rc, &box, format);
 
303
        if (!layout) {
 
304
                cairo_restore (graphics->ct);
 
305
                return OutOfMemory;
 
306
        }
 
307
 
 
308
        /* Setup cairo */
 
309
        if (brush) {
 
310
                gdip_brush_setup (graphics, brush);
 
311
        } else {
 
312
                cairo_set_source_rgb (graphics->ct, 0., 0., 0.);
 
313
        }
 
314
 
 
315
        gdip_cairo_move_to (graphics, box.X, box.Y, FALSE, TRUE);
 
316
        pango_cairo_show_layout (graphics->ct, layout);
 
317
 
 
318
        g_object_unref (layout);
 
319
        cairo_restore (graphics->ct);
 
320
        return Ok;
 
321
}
 
322
 
 
323
GpStatus
 
324
pango_MeasureString (GpGraphics *graphics, GDIPCONST WCHAR *stringUnicode, int length, GDIPCONST GpFont *font, GDIPCONST RectF *rc,
 
325
        GDIPCONST GpStringFormat *format, RectF *boundingBox, int *codepointsFitted, int *linesFilled)
 
326
{
 
327
        PangoLayout *layout;
 
328
 
 
329
        cairo_save (graphics->ct);
 
330
 
 
331
        layout = gdip_pango_setup_layout (graphics->ct, stringUnicode, length, font, rc, boundingBox, format);
 
332
        if (!layout) {
 
333
                cairo_restore (graphics->ct);
 
334
                return OutOfMemory;
 
335
        }
 
336
                
 
337
        if (codepointsFitted) {
 
338
                // TODO - dummy (total) value returned
 
339
                *codepointsFitted = length;
 
340
        }
 
341
 
 
342
        if (linesFilled) {
 
343
                *linesFilled = pango_layout_get_line_count (layout);
 
344
//g_warning ("linesFilled %d", *linesFilled);
 
345
        }
 
346
//else g_warning ("linesFilled %d", pango_layout_get_line_count (layout));
 
347
 
 
348
        g_object_unref (layout);
 
349
        cairo_restore (graphics->ct);
 
350
        return Ok;
 
351
}
 
352
 
 
353
GpStatus
 
354
pango_MeasureCharacterRanges (GpGraphics *graphics, GDIPCONST WCHAR *stringUnicode, int length, GDIPCONST GpFont *font, 
 
355
        GDIPCONST GpRectF *layoutRect, GDIPCONST GpStringFormat *format, int regionCount, GpRegion **regions)
 
356
{
 
357
        PangoLayout *layout;
 
358
        GpStatus status = Ok;
 
359
        int i, j;
 
360
        GpRectF boundingBox;
 
361
 
 
362
        cairo_save (graphics->ct);
 
363
 
 
364
        layout = gdip_pango_setup_layout (graphics->ct, stringUnicode, length, font, layoutRect, &boundingBox, format);
 
365
        if (!layout) {
 
366
                cairo_restore (graphics->ct);
 
367
                return OutOfMemory;
 
368
        }
 
369
 
 
370
        /* Create a region for every char range */
 
371
        for (i = 0; i < format->charRangeCount; i++) {
 
372
                int start, end;
 
373
                CharacterRange range = format->charRanges [i];
 
374
 
 
375
                GdipSetEmpty (regions [i]); 
 
376
 
 
377
                if (range.Length > 0)
 
378
                        start = range.First;
 
379
                else
 
380
                        start = range.First + range.Length;
 
381
 
 
382
                end = start + range.Length;
 
383
 
 
384
                /* sanity check on charRange. negative lengths are allowed */
 
385
                if (range.First < 0) {
 
386
                        status = InvalidParameter;
 
387
                        goto cleanup;
 
388
                }
 
389
 
 
390
                if ((start < 0) || (end > length)) {
 
391
                        status = InvalidParameter;
 
392
                        goto cleanup;
 
393
                }
 
394
 
 
395
                /* calculate the regions */
 
396
                for (j = start; j < end; j++) {
 
397
                        PangoRectangle box;
 
398
                        GpRectF charRect;
 
399
 
 
400
                        pango_layout_index_to_pos (layout, j, &box);
 
401
                        charRect.X = (float)box.x / PANGO_SCALE;
 
402
                        charRect.Y = (float)box.y / PANGO_SCALE;
 
403
                        charRect.Width = (float)box.width / PANGO_SCALE;
 
404
                        charRect.Height = (float)box.height / PANGO_SCALE;
 
405
//g_warning ("[%d] [%d : %d-%d] %c [x %g y %g w %g h %g]", i, j, start, end, (char)stringUnicode[j], charRect.X, charRect.Y, charRect.Width, charRect.Height);
 
406
                        status = GdipCombineRegionRect (regions [i], &charRect, CombineModeUnion);
 
407
                        if (status != Ok)
 
408
                                break;
 
409
                }
 
410
                if (status != Ok)
 
411
                        break;
 
412
        }
 
413
 
 
414
cleanup:
 
415
        cairo_restore (graphics->ct);
 
416
        return status;
 
417
}
 
418
 
 
419
#endif