1 /*
2 * go-pango-extras.c: Various utility routines that should have been in pango.
3 *
4 * Authors:
5 * Morten Welinder (terra@gnome.org)
6 * Andreas J. Guelzow (aguelzow@pyrshep.ca)
7 *
8 * This program is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU General Public License as
10 * published by the Free Software Foundation; either version 2 of the
11 * License, or (at your option) version 3.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
21 * USA
22 */
23
24 #include <goffice/goffice-config.h>
25 #include "go-pango-extras.h"
26 #include "go-glib-extras.h"
27 #include <string.h>
28
29 struct cb_splice {
30 guint pos, len;
31 PangoAttrList *result;
32 };
33
34 static gboolean
cb_splice(PangoAttribute * attr,gpointer _data)35 cb_splice (PangoAttribute *attr, gpointer _data)
36 {
37 struct cb_splice *data = _data;
38
39 if (attr->start_index >= data->pos) {
40 PangoAttribute *new_attr = pango_attribute_copy (attr);
41 new_attr->start_index += data->len;
42 new_attr->end_index += data->len;
43 pango_attr_list_insert (data->result, new_attr);
44 } else if (attr->end_index <= data->pos) {
45 PangoAttribute *new_attr = pango_attribute_copy (attr);
46 pango_attr_list_insert (data->result, new_attr);
47 } else {
48 PangoAttribute *new_attr = pango_attribute_copy (attr);
49 new_attr->end_index = data->pos;
50 pango_attr_list_insert (data->result, new_attr);
51
52 new_attr = pango_attribute_copy (attr);
53 new_attr->start_index = data->pos + data->len;
54 new_attr->end_index += data->len;
55 pango_attr_list_insert (data->result, new_attr);
56 }
57
58 return FALSE;
59 }
60
61 static gboolean
cb_splice_true(G_GNUC_UNUSED PangoAttribute * attr,G_GNUC_UNUSED gpointer data)62 cb_splice_true (G_GNUC_UNUSED PangoAttribute *attr, G_GNUC_UNUSED gpointer data)
63 {
64 return TRUE;
65 }
66
67 /**
68 * go_pango_attr_list_open_hole:
69 * @tape: An attribute list
70 * @pos: a text position in bytes
71 * @len: length of segment in bytes
72 *
73 * This function opens up a blank segment of attributes. This is what to
74 * call after inserting a segment into the text described by the attributes.
75 */
76 void
go_pango_attr_list_open_hole(PangoAttrList * tape,guint pos,guint len)77 go_pango_attr_list_open_hole (PangoAttrList *tape, guint pos, guint len)
78 {
79 /* Clean out the tape. */
80 PangoAttrList *tape2 =
81 pango_attr_list_filter (tape, cb_splice_true, NULL);
82
83 if (tape2) {
84 struct cb_splice data;
85 data.result = tape;
86 data.pos = pos;
87 data.len = len;
88
89 (void)pango_attr_list_filter (tape2, cb_splice, &data);
90 pango_attr_list_unref (tape2);
91 }
92 }
93
94 struct cb_erase {
95 unsigned start_pos, end_pos, len; /* in bytes not chars */
96 };
97
98
99 /*
100 *
101 * | +-------------------+ The attribute
102 *
103 * +----+ (1)
104 * +------------------+ (2)
105 * +------+ (3)
106 * +--+ (4)
107 * +--------------+ (5)
108 * +---------------------------------+ (6)
109 *
110 */
111 static gboolean
cb_delete_filter(PangoAttribute * a,struct cb_erase * change)112 cb_delete_filter (PangoAttribute *a, struct cb_erase *change)
113 {
114 if (change->start_pos >= a->end_index)
115 return FALSE; /* (1) */
116
117 if (change->start_pos <= a->start_index) {
118 if (change->end_pos >= a->end_index)
119 return TRUE; /* (6) */
120
121 a->end_index -= change->len;
122 if (change->end_pos >= a->start_index)
123 a->start_index = change->start_pos; /* (5) */
124 else
125 a->start_index -= change->len; /* (4) */
126 } else {
127 if (change->end_pos >= a->end_index)
128 a->end_index = change->start_pos; /* (2) */
129 else
130 a->end_index -= change->len; /* (3) */
131 }
132
133 return FALSE;
134 }
135
136 /**
137 * go_pango_attr_list_erase:
138 * @attrs: An attribute list
139 * @pos: a text position in bytes
140 * @len: length of segment in bytes
141 *
142 * This function erases a segment of attributes. This is what to call
143 * after deleting a segment from the text described by the attributes.
144 */
145 void
go_pango_attr_list_erase(PangoAttrList * attrs,guint pos,guint len)146 go_pango_attr_list_erase (PangoAttrList *attrs, guint pos, guint len)
147 {
148 PangoAttrList *gunk;
149 struct cb_erase data;
150
151 if (attrs == NULL)
152 return;
153
154 data.start_pos = pos;
155 data.end_pos = pos + len;
156 data.len = len;
157
158 gunk = pango_attr_list_filter (attrs,
159 (PangoAttrFilterFunc)cb_delete_filter,
160 &data);
161 if (gunk != NULL)
162 pango_attr_list_unref (gunk);
163 }
164
165
166
167 struct cb_unset {
168 PangoAttrList *list;
169 guint start_index, end_index;
170 };
171
172 static gboolean
cb_unset1(PangoAttribute * attr,gpointer _data)173 cb_unset1 (PangoAttribute *attr, gpointer _data)
174 {
175 const PangoAttrType *ptype = _data;
176 return *ptype == PANGO_ATTR_INVALID || *ptype == attr->klass->type;
177 }
178
179 static gboolean
cb_unset2(PangoAttribute * attr,gpointer _data)180 cb_unset2 (PangoAttribute *attr, gpointer _data)
181 {
182 struct cb_unset *data = _data;
183
184 /* All inside cleared area. */
185 if (attr->start_index >= data->start_index &&
186 attr->end_index <= data->end_index)
187 return FALSE;
188
189 attr = pango_attribute_copy (attr);
190
191 if (attr->end_index > data->start_index && attr->end_index <= data->end_index)
192 /* Ends inside cleared area. */
193 attr->end_index = data->start_index;
194 else if (attr->start_index >= data->start_index && attr->start_index < data->end_index)
195 /* Starts inside cleared area. */
196 attr->start_index = data->end_index;
197 else if (attr->start_index < data->start_index && attr->end_index > data->end_index) {
198 /* Starts before, ends after. */
199 PangoAttribute *attr2 = pango_attribute_copy (attr);
200 attr2->start_index = data->end_index;
201 pango_attr_list_insert (data->list, attr2);
202 attr->end_index = data->start_index;
203 }
204
205 pango_attr_list_insert (data->list, attr);
206
207 return FALSE;
208 }
209
210 /**
211 * go_pango_attr_list_unset:
212 * @list: #PangoAttrList
213 * @start: starting character index
214 * @end: last character index
215 * @type: #PangoAttrType
216 *
217 * See http://bugzilla.gnome.org/show_bug.cgi?id=163679
218 **/
219 void
go_pango_attr_list_unset(PangoAttrList * list,gint start,gint end,PangoAttrType type)220 go_pango_attr_list_unset (PangoAttrList *list,
221 gint start,
222 gint end,
223 PangoAttrType type)
224 {
225 PangoAttrList *matches;
226 struct cb_unset data;
227
228 g_return_if_fail (list != NULL);
229
230 if (start >= end || end < 0)
231 return;
232
233 /*
234 * Pull out the attribute subset we even care about. Notice that this
235 * is all-or-nothing for a given type so any reordering will not
236 * matter.
237 */
238 matches = pango_attr_list_filter (list, cb_unset1, &type);
239 if (!matches)
240 return;
241
242 /* Get rid of whole segments and adjust end_index of rest. */
243 data.list = list;
244 data.start_index = start;
245 data.end_index = end;
246 (void)pango_attr_list_filter (matches, cb_unset2, &data);
247
248 pango_attr_list_unref (matches);
249 }
250
251 static gboolean
cb_empty(G_GNUC_UNUSED PangoAttribute * attr,gpointer data)252 cb_empty (G_GNUC_UNUSED PangoAttribute *attr, gpointer data)
253 {
254 gboolean *pempty = data;
255 *pempty = FALSE;
256 return FALSE;
257 }
258
259 gboolean
go_pango_attr_list_is_empty(const PangoAttrList * attrs)260 go_pango_attr_list_is_empty (const PangoAttrList *attrs)
261 {
262 gboolean empty = TRUE;
263 if (attrs)
264 (void)pango_attr_list_filter ((PangoAttrList *)attrs,
265 cb_empty, &empty);
266 return empty;
267 }
268
269 #ifdef GOFFICE_WITH_GTK
270 static gboolean
go_load_pango_attributes_into_buffer_filter(PangoAttribute * attribute,G_GNUC_UNUSED gpointer data)271 go_load_pango_attributes_into_buffer_filter (PangoAttribute *attribute,
272 G_GNUC_UNUSED gpointer data)
273 {
274 return ((PANGO_ATTR_FOREGROUND == attribute->klass->type) ||
275 (PANGO_ATTR_RISE == attribute->klass->type));
276 }
277
278 static gboolean
go_load_pango_attributes_into_buffer_named_filter(PangoAttribute * attribute,G_GNUC_UNUSED gpointer data)279 go_load_pango_attributes_into_buffer_named_filter (PangoAttribute *attribute,
280 G_GNUC_UNUSED gpointer data)
281 {
282 return ((PANGO_ATTR_STYLE == attribute->klass->type) ||
283 (PANGO_ATTR_WEIGHT == attribute->klass->type) ||
284 (PANGO_ATTR_UNDERLINE == attribute->klass->type) ||
285 (PANGO_ATTR_STRIKETHROUGH == attribute->klass->type));
286 }
287
288 void
go_create_std_tags_for_buffer(GtkTextBuffer * buffer)289 go_create_std_tags_for_buffer (GtkTextBuffer *buffer)
290 {
291 gtk_text_buffer_create_tag (buffer, "PANGO_STYLE_NORMAL", "style", PANGO_STYLE_NORMAL,
292 "style-set", TRUE, NULL);
293 gtk_text_buffer_create_tag (buffer, "PANGO_STYLE_ITALIC", "style", PANGO_STYLE_ITALIC,
294 "style-set", TRUE, NULL);
295 gtk_text_buffer_create_tag (buffer, "PANGO_STRIKETHROUGH_TRUE", "strikethrough", TRUE,
296 "strikethrough-set", TRUE, NULL);
297 gtk_text_buffer_create_tag (buffer, "PANGO_STRIKETHROUGH_FALSE", "strikethrough", FALSE,
298 "strikethrough-set", TRUE, NULL);
299 gtk_text_buffer_create_tag (buffer, "PANGO_WEIGHT_THIN", "weight", 100,
300 "weight-set", TRUE, NULL);
301 gtk_text_buffer_create_tag (buffer, "PANGO_WEIGHT_ULTRALIGHT", "weight", PANGO_WEIGHT_ULTRALIGHT,
302 "weight-set", TRUE, NULL);
303 gtk_text_buffer_create_tag (buffer, "PANGO_WEIGHT_LIGHT", "weight", PANGO_WEIGHT_LIGHT,
304 "weight-set", TRUE, NULL);
305 gtk_text_buffer_create_tag (buffer, "PANGO_WEIGHT_BOOK", "weight", 380,
306 "weight-set", TRUE, NULL);
307 gtk_text_buffer_create_tag (buffer, "PANGO_WEIGHT_NORMAL", "weight", PANGO_WEIGHT_NORMAL,
308 "weight-set", TRUE, NULL);
309 gtk_text_buffer_create_tag (buffer, "PANGO_WEIGHT_MEDIUM", "weight", 500,
310 "weight-set", TRUE, NULL);
311 gtk_text_buffer_create_tag (buffer, "PANGO_WEIGHT_SEMIBOLD", "weight", PANGO_WEIGHT_SEMIBOLD,
312 "weight-set", TRUE, NULL);
313 gtk_text_buffer_create_tag (buffer, "PANGO_WEIGHT_BOLD", "weight", PANGO_WEIGHT_BOLD,
314 "weight-set", TRUE, NULL);
315 gtk_text_buffer_create_tag (buffer, "PANGO_WEIGHT_ULTRABOLD", "weight", PANGO_WEIGHT_ULTRABOLD,
316 "weight-set", TRUE, NULL);
317 gtk_text_buffer_create_tag (buffer, "PANGO_WEIGHT_HEAVY", "weight", PANGO_WEIGHT_HEAVY,
318 "weight-set", TRUE, NULL);
319 gtk_text_buffer_create_tag (buffer, "PANGO_WEIGHT_ULTRAHEAVY", "weight", 1000,
320 "weight-set", TRUE, NULL);
321 gtk_text_buffer_create_tag (buffer, "PANGO_UNDERLINE_NONE", "underline", PANGO_UNDERLINE_NONE,
322 "underline-set", TRUE, NULL);
323 gtk_text_buffer_create_tag (buffer, "PANGO_UNDERLINE_SINGLE", "underline", PANGO_UNDERLINE_SINGLE,
324 "underline-set", TRUE, NULL);
325 gtk_text_buffer_create_tag (buffer, "PANGO_UNDERLINE_DOUBLE", "underline", PANGO_UNDERLINE_DOUBLE,
326 "underline-set", TRUE, NULL);
327 gtk_text_buffer_create_tag (buffer, "PANGO_UNDERLINE_LOW", "underline", PANGO_UNDERLINE_LOW,
328 "underline-set", TRUE, NULL);
329 gtk_text_buffer_create_tag (buffer, "PANGO_UNDERLINE_ERROR", "underline", PANGO_UNDERLINE_ERROR,
330 "underline-set", TRUE, NULL);
331 }
332
333 static gint
go_load_pango_byte_to_char(gchar const * str,gint byte)334 go_load_pango_byte_to_char (gchar const *str, gint byte)
335 {
336 if (byte >= (gint)strlen (str))
337 return g_utf8_strlen (str, -1);
338 return g_utf8_pointer_to_offset (str, g_utf8_prev_char (str + byte + 1));
339 }
340
341 void
go_load_pango_attributes_into_buffer(PangoAttrList * markup,GtkTextBuffer * buffer,gchar const * str)342 go_load_pango_attributes_into_buffer (PangoAttrList *markup, GtkTextBuffer *buffer, gchar const *str)
343 {
344 PangoAttrIterator * iter;
345 PangoAttrList *copied_markup;
346 PangoAttrList *our_markup;
347
348 if (markup == NULL)
349 return;
350
351 /* For some styles we create named tags. The names are taken from the Pango enums */
352
353 copied_markup = pango_attr_list_copy (markup);
354 our_markup = pango_attr_list_filter (copied_markup,
355 go_load_pango_attributes_into_buffer_named_filter,
356 NULL);
357 pango_attr_list_unref (copied_markup);
358 if (our_markup != NULL) {
359 iter = pango_attr_list_get_iterator (our_markup);
360
361 do {
362 GSList *attr = pango_attr_iterator_get_attrs (iter);
363 if (attr != NULL) {
364 GSList *ptr;
365 gint start, end;
366 GtkTextIter start_iter, end_iter;
367 char const *name;
368
369 pango_attr_iterator_range (iter, &start, &end);
370 start = go_load_pango_byte_to_char (str, start);
371 end = go_load_pango_byte_to_char (str, end);
372 gtk_text_buffer_get_iter_at_offset (buffer, &start_iter, start);
373 gtk_text_buffer_get_iter_at_offset (buffer, &end_iter, end);
374
375 for (ptr = attr; ptr != NULL; ptr = ptr->next) {
376 PangoAttribute *attribute = ptr->data;
377 GtkTextTag *tag;
378 int val;
379
380 switch (attribute->klass->type) {
381 case PANGO_ATTR_STYLE:
382 name = (((PangoAttrInt *)attribute)->value
383 == PANGO_STYLE_NORMAL)
384 ? "PANGO_STYLE_NORMAL" :
385 "PANGO_STYLE_ITALIC";
386 tag = gtk_text_tag_table_lookup
387 (gtk_text_buffer_get_tag_table (buffer),
388 name);
389 gtk_text_buffer_apply_tag (buffer, tag,
390 &start_iter, &end_iter);
391 break;
392 case PANGO_ATTR_STRIKETHROUGH:
393 name = (((PangoAttrInt *)attribute)->value) ?
394 "PANGO_STRIKETHROUGH_TRUE" :
395 "PANGO_STRIKETHROUGH_FALSE";
396 tag = gtk_text_tag_table_lookup
397 (gtk_text_buffer_get_tag_table (buffer),
398 name);
399 gtk_text_buffer_apply_tag (buffer, tag,
400 &start_iter, &end_iter);
401 break;
402 case PANGO_ATTR_UNDERLINE:
403 val = ((PangoAttrInt *)attribute)->value;
404 if (val == PANGO_UNDERLINE_NONE)
405 gtk_text_buffer_apply_tag_by_name (buffer,"PANGO_UNDERLINE_NONE",
406 &start_iter, &end_iter);
407 else if (val == PANGO_UNDERLINE_SINGLE)
408 gtk_text_buffer_apply_tag_by_name (buffer,"PANGO_UNDERLINE_SINGLE",
409 &start_iter, &end_iter);
410 else if (val == PANGO_UNDERLINE_DOUBLE)
411 gtk_text_buffer_apply_tag_by_name (buffer,"PANGO_UNDERLINE_DOUBLE",
412 &start_iter, &end_iter);
413 else if (val == PANGO_UNDERLINE_LOW)
414 gtk_text_buffer_apply_tag_by_name (buffer,"PANGO_UNDERLINE_LOW",
415 &start_iter, &end_iter);
416 else if (val == PANGO_UNDERLINE_ERROR)
417 gtk_text_buffer_apply_tag_by_name (buffer,"PANGO_UNDERLINE_ERROR",
418 &start_iter, &end_iter);
419 break;
420 case PANGO_ATTR_WEIGHT:
421 val = ((PangoAttrInt *)attribute)->value;
422 if (val < (PANGO_WEIGHT_THIN + PANGO_WEIGHT_ULTRALIGHT)/2)
423 gtk_text_buffer_apply_tag_by_name (buffer,"PANGO_WEIGHT_THIN",
424 &start_iter, &end_iter);
425 else if (val < (PANGO_WEIGHT_ULTRALIGHT + PANGO_WEIGHT_LIGHT)/2)
426 gtk_text_buffer_apply_tag_by_name (buffer,"PANGO_WEIGHT_ULTRALIGHT",
427 &start_iter, &end_iter);
428 else if (val < (PANGO_WEIGHT_LIGHT + PANGO_WEIGHT_BOOK)/2)
429 gtk_text_buffer_apply_tag_by_name (buffer,"PANGO_WEIGHT_LIGHT",
430 &start_iter, &end_iter);
431 else if (val < (PANGO_WEIGHT_BOOK + PANGO_WEIGHT_NORMAL)/2)
432 gtk_text_buffer_apply_tag_by_name (buffer,"PANGO_WEIGHT_BOOK",
433 &start_iter, &end_iter);
434 else if (val < (PANGO_WEIGHT_NORMAL + PANGO_WEIGHT_MEDIUM)/2)
435 gtk_text_buffer_apply_tag_by_name (buffer,"PANGO_WEIGHT_NORMAL",
436 &start_iter, &end_iter);
437 else if (val < (PANGO_WEIGHT_MEDIUM + PANGO_WEIGHT_SEMIBOLD)/2)
438 gtk_text_buffer_apply_tag_by_name (buffer,"PANGO_WEIGHT_MEDIUM",
439 &start_iter, &end_iter);
440 else if (val < (PANGO_WEIGHT_SEMIBOLD + PANGO_WEIGHT_BOLD)/2)
441 gtk_text_buffer_apply_tag_by_name (buffer,"PANGO_WEIGHT_SEMIBOLD",
442 &start_iter, &end_iter);
443 else if (val < (PANGO_WEIGHT_BOLD + PANGO_WEIGHT_ULTRABOLD)/2)
444 gtk_text_buffer_apply_tag_by_name (buffer,"PANGO_WEIGHT_BOLD",
445 &start_iter, &end_iter);
446 else if (val < (PANGO_WEIGHT_ULTRABOLD + PANGO_WEIGHT_HEAVY)/2)
447 gtk_text_buffer_apply_tag_by_name (buffer,"PANGO_WEIGHT_ULTRABOLD",
448 &start_iter, &end_iter);
449 else if (val < (PANGO_WEIGHT_HEAVY + PANGO_WEIGHT_ULTRAHEAVY)/2)
450 gtk_text_buffer_apply_tag_by_name (buffer,"PANGO_WEIGHT_HEAVY",
451 &start_iter, &end_iter);
452 else gtk_text_buffer_apply_tag_by_name (buffer,"PANGO_WEIGHT_ULTRAHEAVY",
453 &start_iter, &end_iter);
454 break;
455 default:
456 break;
457 }
458 }
459 g_slist_free_full (attr, (GDestroyNotify)pango_attribute_destroy);
460 }
461 } while (pango_attr_iterator_next (iter));
462 pango_attr_iterator_destroy (iter);
463 pango_attr_list_unref (our_markup);
464 }
465
466 /* For other styles (that are not at true/false type styles) we use unnamed styles */
467
468 copied_markup = pango_attr_list_copy (markup);
469 our_markup = pango_attr_list_filter (copied_markup,
470 go_load_pango_attributes_into_buffer_filter,
471 NULL);
472 pango_attr_list_unref (copied_markup);
473 if (our_markup != NULL) {
474 iter = pango_attr_list_get_iterator (our_markup);
475
476 do {
477 GSList *attr = pango_attr_iterator_get_attrs (iter);
478 if (attr != NULL) {
479 char *string;
480 GSList *ptr;
481 gint start, end;
482 GtkTextIter start_iter, end_iter;
483 GtkTextTag *tag = gtk_text_buffer_create_tag (buffer, NULL, NULL);
484 for (ptr = attr; ptr != NULL; ptr = ptr->next) {
485 PangoAttribute *attribute = ptr->data;
486 switch (attribute->klass->type) {
487 case PANGO_ATTR_FOREGROUND:
488 string = pango_color_to_string
489 (&((PangoAttrColor *)attribute)->color);
490 g_object_set (G_OBJECT (tag),
491 "foreground", string,
492 "foreground-set", TRUE,
493 NULL);
494 g_free (string);
495 break;
496 case PANGO_ATTR_RISE:
497 g_object_set (G_OBJECT (tag),
498 "rise",
499 ((PangoAttrInt *)attribute)->value,
500 "rise-set", TRUE,
501 NULL);
502 break;
503 default:
504 break;
505 }
506 }
507 pango_attr_iterator_range (iter, &start, &end);
508 start = go_load_pango_byte_to_char (str, start);
509 end = go_load_pango_byte_to_char (str, end);
510 gtk_text_buffer_get_iter_at_offset (buffer, &start_iter, start);
511 gtk_text_buffer_get_iter_at_offset (buffer, &end_iter, end);
512 gtk_text_buffer_apply_tag (buffer, tag, &start_iter, &end_iter);
513 g_slist_free_full (attr, (GDestroyNotify)pango_attribute_destroy);
514 }
515 } while (pango_attr_iterator_next (iter));
516 pango_attr_iterator_destroy (iter);
517 pango_attr_list_unref (our_markup);
518 }
519 }
520 #endif
521
522 static gboolean
filter_func(PangoAttribute * attribute,G_GNUC_UNUSED gpointer data)523 filter_func (PangoAttribute *attribute, G_GNUC_UNUSED gpointer data)
524 {
525 PangoAttrType type = attribute->klass->type;
526 return (type == go_pango_attr_superscript_get_attr_type () ||
527 type == go_pango_attr_subscript_get_attr_type ());
528 }
529
530 static void
go_pango_translate_here(PangoAttrIterator * state_iter,PangoAttrIterator * attr_iter,PangoAttrList * attrs)531 go_pango_translate_here (PangoAttrIterator *state_iter,
532 PangoAttrIterator *attr_iter, PangoAttrList *attrs)
533 {
534 double font_scale = 1.;
535 double scale = 1.;
536 int rise = 0;
537 PangoAttribute *pa;
538 PangoFontDescription *desc;
539 GSList *the_attrs, *l;
540 gint range_start, range_end;
541
542 pango_attr_iterator_range (attr_iter, &range_start, &range_end);
543
544 if (range_start == range_end)
545 return;
546
547 desc = pango_font_description_new ();
548 pango_attr_iterator_get_font (state_iter, desc, NULL, NULL);
549 font_scale = pango_font_description_get_size (desc)/
550 (double)PANGO_SCALE/10.;
551 pango_font_description_free (desc);
552
553 pa = pango_attr_iterator_get
554 (state_iter, PANGO_ATTR_SCALE);
555 if (pa)
556 scale = ((PangoAttrFloat *)pa)->value;
557 pa = pango_attr_iterator_get
558 (state_iter, PANGO_ATTR_RISE);
559 if (pa)
560 rise = ((PangoAttrInt *)pa)->value;
561
562 /* We should probably figured out the default font size used */
563 /* rather than assuming it is 10 pts * scale */
564 if (font_scale == 0)
565 font_scale = scale;
566
567 the_attrs = pango_attr_iterator_get_attrs (attr_iter);
568 for (l = the_attrs; l != NULL; l = l->next) {
569 PangoAttribute *attribute = l->data;
570 PangoAttrType type = attribute->klass->type;
571 if (type == go_pango_attr_superscript_get_attr_type ()) {
572 GOPangoAttrSuperscript *attr = (GOPangoAttrSuperscript *)attribute;
573 if (attr->val) {
574 scale *= GO_SUPERSCRIPT_SCALE;
575 rise += GO_SUPERSCRIPT_RISE * font_scale;
576 font_scale *= GO_SUPERSCRIPT_SCALE;
577 }
578 } else { /* go_pango_attr_subscript_type */
579 GOPangoAttrSubscript *attr = (GOPangoAttrSubscript *)attribute;
580 if (attr->val) {
581 scale *= GO_SUBSCRIPT_SCALE;
582 rise += GO_SUBSCRIPT_RISE * font_scale;
583 font_scale *= GO_SUBSCRIPT_SCALE;
584 }
585 }
586 }
587
588 if (the_attrs != NULL) {
589 PangoAttribute *attr = pango_attr_scale_new (scale);
590 attr->start_index = range_start;
591 attr->end_index = range_end;
592 pango_attr_list_insert (attrs, attr);
593 attr = pango_attr_rise_new (rise);
594 attr->start_index = range_start;
595 attr->end_index = range_end;
596 pango_attr_list_insert (attrs, attr);
597 }
598 g_slist_free_full (the_attrs, (GDestroyNotify)pango_attribute_destroy);
599 }
600
601
602 PangoAttrList *
go_pango_translate_attributes(PangoAttrList * attrs)603 go_pango_translate_attributes (PangoAttrList *attrs)
604 {
605 PangoAttrList *n_attrs, *filtered;
606
607 if (attrs == NULL)
608 return NULL;
609
610 n_attrs = pango_attr_list_copy (attrs);
611 filtered = pango_attr_list_filter (n_attrs, filter_func, NULL);
612
613 if (filtered == NULL) {
614 pango_attr_list_unref (n_attrs);
615 return attrs;
616 } else {
617 PangoAttrIterator *iter, *f_iter;
618 f_iter = pango_attr_list_get_iterator (filtered);
619 do {
620 gint f_range_start, f_range_end;
621 gint range_start, range_end;
622 /* We need to restart everytime since we are changing n_attrs */
623 iter = pango_attr_list_get_iterator (n_attrs);
624 pango_attr_iterator_range (f_iter, &f_range_start,
625 &f_range_end);
626 pango_attr_iterator_range (iter, &range_start,
627 &range_end);
628 while (range_end <= f_range_start) {
629 if (!pango_attr_iterator_next (iter))
630 break;
631 pango_attr_iterator_range (iter, &range_start,
632 &range_end);
633 }
634 /* Now range_end > f_range_start >= range_start */
635 go_pango_translate_here (iter, f_iter, n_attrs);
636 pango_attr_iterator_destroy (iter);
637 } while (pango_attr_iterator_next (f_iter));
638 pango_attr_iterator_destroy (f_iter);
639 }
640 pango_attr_list_unref (filtered);
641 return n_attrs;
642 }
643
644 void
go_pango_translate_layout(PangoLayout * layout)645 go_pango_translate_layout (PangoLayout *layout)
646 {
647 PangoAttrList *attrs, *n_attrs;
648
649 g_return_if_fail (layout != NULL);
650
651 attrs = pango_layout_get_attributes (layout);
652 n_attrs = go_pango_translate_attributes (attrs);
653 if (attrs != n_attrs) {
654 pango_layout_set_attributes (layout, n_attrs);
655 pango_attr_list_unref (n_attrs);
656 }
657
658 }
659
660 PangoAttrType
go_pango_attr_subscript_get_attr_type(void)661 go_pango_attr_subscript_get_attr_type (void)
662 {
663 static PangoAttrType go_pango_attr_subscript_type = PANGO_ATTR_INVALID;
664
665 if(go_pango_attr_subscript_type == PANGO_ATTR_INVALID)
666 go_pango_attr_subscript_type =
667 pango_attr_type_register ("GOSubscript");
668
669 return go_pango_attr_subscript_type;
670 }
671
672 PangoAttrType
go_pango_attr_subscript_get_type(void)673 go_pango_attr_subscript_get_type (void)
674 {
675 return go_pango_attr_subscript_get_attr_type ();
676 }
677
678 PangoAttrType
go_pango_attr_superscript_get_attr_type(void)679 go_pango_attr_superscript_get_attr_type (void)
680 {
681 static PangoAttrType go_pango_attr_superscript_type = PANGO_ATTR_INVALID;
682
683 if(go_pango_attr_superscript_type == PANGO_ATTR_INVALID)
684 go_pango_attr_superscript_type =
685 pango_attr_type_register ("GOSuperscript");
686
687 return go_pango_attr_superscript_type;
688 }
689
690 PangoAttrType
go_pango_attr_superscript_get_type(void)691 go_pango_attr_superscript_get_type (void)
692 {
693 return go_pango_attr_superscript_get_attr_type ();
694 }
695
696 static PangoAttribute *
go_pango_attr_subscript_copy(PangoAttribute const * attr)697 go_pango_attr_subscript_copy (PangoAttribute const *attr)
698 {
699 GOPangoAttrSubscript *at = (GOPangoAttrSubscript *)attr;
700 return go_pango_attr_subscript_new (at->val);
701 }
702
703 static PangoAttribute *
go_pango_attr_superscript_copy(PangoAttribute const * attr)704 go_pango_attr_superscript_copy (PangoAttribute const *attr)
705 {
706 GOPangoAttrSuperscript *at = (GOPangoAttrSuperscript *)attr;
707 return go_pango_attr_superscript_new (at->val);
708 }
709
710 static void
go_pango_attr_destroy(PangoAttribute * attr)711 go_pango_attr_destroy (PangoAttribute *attr)
712 {
713 g_free (attr);
714 }
715
716 static gboolean
go_pango_attr_superscript_compare(PangoAttribute const * attr1,PangoAttribute const * attr2)717 go_pango_attr_superscript_compare (PangoAttribute const *attr1,
718 PangoAttribute const *attr2)
719 {
720 GOPangoAttrSuperscript *at1 = (GOPangoAttrSuperscript *)attr1;
721 GOPangoAttrSuperscript *at2 = (GOPangoAttrSuperscript *)attr2;
722 return (at1->val == at2->val);
723 }
724
725 static gboolean
go_pango_attr_subscript_compare(PangoAttribute const * attr1,PangoAttribute const * attr2)726 go_pango_attr_subscript_compare (PangoAttribute const *attr1,
727 PangoAttribute const *attr2)
728 {
729 GOPangoAttrSubscript *at1 = (GOPangoAttrSubscript *)attr1;
730 GOPangoAttrSubscript *at2 = (GOPangoAttrSubscript *)attr2;
731 return (at1->val == at2->val);
732 }
733
734 /**
735 * go_pango_attr_subscript_new: (skip)
736 * @val: %TRUE if the characters must be lowered.
737 *
738 * Returns: (transfer full): a subscript attribute.
739 **/
740 PangoAttribute *
go_pango_attr_subscript_new(gboolean val)741 go_pango_attr_subscript_new (gboolean val)
742 {
743 GOPangoAttrSubscript *result;
744
745 static PangoAttrClass klass = {
746 0,
747 go_pango_attr_subscript_copy,
748 go_pango_attr_destroy,
749 go_pango_attr_subscript_compare
750 };
751
752 if (!klass.type)
753 klass.type = go_pango_attr_subscript_get_attr_type ();
754
755 result = g_new (GOPangoAttrSubscript, 1);
756 result->attr.klass = &klass;
757 result->val = val;
758
759 return (PangoAttribute *) result;
760 }
761
762 /**
763 * go_pango_attr_superscript_new: (skip)
764 * @val: %TRUE if the characters must be raised.
765 *
766 * Returns: (transfer full): a superscript attribute.
767 **/
768 PangoAttribute *
go_pango_attr_superscript_new(gboolean val)769 go_pango_attr_superscript_new (gboolean val)
770 {
771 GOPangoAttrSuperscript *result;
772
773 static PangoAttrClass klass = {
774 0,
775 go_pango_attr_superscript_copy,
776 go_pango_attr_destroy,
777 go_pango_attr_superscript_compare
778 };
779
780 if (!klass.type)
781 klass.type = go_pango_attr_superscript_get_attr_type ();
782
783 result = g_new (GOPangoAttrSuperscript, 1);
784 result->attr.klass = &klass;
785 result->val = val;
786
787 return (PangoAttribute *) result;
788 }
789
790 static int
go_pango_attr_as_markup_string(PangoAttribute * a,GString * gstr)791 go_pango_attr_as_markup_string (PangoAttribute *a, GString *gstr)
792 {
793 int spans = 0;
794
795 switch (a->klass->type) {
796 case PANGO_ATTR_FONT_DESC :
797 {
798 char *str = pango_font_description_to_string
799 (((PangoAttrFontDesc *)a)->desc);
800 spans += 1;
801 g_string_append_printf (gstr, "<span font_desc=\"%s\">", str);
802 g_free (str);
803 }
804 break;
805 case PANGO_ATTR_FAMILY :
806 spans += 1;
807 g_string_append_printf (gstr, "<span font_family=\"%s\">",
808 ((PangoAttrString *)a)->value);
809 break;
810 case PANGO_ATTR_ABSOLUTE_SIZE :
811 case PANGO_ATTR_SIZE :
812 spans += 1;
813 g_string_append_printf (gstr, "<span font_size=\"%i\">",
814 ((PangoAttrSize *)a)->size);
815 break;
816 case PANGO_ATTR_RISE:
817 spans += 1;
818 g_string_append_printf (gstr, "<span rise=\"%i\">",
819 ((PangoAttrInt *)a)->value);
820 break;
821 case PANGO_ATTR_STYLE :
822 spans += 1;
823 switch (((PangoAttrInt *)a)->value) {
824 case PANGO_STYLE_ITALIC:
825 g_string_append (gstr, "<span font_style=\"italic\">");
826 break;
827 case PANGO_STYLE_OBLIQUE:
828 g_string_append (gstr, "<span font_style=\"oblique\">");
829 break;
830 case PANGO_STYLE_NORMAL:
831 default:
832 g_string_append (gstr, "<span font_style=\"normal\">");
833 break;
834 }
835 break;
836 case PANGO_ATTR_WEIGHT :
837 spans += 1;
838 g_string_append_printf (gstr, "<span font_weight=\"%i\">",
839 ((PangoAttrInt *)a)->value);
840 break;
841 case PANGO_ATTR_STRIKETHROUGH :
842 spans += 1;
843 if (((PangoAttrInt *)a)->value)
844 g_string_append (gstr, "<span strikethrough=\"true\">");
845 else
846 g_string_append (gstr, "<span strikethrough=\"false\">");
847 break;
848 case PANGO_ATTR_UNDERLINE :
849 spans += 1;
850 switch (((PangoAttrInt *)a)->value) {
851 case PANGO_UNDERLINE_SINGLE:
852 g_string_append (gstr, "<span underline=\"single\">");
853 break;
854 case PANGO_UNDERLINE_DOUBLE:
855 g_string_append (gstr, "<span underline=\"double\">");
856 break;
857 case PANGO_UNDERLINE_LOW:
858 g_string_append (gstr, "<span underline=\"low\">");
859 break;
860 case PANGO_UNDERLINE_ERROR:
861 g_string_append (gstr, "<span underline=\"error\">");
862 break;
863 case PANGO_UNDERLINE_NONE:
864 default:
865 g_string_append (gstr, "<span underline=\"none\">");
866 break;
867 }
868 break;
869 case PANGO_ATTR_LANGUAGE :
870 spans += 1;
871 g_string_append_printf (gstr, "<span lang=\"%s\">",
872 pango_language_to_string (((PangoAttrLanguage *)a)->value));
873 break;
874 case PANGO_ATTR_VARIANT :
875 spans += 1;
876 if (((PangoAttrInt *)a)->value == PANGO_VARIANT_NORMAL)
877 g_string_append (gstr, "<span font_variant=\"normal\">");
878 else
879 g_string_append (gstr, "<span font_variant=\"smallcaps\">");
880 break;
881 case PANGO_ATTR_LETTER_SPACING :
882 spans += 1;
883 g_string_append_printf (gstr, "<span letter_spacing=\"%i\">",
884 ((PangoAttrInt *)a)->value);
885 break;
886 case PANGO_ATTR_FALLBACK :
887 spans += 1;
888 if (((PangoAttrInt *)a)->value)
889 g_string_append (gstr, "<span fallback=\"true\">");
890 else
891 g_string_append (gstr, "<span fallback=\"false\">");
892 break;
893 case PANGO_ATTR_STRETCH :
894 spans += 1;
895 switch (((PangoAttrInt *)a)->value) {
896 case PANGO_STRETCH_ULTRA_CONDENSED:
897 g_string_append (gstr, "<span font_stretch=\"ultracondensed\">");
898 break;
899 case PANGO_STRETCH_EXTRA_CONDENSED:
900 g_string_append (gstr, "<span font_stretch=\"extracondensed\">");
901 break;
902 case PANGO_STRETCH_CONDENSED:
903 g_string_append (gstr, "<span font_stretch=\"condensed\">");
904 break;
905 case PANGO_STRETCH_SEMI_CONDENSED:
906 g_string_append (gstr, "<span font_stretch=\"semicondensed\">");
907 break;
908 case PANGO_STRETCH_SEMI_EXPANDED:
909 g_string_append (gstr, "<span font_stretch=\"semiexpanded\">");
910 break;
911 case PANGO_STRETCH_EXPANDED:
912 g_string_append (gstr, "<span font_stretch=\"expanded\">");
913 break;
914 case PANGO_STRETCH_EXTRA_EXPANDED:
915 g_string_append (gstr, "<span font_stretch=\"extraexpanded\">");
916 break;
917 case PANGO_STRETCH_ULTRA_EXPANDED:
918 g_string_append (gstr, "<span font_stretch=\"ultraexpanded\">");
919 break;
920 case PANGO_STRETCH_NORMAL:
921 default:
922 g_string_append (gstr, "<span font_stretch=\"normal\">");
923 break;
924 }
925 break;
926 case PANGO_ATTR_GRAVITY :
927 spans += 1;
928 switch (((PangoAttrInt *)a)->value) {
929 case PANGO_GRAVITY_SOUTH:
930 g_string_append (gstr, "<span gravity=\"south\">");
931 break;
932 case PANGO_GRAVITY_EAST:
933 g_string_append (gstr, "<span gravity=\"east\">");
934 break;
935 case PANGO_GRAVITY_NORTH:
936 g_string_append (gstr, "<span gravity=\"north\">");
937 break;
938 case PANGO_GRAVITY_WEST:
939 g_string_append (gstr, "<span gravity=\"west\">");
940 break;
941 case PANGO_GRAVITY_AUTO:
942 default:
943 g_string_append (gstr, "<span gravity=\"auto\">");
944 break;
945 }
946 break;
947 case PANGO_ATTR_GRAVITY_HINT :
948 spans += 1;
949 switch (((PangoAttrInt *)a)->value) {
950 case PANGO_GRAVITY_HINT_LINE:
951 g_string_append (gstr, "<span gravity_hint=\"line\">");
952 break;
953 case PANGO_GRAVITY_HINT_STRONG:
954 g_string_append (gstr, "<span gravity_hint=\"strong\">");
955 break;
956 case PANGO_GRAVITY_HINT_NATURAL:
957 default:
958 g_string_append (gstr, "<span gravity_hint=\"natural\">");
959 break;
960 }
961 break;
962
963 case PANGO_ATTR_FOREGROUND :
964 {
965 PangoColor *color = &((PangoAttrColor *)a)->color;
966 spans += 1;
967 g_string_append_printf (gstr, "<span foreground=\"#%02X%02X%02X\">",
968 color->red, color->green, color->blue);
969 }
970 break;
971 case PANGO_ATTR_BACKGROUND :
972 {
973 PangoColor *color = &((PangoAttrColor *)a)->color;
974 spans += 1;
975 g_string_append_printf (gstr, "<span background=\"#%02X%02X%02X\">",
976 color->red, color->green, color->blue);
977 }
978 break;
979 case PANGO_ATTR_UNDERLINE_COLOR :
980 {
981 PangoColor *color = &((PangoAttrColor *)a)->color;
982 spans += 1;
983 g_string_append_printf (gstr, "<span underline_color=\"#%02X%02X%02X\">",
984 color->red, color->green, color->blue);
985 }
986 break;
987 case PANGO_ATTR_STRIKETHROUGH_COLOR :
988 {
989 PangoColor *color = &((PangoAttrColor *)a)->color;
990 spans += 1;
991 g_string_append_printf (gstr, "<span strikethrough_color=\"#%02X%02X%02X\">",
992 color->red, color->green, color->blue);
993 }
994 break;
995
996 case PANGO_ATTR_SCALE :
997 case PANGO_ATTR_SHAPE :
998 default :
999 break; /* ignored */
1000 }
1001
1002 return spans;
1003 }
1004
1005 char *
go_pango_attrs_to_markup(PangoAttrList * attrs,char const * text)1006 go_pango_attrs_to_markup (PangoAttrList *attrs, char const *text)
1007 {
1008 PangoAttrIterator * iter;
1009 int handled = 0;
1010 int from, to;
1011 int len;
1012 GString *gstr;
1013
1014 if (text == NULL)
1015 return NULL;
1016 if (attrs == NULL || go_pango_attr_list_is_empty (attrs))
1017 return g_strdup (text);
1018
1019 len = strlen (text);
1020 gstr = g_string_sized_new (len + 1);
1021
1022 iter = pango_attr_list_get_iterator (attrs);
1023 do {
1024 GSList *list, *l;
1025 int spans = 0;
1026
1027 pango_attr_iterator_range (iter, &from, &to);
1028 to = (to > len) ? len : to; /* Since "to" can be really big! */
1029 from = (from > len) ? len : from; /* Since "from" can also be really big! */
1030 if (from > handled)
1031 g_string_append_len (gstr, text + handled, from - handled);
1032 list = pango_attr_iterator_get_attrs (iter);
1033 for (l = list; l != NULL; l = l->next)
1034 spans += go_pango_attr_as_markup_string (l->data, gstr);
1035 g_slist_free (list);
1036 if (to > from)
1037 g_string_append_len (gstr, text + from, to - from);
1038 while (spans-- > 0)
1039 g_string_append (gstr, "</span>");
1040 handled = to;
1041 } while (pango_attr_iterator_next (iter));
1042
1043 pango_attr_iterator_destroy (iter);
1044
1045 return g_string_free (gstr, FALSE);
1046 }
1047
1048
1049 /**
1050 * go_pango_measure_string:
1051 * @context: #PangoContext
1052 * @font_desc: #PangoFontDescription
1053 * @str: The text to measure.
1054 *
1055 * A utility function to measure text.
1056 *
1057 * Returns: the pixel length of @str according to @context.
1058 **/
1059 int
go_pango_measure_string(PangoContext * context,PangoFontDescription const * font_desc,char const * str)1060 go_pango_measure_string (PangoContext *context,
1061 PangoFontDescription const *font_desc,
1062 char const *str)
1063 {
1064 PangoLayout *layout = pango_layout_new (context);
1065 int width;
1066
1067 pango_layout_set_text (layout, str, -1);
1068 pango_layout_set_font_description (layout, font_desc);
1069 pango_layout_get_pixel_size (layout, &width, NULL);
1070
1071 g_object_unref (layout);
1072
1073 return width;
1074 }
1075