1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /* This file is part of the GtkHTML library.
3  *
4  * Copyright (C) 1997 Martin Jones (mjones@kde.org)
5  * Copyright (C) 1997 Torben Weis (weis@kde.org)
6  * Copyright (C) 1999, 2000 Helix Code, Inc.
7  * Copyright (C) 2000, 2001, 2002, 2003 Ximian, Inc.
8  *
9  * This library is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Library General Public
11  * License as published by the Free Software Foundation; either
12  * version 2 of the License, or (at your option) any later version.
13  *
14  * This library is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17  * Library General Public License for more details.
18  *
19  * You should have received a copy of the GNU Library General Public License
20  * along with this library; see the file COPYING.LIB.  If not, write to
21  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
22  * Boston, MA 02110-1301, USA.
23 */
24 
25 #include <config.h>
26 #include <string.h> /* strcmp */
27 #include <stdlib.h>
28 #include "gtkhtml-compat.h"
29 
30 #include "htmlcolor.h"
31 #include "htmlcolorset.h"
32 #include "htmlentity.h"
33 #include "htmltext.h"
34 #include "htmltextslave.h"
35 #include "htmlpainter.h"
36 
37 
38 /* Convenience macro to extract the HTMLPainterClass from a GTK+ object.  */
39 #define HP_CLASS(obj)					\
40 	HTML_PAINTER_CLASS (G_OBJECT_GET_CLASS (obj))
41 
42 /* Our parent class.  */
43 static GObjectClass *parent_class = NULL;
44 
45 
46 /* GObject methods.  */
47 
48 static void
finalize(GObject * object)49 finalize (GObject *object)
50 {
51 	HTMLPainter *painter;
52 
53 	painter = HTML_PAINTER (object);
54 	html_font_manager_finalize (&painter->font_manager);
55 
56 	g_free (painter->font_face);
57 
58 	if (painter->pango_context)
59 		g_object_unref (painter->pango_context);
60 
61 	/* FIXME ownership of the color set?  */
62 
63 	(* G_OBJECT_CLASS (parent_class)->finalize) (object);
64 
65 	if (painter->widget) {
66 		g_object_unref (painter->widget);
67 		painter->widget = NULL;
68 	}
69 }
70 
71 
72 #define DEFINE_UNIMPLEMENTED(method)						\
73 	static gint								\
74 	method##_unimplemented (GObject *obj)					\
75 	{									\
76 		g_warning ("Class `%s' does not implement `" #method "'\n",	\
77 			   g_type_name (G_TYPE_FROM_INSTANCE (obj)));		\
78 		return 0;							\
79 	}
80 
81 DEFINE_UNIMPLEMENTED (begin)
DEFINE_UNIMPLEMENTED(end)82 DEFINE_UNIMPLEMENTED (end)
83 
84 DEFINE_UNIMPLEMENTED (alloc_color)
85 DEFINE_UNIMPLEMENTED (free_color)
86 
87 DEFINE_UNIMPLEMENTED (set_pen)
88 DEFINE_UNIMPLEMENTED (get_black)
89 DEFINE_UNIMPLEMENTED (draw_line)
90 DEFINE_UNIMPLEMENTED (draw_rect)
91 DEFINE_UNIMPLEMENTED (draw_glyphs)
92 DEFINE_UNIMPLEMENTED (draw_spell_error)
93 DEFINE_UNIMPLEMENTED (fill_rect)
94 DEFINE_UNIMPLEMENTED (draw_pixmap)
95 DEFINE_UNIMPLEMENTED (draw_ellipse)
96 DEFINE_UNIMPLEMENTED (clear)
97 DEFINE_UNIMPLEMENTED (set_background_color)
98 DEFINE_UNIMPLEMENTED (draw_shade_line)
99 DEFINE_UNIMPLEMENTED (draw_border)
100 
101 DEFINE_UNIMPLEMENTED (set_clip_rectangle)
102 DEFINE_UNIMPLEMENTED (draw_background)
103 DEFINE_UNIMPLEMENTED (draw_embedded)
104 
105 DEFINE_UNIMPLEMENTED (get_pixel_size)
106 DEFINE_UNIMPLEMENTED (get_page_width)
107 DEFINE_UNIMPLEMENTED (get_page_height)
108 
109 static void
110 html_painter_init (GObject *object,
111                    HTMLPainterClass *real_klass)
112 {
113 	HTMLPainter *painter;
114 
115 	painter = HTML_PAINTER (object);
116 	html_font_manager_init (&painter->font_manager, painter);
117 	painter->font_style = GTK_HTML_FONT_STYLE_DEFAULT;
118 	painter->font_face = NULL;
119 	painter->widget = NULL;
120 	painter->clip_width = painter->clip_height = 0;
121 }
122 
123 static void
html_painter_real_set_widget(HTMLPainter * painter,GtkWidget * widget)124 html_painter_real_set_widget (HTMLPainter *painter,
125                               GtkWidget *widget)
126 {
127 	if (painter->widget)
128 		g_object_unref (painter->widget);
129 	painter->widget = widget;
130 	g_object_ref (widget);
131 }
132 
133 static gint
text_width(HTMLPainter * painter,PangoFontDescription * desc,const gchar * text,gint bytes)134 text_width (HTMLPainter *painter,
135             PangoFontDescription *desc,
136             const gchar *text,
137             gint bytes)
138 {
139 	HTMLTextPangoInfo *pi;
140 	GList *glyphs;
141 	gint width = 0;
142 
143 	pi = html_painter_text_itemize_and_prepare_glyphs (painter, desc, text, bytes, &glyphs, NULL);
144 
145 	if (pi && glyphs) {
146 		GList *list;
147 		gint i;
148 		for (list = glyphs; list; list = list->next->next) {
149 			PangoGlyphString *str = (PangoGlyphString *) list->data;
150 			for (i = 0; i < str->num_glyphs; i++)
151 				width += str->glyphs[i].geometry.width;
152 		}
153 	}
154 	if (glyphs)
155 		html_painter_glyphs_destroy (glyphs);
156 	if (pi)
157 		html_text_pango_info_destroy (pi);
158 	/* printf ("text_width %d\n", PANGO_PIXELS (width)); */
159 	return html_painter_pango_to_engine (painter, width);
160 }
161 
162 static void
text_size(HTMLPainter * painter,PangoFontDescription * desc,const gchar * text,gint bytes,HTMLTextPangoInfo * pi,GList * glyphs,gint * width_out,gint * ascent_out,gint * descent_out)163 text_size (HTMLPainter *painter,
164            PangoFontDescription *desc,
165            const gchar *text,
166            gint bytes,
167            HTMLTextPangoInfo *pi,
168            GList *glyphs,
169            gint *width_out,
170            gint *ascent_out,
171            gint *descent_out)
172 {
173 	gboolean temp_pi = FALSE;
174 	gint ascent = 0;
175 	gint descent = 0;
176 	gint width = 0;
177 
178 	if (!pi) {
179 		pi = html_painter_text_itemize_and_prepare_glyphs (painter, desc, text, bytes, &glyphs, NULL);
180 		temp_pi = TRUE;
181 	}
182 
183 	if (pi && pi->n && glyphs) {
184 		GList *gl;
185 		PangoRectangle log_rect;
186 		PangoItem *item;
187 		PangoGlyphString *str;
188 		const gchar *c_text = text;
189 		gint c_bytes, ii;
190 
191 		c_bytes = 0;
192 		for (gl = glyphs; gl && c_bytes < bytes; gl = gl->next) {
193 			str = (PangoGlyphString *) gl->data;
194 			gl = gl->next;
195 			ii = GPOINTER_TO_INT (gl->data);
196 			item = pi->entries[ii].glyph_item.item;
197 			pango_glyph_string_extents (str, item->analysis.font, NULL, &log_rect);
198 			width += log_rect.width;
199 
200 			if (ascent_out || descent_out) {
201 				PangoFontMetrics *pfm;
202 
203 				pfm = pango_font_get_metrics (item->analysis.font, item->analysis.language);
204 				ascent = MAX (ascent, pango_font_metrics_get_ascent (pfm));
205 				descent = MAX (descent, pango_font_metrics_get_descent (pfm));
206 				pango_font_metrics_unref (pfm);
207 			}
208 
209 			c_text = g_utf8_offset_to_pointer (c_text, str->num_glyphs);
210 			if (*text == '\t')
211 				c_text++;
212 			c_bytes = c_text - text;
213 		}
214 	}
215 
216 	if (width_out)
217 		*width_out = html_painter_pango_to_engine (painter, width);
218 	if (ascent_out)
219 		*ascent_out = html_painter_pango_to_engine (painter, ascent);
220 	if (descent_out)
221 		*descent_out = html_painter_pango_to_engine (painter, descent);
222 
223 	if (temp_pi) {
224 		if (glyphs)
225 			html_painter_glyphs_destroy (glyphs);
226 		if (pi)
227 			html_text_pango_info_destroy (pi);
228 	}
229 }
230 
231 static void
html_painter_class_init(GObjectClass * object_class)232 html_painter_class_init (GObjectClass *object_class)
233 {
234 	HTMLPainterClass *class;
235 
236 	class = HTML_PAINTER_CLASS (object_class);
237 
238 	object_class->finalize = finalize;
239 	parent_class = g_type_class_ref (G_TYPE_OBJECT);
240 
241 	class->set_widget = html_painter_real_set_widget;
242 	class->begin = (gpointer) begin_unimplemented;
243 	class->end = (gpointer) end_unimplemented;
244 
245 	class->alloc_color = (gpointer) alloc_color_unimplemented;
246 	class->free_color = (gpointer) free_color_unimplemented;
247 
248 	class->set_pen = (gpointer) set_pen_unimplemented;
249 	class->get_black = (gpointer) get_black_unimplemented;
250 	class->draw_line = (gpointer) draw_line_unimplemented;
251 	class->draw_rect = (gpointer) draw_rect_unimplemented;
252 	class->draw_glyphs = (gpointer) draw_glyphs_unimplemented;
253 	class->draw_spell_error = (gpointer) draw_spell_error_unimplemented;
254 	class->fill_rect = (gpointer) fill_rect_unimplemented;
255 	class->draw_pixmap = (gpointer) draw_pixmap_unimplemented;
256 	class->draw_ellipse = (gpointer) draw_ellipse_unimplemented;
257 	class->clear = (gpointer) clear_unimplemented;
258 	class->set_background_color = (gpointer) set_background_color_unimplemented;
259 	class->draw_shade_line = (gpointer) draw_shade_line_unimplemented;
260 	class->draw_border = (gpointer) draw_border_unimplemented;
261 
262 	class->set_clip_rectangle = (gpointer) set_clip_rectangle_unimplemented;
263 	class->draw_background = (gpointer) draw_background_unimplemented;
264 	class->draw_embedded = (gpointer) draw_embedded_unimplemented;
265 
266 	class->get_pixel_size = (gpointer) get_pixel_size_unimplemented;
267 
268 	class->get_page_width  = (gpointer) get_page_width_unimplemented;
269 	class->get_page_height = (gpointer) get_page_height_unimplemented;
270 }
271 
272 GType
html_painter_get_type(void)273 html_painter_get_type (void)
274 {
275 	static GType html_painter_type = 0;
276 
277 	if (html_painter_type == 0) {
278 		static const GTypeInfo html_painter_info = {
279 			sizeof (HTMLPainterClass),
280 			NULL,
281 			NULL,
282 			(GClassInitFunc) html_painter_class_init,
283 			NULL,
284 			NULL,
285 			sizeof (HTMLPainter),
286 			1,
287 			(GInstanceInitFunc) html_painter_init,
288 		};
289 		html_painter_type = g_type_register_static (G_TYPE_OBJECT, "HTMLPainter", &html_painter_info, 0);
290 	}
291 
292 	return html_painter_type;
293 }
294 
295 /* Functions to begin/end a painting process.  */
296 
297 void
html_painter_begin(HTMLPainter * painter,gint x1,gint y1,gint x2,gint y2)298 html_painter_begin (HTMLPainter *painter,
299                     gint x1,
300                     gint y1,
301                     gint x2,
302                     gint y2)
303 {
304 	g_return_if_fail (painter != NULL);
305 	g_return_if_fail (HTML_IS_PAINTER (painter));
306 
307 	painter->clip_height = painter->clip_width = 0;
308 
309 	(* HP_CLASS (painter)->begin) (painter, x1, y1, x2, y2);
310 }
311 
312 void
html_painter_end(HTMLPainter * painter)313 html_painter_end (HTMLPainter *painter)
314 {
315 	g_return_if_fail (painter != NULL);
316 	g_return_if_fail (HTML_IS_PAINTER (painter));
317 
318 	(* HP_CLASS (painter)->end) (painter);
319 }
320 
321 
322 /* Color control.  */
323 void
html_painter_alloc_color(HTMLPainter * painter,GdkColor * color)324 html_painter_alloc_color (HTMLPainter *painter,
325                           GdkColor *color)
326 {
327 	g_return_if_fail (painter != NULL);
328 	g_return_if_fail (HTML_IS_PAINTER (painter));
329 	g_return_if_fail (color != NULL);
330 
331 	(* HP_CLASS (painter)->alloc_color) (painter, color);
332 }
333 
334 void
html_painter_free_color(HTMLPainter * painter,GdkColor * color)335 html_painter_free_color (HTMLPainter *painter,
336                          GdkColor *color)
337 {
338 	g_return_if_fail (painter != NULL);
339 	g_return_if_fail (HTML_IS_PAINTER (painter));
340 	g_return_if_fail (color != NULL);
341 
342 	(* HP_CLASS (painter)->free_color) (painter, color);
343 }
344 
345 
346 /* Font handling.  */
347 
348 void
html_painter_set_font_style(HTMLPainter * painter,GtkHTMLFontStyle font_style)349 html_painter_set_font_style (HTMLPainter *painter,
350                              GtkHTMLFontStyle font_style)
351 {
352 	g_return_if_fail (painter != NULL);
353 	g_return_if_fail (HTML_IS_PAINTER (painter));
354 	g_return_if_fail (font_style != GTK_HTML_FONT_STYLE_DEFAULT);
355 
356 	painter->font_style = font_style;
357 }
358 
359 GtkHTMLFontStyle
html_painter_get_font_style(HTMLPainter * painter)360 html_painter_get_font_style (HTMLPainter *painter)
361 {
362 	g_return_val_if_fail (painter != NULL, GTK_HTML_FONT_STYLE_DEFAULT);
363 	g_return_val_if_fail (HTML_IS_PAINTER (painter), GTK_HTML_FONT_STYLE_DEFAULT);
364 
365 	return painter->font_style;
366 }
367 
368 void
html_painter_set_font_face(HTMLPainter * painter,HTMLFontFace * face)369 html_painter_set_font_face (HTMLPainter *painter,
370                             HTMLFontFace *face)
371 {
372 	g_return_if_fail (painter != NULL);
373 	g_return_if_fail (HTML_IS_PAINTER (painter));
374 
375 	if (!painter->font_face || !face || strcmp (painter->font_face, face)) {
376 		g_free (painter->font_face);
377 		painter->font_face = g_strdup (face);
378 	}
379 }
380 
381 gpointer
html_painter_get_font(HTMLPainter * painter,HTMLFontFace * face,GtkHTMLFontStyle style)382 html_painter_get_font (HTMLPainter *painter,
383                        HTMLFontFace *face,
384                        GtkHTMLFontStyle style)
385 {
386 	HTMLFont *font;
387 
388 	font = html_font_manager_get_font (&painter->font_manager, face, style);
389 	return font ? font->data : NULL;
390 }
391 
392 static void
get_font_info(HTMLPainter * painter,HTMLTextPangoInfo * pi,HTMLFontFace ** font_face,GtkHTMLFontStyle * font_style)393 get_font_info (HTMLPainter *painter,
394                HTMLTextPangoInfo *pi,
395                HTMLFontFace **font_face,
396                GtkHTMLFontStyle *font_style)
397 {
398 	if (pi && pi->have_font) {
399 		*font_face = pi->face;
400 		*font_style = pi->font_style;
401 	} else {
402 		*font_face = painter->font_face;
403 		*font_style = painter->font_style;
404 	}
405 }
406 
407 static gint
get_space_width(HTMLPainter * painter,HTMLTextPangoInfo * pi)408 get_space_width (HTMLPainter *painter,
409                  HTMLTextPangoInfo *pi)
410 {
411 	HTMLFontFace    *font_face;
412 	GtkHTMLFontStyle font_style;
413 
414 	get_font_info (painter, pi, &font_face, &font_style);
415 
416 	return html_painter_get_space_width (painter, font_style, font_face);
417 }
418 
419 /**
420  * html_painter_calc_entries_size:
421  * @painter: a #HTMLPainter
422  * @text: text to compute size of
423  * @len: length of text, in characters
424  * @pi: #HTMLTextPangoInfo structure holding information about
425  *   the text. (It may be for a larger range of text that includes
426  *   the range specified by @text and @len.)
427  * @glyphs: list holding information about segments of text to draw.
428  *   There is one segment for each run of non-tab characters. The
429  *   list is structure as a series of pairs of PangoGlyphString,
430  *   GINT_TO_POINTER(item_index), where item_index is an index
431  *   within pi->entries[].
432  * @line_offset: location to store column offset in output, used
433  *   for tab display. On entry holds the the column offset for the
434  *   first character in @text. On exit, updated to hold the character
435  *   offset for the first character after the end of the text.
436  *   If %NULL is passed, then tabs are disabled and substituted with spaces.
437  * @width: location to store width of text (in engine coordinates)
438  * @asc: location to store ascent of text (in engine coordinates)
439  * @dsc: location to store descent of text (in engine coordinates)
440  *
441  * Computes size information for a piece of text, using provided Pango
442  * layout information.
443  **/
444 void
html_painter_calc_entries_size(HTMLPainter * painter,const gchar * text,guint len,HTMLTextPangoInfo * pi,GList * glyphs,gint * line_offset,gint * width,gint * asc,gint * dsc)445 html_painter_calc_entries_size (HTMLPainter *painter,
446                                 const gchar *text,
447                                 guint len,
448                                 HTMLTextPangoInfo *pi,
449                                 GList *glyphs,
450                                 gint *line_offset,
451                                 gint *width,
452                                 gint *asc,
453                                 gint *dsc)
454 {
455 	HTMLFontFace *font_face = NULL;
456 	GtkHTMLFontStyle font_style = GTK_HTML_FONT_STYLE_DEFAULT;
457 	HTMLFont *font;
458 
459 	g_return_if_fail (painter != NULL);
460 	g_return_if_fail (HTML_IS_PAINTER (painter));
461 	g_return_if_fail (text != NULL);
462 
463 	if (line_offset || !pi) {
464 		get_font_info (painter, pi, &font_face, &font_style);
465 		font = html_font_manager_get_font (&painter->font_manager, font_face, font_style);
466 	} else
467 		font = NULL;
468 
469 	text_size (painter, (PangoFontDescription *) font->data, text, g_utf8_offset_to_pointer (text, len) - text,
470 		   pi, glyphs, width, asc, dsc);
471 	/* g_print ("calc_text_size %s %d %d %d\n", text, *width, asc ? *asc : -1, dsc ? *dsc : -1); */
472 
473 	if (line_offset) {
474 		gint space_width = html_painter_get_space_width (painter, font_style, font_face);
475 		gint tabs;
476 
477 		*width += (html_text_text_line_length (text, line_offset, len, &tabs) - len + tabs)*space_width;
478 	}
479 }
480 
481 /**
482  * html_painter_calc_text_size:
483  * @painter: a #HTMLPainter
484  * @text: text to compute size of
485  * @len: length of text, in characters
486  * @width: location to store width of text (in engine coordinates)
487  * @asc: location to store ascent of text (in engine coordinates)
488  * @dsc: location to store descent of text (in engine coordinates)
489  *
490  * Computes size information for a piece of text.
491  **/
492 void
html_painter_calc_text_size(HTMLPainter * painter,const gchar * text,guint len,gint * width,gint * asc,gint * dsc)493 html_painter_calc_text_size (HTMLPainter *painter,
494                              const gchar *text,
495                              guint len,
496                              gint *width,
497                              gint *asc,
498                              gint *dsc)
499 {
500 	gint line_offset = 0;
501 
502 	g_return_if_fail (painter != NULL);
503 	g_return_if_fail (HTML_IS_PAINTER (painter));
504 	g_return_if_fail (text != NULL);
505 
506 	html_painter_calc_entries_size (painter, text, len, NULL, NULL, &line_offset,
507 					width, asc, dsc);
508 }
509 
510 /* The actual paint operations.  */
511 
512 void
html_painter_set_pen(HTMLPainter * painter,const GdkColor * color)513 html_painter_set_pen (HTMLPainter *painter,
514                       const GdkColor *color)
515 {
516 	g_return_if_fail (painter != NULL);
517 	g_return_if_fail (HTML_IS_PAINTER (painter));
518 	g_return_if_fail (color != NULL);
519 
520 	(* HP_CLASS (painter)->set_pen) (painter, color);
521 }
522 
523 void
html_painter_draw_line(HTMLPainter * painter,gint x1,gint y1,gint x2,gint y2)524 html_painter_draw_line (HTMLPainter *painter,
525                         gint x1,
526                         gint y1,
527                         gint x2,
528                         gint y2)
529 {
530 	g_return_if_fail (painter != NULL);
531 	g_return_if_fail (HTML_IS_PAINTER (painter));
532 
533 	(* HP_CLASS (painter)->draw_line) (painter, x1, y1, x2, y2);
534 }
535 
536 void
html_painter_draw_rect(HTMLPainter * painter,gint x,gint y,gint width,gint height)537 html_painter_draw_rect (HTMLPainter *painter,
538                         gint x,
539                         gint y,
540                         gint width,
541                         gint height)
542 {
543 	g_return_if_fail (painter != NULL);
544 	g_return_if_fail (HTML_IS_PAINTER (painter));
545 
546 	(* HP_CLASS (painter)->draw_rect) (painter, x, y, width, height);
547 }
548 
549 void
html_replace_tabs(const gchar * text,gchar * translated,guint bytes)550 html_replace_tabs (const gchar *text,
551                    gchar *translated,
552                    guint bytes)
553 {
554 	const gchar *t, *tab;
555 	gchar *tt;
556 
557 	t = text;
558 	tt = translated;
559 
560 	do {
561 		tab = memchr (t, (guchar) '\t', bytes - (t - text));
562 		if (tab) {
563 			strncpy (tt, t, tab - t);
564 			tt += tab - t;
565 			*tt = ' ';
566 			tt++;
567 			t = tab + 1;
568 		} else
569 			strncpy (tt, t, bytes - (t - text));
570 	} while (tab);
571 }
572 
573 /**
574  * html_painter_draw_entries:
575  * @painter: a #HTMLPainter
576  * @x: x coordinate at which to draw text, in engine coordinates
577  * @y: x coordinate at which to draw text, in engine coordinates
578  * @text: text to draw
579  * @len: length of text, in characters
580  * @pi: #HTMLTextPangoInfo structure holding information about
581  *   the text. (It may be for a larger range of text that includes
582  *   the range specified by @text and @len.)
583  * @glyphs: list holding information about segments of text to draw.
584  *   There is one segment for each run of non-tab characters. The
585  *   list is structure as a series of pairs of PangoGlyphString,
586  *   GINT_TO_POINTER(item_index), where item_index is an index
587  *   within pi->entries[].
588  * @line_offset: column offset of the first character in @text, used for
589  *  tab display. If set to -1, then tabs are disabled and substituted
590  * with spaces.
591  *
592  * Draws a piece of text, using provided Pango layout information.
593  **/
594 void
html_painter_draw_entries(HTMLPainter * painter,gint x,gint y,const gchar * text,gint len,HTMLTextPangoInfo * pi,GList * glyphs,gint line_offset)595 html_painter_draw_entries (HTMLPainter *painter,
596                            gint x,
597                            gint y,
598                            const gchar *text,
599                            gint len,
600                            HTMLTextPangoInfo *pi,
601                            GList *glyphs,
602                            gint line_offset)
603 {
604 	const gchar *tab, *c_text;
605 	gint bytes;
606 	GList *gl;
607 	gint first_item_offset = -1;
608 	gint space_width = -1;
609 
610 	g_return_if_fail (painter != NULL);
611 	g_return_if_fail (HTML_IS_PAINTER (painter));
612 
613 	c_text = text;
614 	bytes = g_utf8_offset_to_pointer (text, len) - text;
615 	gl = glyphs;
616 	tab = memchr (c_text, (guchar) '\t', bytes);
617 
618 	/* We iterate through the string by tabs and non-tab segments, skipping over
619 	 * the tabs and drawing the non-tab segments.
620 	 *
621 	 * We have one PangoGlyphString for each non-tab segment within each item.
622 	 */
623 	while (gl) {
624 		gint ii = GPOINTER_TO_INT (gl->next->data);
625 		PangoItem *item = pi->entries[ii].glyph_item.item;
626 		const gchar *item_end;
627 		const gchar *next;
628 
629 		if (first_item_offset < 0)
630 			first_item_offset = item->offset;
631 
632 		item_end = text + item->offset - first_item_offset + item->length;
633 
634 		if (*c_text == '\t')
635 			next = c_text + 1;
636 		else if (tab && tab < item_end)
637 			next = tab;
638 		else
639 			next = item_end;
640 
641 		if (*c_text == '\t') {
642 			if (space_width < 0)
643 				space_width = get_space_width (painter, pi);
644 
645 			if (line_offset == -1)
646 				x += space_width;
647 			else {
648 				x += space_width * (8 - (line_offset % 8));
649 				line_offset += 8 - (line_offset % 8);
650 			}
651 
652 			tab = memchr (c_text + 1, (guchar) '\t', bytes - 1);
653 		} else {
654 			x += html_painter_pango_to_engine (painter, (* HP_CLASS (painter)->draw_glyphs) (painter, x, y, item, gl->data, NULL, NULL));
655 
656 			if (line_offset != -1)
657 				line_offset += g_utf8_pointer_to_offset (c_text, next);
658 
659 			gl = gl->next->next;
660 		}
661 
662 		bytes -= next - c_text;
663 		c_text = next;
664 	}
665 }
666 
667 gint
html_painter_draw_glyphs(HTMLPainter * painter,gint x,gint y,PangoItem * item,PangoGlyphString * glyphs,GdkColor * fg,GdkColor * bg)668 html_painter_draw_glyphs (HTMLPainter *painter,
669                           gint x,
670                           gint y,
671                           PangoItem *item,
672                           PangoGlyphString *glyphs,
673                           GdkColor *fg,
674                           GdkColor *bg)
675 {
676 	return (* HP_CLASS (painter)->draw_glyphs) (painter, x, y, item, glyphs, fg, bg);
677 }
678 
679 /**
680  * html_painter_draw_text:
681  * @painter: a #HTMLPainter
682  * @x: x coordinate at which to draw text, in engine coordinates
683  * @y: x coordinate at which to draw text, in engine coordinates
684  * @text: text to draw
685  * @len: length of text, in characters
686  *
687  * Draws a piece of text.
688  **/
689 void
html_painter_draw_text(HTMLPainter * painter,gint x,gint y,const gchar * text,gint len)690 html_painter_draw_text (HTMLPainter *painter,
691                         gint x,
692                         gint y,
693                         const gchar *text,
694                         gint len)
695 {
696 	HTMLTextPangoInfo *pi;
697 	GList *glyphs;
698 	gint blen;
699 
700 	g_return_if_fail (painter != NULL);
701 	g_return_if_fail (HTML_IS_PAINTER (painter));
702 
703 	if (len < 0)
704 		len = g_utf8_strlen (text, -1);
705 
706 	blen = g_utf8_offset_to_pointer (text, len) - text;
707 
708 	pi = html_painter_text_itemize_and_prepare_glyphs (painter, html_painter_get_font (painter, painter->font_face, painter->font_style),
709 							   text, blen, &glyphs, NULL);
710 
711 	html_painter_draw_entries (painter, x, y, text, len, pi, glyphs, 0);
712 
713 	if (glyphs)
714 		html_painter_glyphs_destroy (glyphs);
715 	if (pi)
716 		html_text_pango_info_destroy (pi);
717 }
718 
719 void
html_painter_fill_rect(HTMLPainter * painter,gint x,gint y,gint width,gint height)720 html_painter_fill_rect (HTMLPainter *painter,
721                         gint x,
722                         gint y,
723                         gint width,
724                         gint height)
725 {
726 	g_return_if_fail (painter != NULL);
727 	g_return_if_fail (HTML_IS_PAINTER (painter));
728 
729 	(* HP_CLASS (painter)->fill_rect) (painter, x, y, width, height);
730 }
731 
732 void
html_painter_draw_pixmap(HTMLPainter * painter,GdkPixbuf * pixbuf,gint x,gint y,gint scale_width,gint scale_height,const GdkColor * color)733 html_painter_draw_pixmap (HTMLPainter *painter,
734                           GdkPixbuf *pixbuf,
735                           gint x,
736                           gint y,
737                           gint scale_width,
738                           gint scale_height,
739                           const GdkColor *color)
740 {
741 	g_return_if_fail (painter != NULL);
742 	g_return_if_fail (HTML_IS_PAINTER (painter));
743 	g_return_if_fail (pixbuf != NULL);
744 
745 	(* HP_CLASS (painter)->draw_pixmap) (painter, pixbuf, x, y, scale_width, scale_height, color);
746 }
747 
748 void
html_painter_draw_ellipse(HTMLPainter * painter,gint x,gint y,gint width,gint height)749 html_painter_draw_ellipse (HTMLPainter *painter,
750                            gint x,
751                            gint y,
752                            gint width,
753                            gint height)
754 {
755 	g_return_if_fail (painter != NULL);
756 	g_return_if_fail (HTML_IS_PAINTER (painter));
757 
758 	(* HP_CLASS (painter)->draw_ellipse) (painter, x, y, width, height);
759 }
760 
761 void
html_painter_clear(HTMLPainter * painter)762 html_painter_clear (HTMLPainter *painter)
763 {
764 	g_return_if_fail (painter != NULL);
765 	g_return_if_fail (HTML_IS_PAINTER (painter));
766 
767 	(* HP_CLASS (painter)->clear) (painter);
768 }
769 
770 void
html_painter_set_background_color(HTMLPainter * painter,const GdkColor * color)771 html_painter_set_background_color (HTMLPainter *painter,
772                                    const GdkColor *color)
773 {
774 	g_return_if_fail (painter != NULL);
775 	g_return_if_fail (HTML_IS_PAINTER (painter));
776 	g_return_if_fail (color != NULL);
777 
778 	(* HP_CLASS (painter)->set_background_color) (painter, color);
779 }
780 
781 void
html_painter_draw_shade_line(HTMLPainter * painter,gint x,gint y,gint width)782 html_painter_draw_shade_line (HTMLPainter *painter,
783                               gint x,
784                               gint y,
785                               gint width)
786 {
787 	g_return_if_fail (painter != NULL);
788 	g_return_if_fail (HTML_IS_PAINTER (painter));
789 
790 	(* HP_CLASS (painter)->draw_shade_line) (painter, x, y, width);
791 }
792 
793 void
html_painter_draw_border(HTMLPainter * painter,GdkColor * bg,gint x,gint y,gint width,gint height,HTMLBorderStyle style,gint bordersize)794 html_painter_draw_border (HTMLPainter *painter,
795                           GdkColor *bg,
796                           gint x,
797                           gint y,
798                           gint width,
799                           gint height,
800                           HTMLBorderStyle style,
801                           gint bordersize)
802 {
803 	g_return_if_fail (painter != NULL);
804 	g_return_if_fail (HTML_IS_PAINTER (painter));
805 
806 	(* HP_CLASS (painter)->draw_border) (painter, bg, x, y, width, height, style, bordersize);
807 }
808 
809 void
html_painter_draw_embedded(HTMLPainter * painter,HTMLEmbedded * element,gint x,gint y)810 html_painter_draw_embedded (HTMLPainter *painter,
811                             HTMLEmbedded *element,
812                             gint x,
813                             gint y)
814 {
815 	g_return_if_fail (painter != NULL);
816 	g_return_if_fail (HTML_IS_PAINTER (painter));
817 	g_return_if_fail (element != NULL);
818 
819 	(* HP_CLASS (painter)->draw_embedded) (painter, element, x, y);
820 }
821 
822 /* Passing 0 for width/height means remove clip rectangle */
823 void
html_painter_set_clip_rectangle(HTMLPainter * painter,gint x,gint y,gint width,gint height)824 html_painter_set_clip_rectangle (HTMLPainter *painter,
825                                  gint x,
826                                  gint y,
827                                  gint width,
828                                  gint height)
829 {
830 	g_return_if_fail (painter != NULL);
831 	g_return_if_fail (HTML_IS_PAINTER (painter));
832 
833 	painter->clip_x = x;
834 	painter->clip_y = y;
835 	painter->clip_width = width;
836 	painter->clip_height = height;
837 
838 	/* printf ("clip rect: %d,%d %dx%d\n", x, y, width, height); */
839 
840 	(* HP_CLASS (painter)->set_clip_rectangle) (painter, x, y, width, height);
841 }
842 
843 void
html_painter_get_clip_rectangle(HTMLPainter * painter,gint * x,gint * y,gint * width,gint * height)844 html_painter_get_clip_rectangle (HTMLPainter *painter,
845                                  gint *x,
846                                  gint *y,
847                                  gint *width,
848                                  gint *height)
849 {
850 	*x = painter->clip_x;
851 	*y = painter->clip_y;
852 	*width = painter->clip_width;
853 	*height = painter->clip_height;
854 }
855 
856 /* Passing 0 for pix_width / pix_height makes it use the image width */
857 void
html_painter_draw_background(HTMLPainter * painter,GdkColor * color,GdkPixbuf * pixbuf,gint x,gint y,gint width,gint height,gint tile_x,gint tile_y)858 html_painter_draw_background (HTMLPainter *painter,
859                               GdkColor *color,
860                               GdkPixbuf *pixbuf,
861                               gint x,
862                               gint y,
863                               gint width,
864                               gint height,
865                               gint tile_x,
866                               gint tile_y)
867 {
868 	g_return_if_fail (painter != NULL);
869 	g_return_if_fail (HTML_IS_PAINTER (painter));
870 
871 	(* HP_CLASS (painter)->draw_background) (painter, color, pixbuf, x, y, width, height, tile_x, tile_y);
872 }
873 
874 guint
html_painter_get_pixel_size(HTMLPainter * painter)875 html_painter_get_pixel_size (HTMLPainter *painter)
876 {
877 	g_return_val_if_fail (painter != NULL, 0);
878 	g_return_val_if_fail (HTML_IS_PAINTER (painter), 0);
879 
880 	return (* HP_CLASS (painter)->get_pixel_size) (painter);
881 }
882 
883 gint
html_painter_draw_spell_error(HTMLPainter * painter,gint x,gint y,gint width)884 html_painter_draw_spell_error (HTMLPainter *painter,
885                                gint x,
886                                gint y,
887                                gint width)
888 {
889 	return (* HP_CLASS (painter)->draw_spell_error) (painter, x, y, width);
890 }
891 
892 HTMLFont *
html_painter_alloc_font(HTMLPainter * painter,gchar * face,gdouble size,gboolean points,GtkHTMLFontStyle style)893 html_painter_alloc_font (HTMLPainter *painter,
894                          gchar *face,
895                          gdouble size,
896                          gboolean points,
897                          GtkHTMLFontStyle style)
898 {
899 	PangoFontDescription *desc = NULL;
900 	gint space_width, space_asc, space_dsc;
901 
902 	if (face) {
903 		desc = pango_font_description_from_string (face);
904 		if (points)
905 			pango_font_description_set_size (desc, (gint) size);
906 		else
907 			pango_font_description_set_absolute_size (desc, (gint) size);
908 	}
909 
910 	if (!desc || !pango_font_description_get_family (desc)) {
911 		GtkStyleContext *style_context;
912 		const PangoFontDescription *font_desc;
913 
914 		if (desc)
915 			pango_font_description_free (desc);
916 
917 		style_context = gtk_widget_get_style_context (painter->widget);
918 		font_desc = gtk_style_context_get_font (style_context, GTK_STATE_FLAG_NORMAL);
919 		desc = pango_font_description_copy (font_desc);
920 	}
921 
922 	if (points)
923 		pango_font_description_set_size (desc, size);
924 	else
925 		pango_font_description_set_absolute_size (desc, (gint) size);
926 
927 	pango_font_description_set_style (desc, style & GTK_HTML_FONT_STYLE_ITALIC ? PANGO_STYLE_ITALIC : PANGO_STYLE_NORMAL);
928 	pango_font_description_set_weight (desc, style & GTK_HTML_FONT_STYLE_BOLD ? PANGO_WEIGHT_BOLD : PANGO_WEIGHT_NORMAL);
929 
930 	text_size (painter, desc, " ", 1, NULL, NULL, &space_width, &space_asc, &space_dsc);
931 
932 	return html_font_new (desc,
933 			      space_width,
934 			      space_asc, space_dsc,
935 			      text_width (painter, desc, "\xc2\xa0", 2),
936 			      text_width (painter, desc, "\t", 1),
937 			      text_width (painter, desc, "e", 1),
938 			      text_width (painter, desc, HTML_BLOCK_INDENT, strlen (HTML_BLOCK_INDENT)),
939 			      text_width (painter, desc, HTML_BLOCK_CITE_LTR, strlen (HTML_BLOCK_CITE_LTR)),
940 			      text_width (painter, desc, HTML_BLOCK_CITE_RTL, strlen (HTML_BLOCK_CITE_RTL)));
941 }
942 
943 void
html_painter_ref_font(HTMLPainter * painter,HTMLFont * font)944 html_painter_ref_font (HTMLPainter *painter,
945                        HTMLFont *font)
946 {
947 }
948 
949 void
html_painter_unref_font(HTMLPainter * painter,HTMLFont * font)950 html_painter_unref_font (HTMLPainter *painter,
951                          HTMLFont *font)
952 {
953 	if (font->ref_count < 1) {
954 		pango_font_description_free (font->data);
955 		font->data = NULL;
956 	}
957 }
958 
959 guint
html_painter_get_space_width(HTMLPainter * painter,GtkHTMLFontStyle style,HTMLFontFace * face)960 html_painter_get_space_width (HTMLPainter *painter,
961                               GtkHTMLFontStyle style,
962                               HTMLFontFace *face)
963 {
964 	return html_font_manager_get_font (&painter->font_manager, face, style)->space_width;
965 }
966 
967 guint
html_painter_get_space_asc(HTMLPainter * painter,GtkHTMLFontStyle style,HTMLFontFace * face)968 html_painter_get_space_asc (HTMLPainter *painter,
969                             GtkHTMLFontStyle style,
970                             HTMLFontFace *face)
971 {
972 	return html_font_manager_get_font (&painter->font_manager, face, style)->space_asc;
973 }
974 
975 guint
html_painter_get_space_dsc(HTMLPainter * painter,GtkHTMLFontStyle style,HTMLFontFace * face)976 html_painter_get_space_dsc (HTMLPainter *painter,
977                             GtkHTMLFontStyle style,
978                             HTMLFontFace *face)
979 {
980 	return html_font_manager_get_font (&painter->font_manager, face, style)->space_dsc;
981 }
982 
983 guint
html_painter_get_e_width(HTMLPainter * painter,GtkHTMLFontStyle style,HTMLFontFace * face)984 html_painter_get_e_width (HTMLPainter *painter,
985                           GtkHTMLFontStyle style,
986                           HTMLFontFace *face)
987 {
988 	return html_font_manager_get_font (&painter->font_manager, face, style)->e_width;
989 }
990 
991 guint
html_painter_get_block_indent_width(HTMLPainter * painter,GtkHTMLFontStyle style,HTMLFontFace * face)992 html_painter_get_block_indent_width (HTMLPainter *painter,
993                                      GtkHTMLFontStyle style,
994                                      HTMLFontFace *face)
995 {
996 	return html_font_manager_get_font (&painter->font_manager, face, style)->indent_width;
997 }
998 
999 guint
html_painter_get_block_cite_width(HTMLPainter * painter,GtkHTMLFontStyle style,HTMLFontFace * face,HTMLDirection dir)1000 html_painter_get_block_cite_width (HTMLPainter *painter,
1001                                    GtkHTMLFontStyle style,
1002                                    HTMLFontFace *face,
1003                                    HTMLDirection dir)
1004 {
1005 	HTMLFont *font = html_font_manager_get_font (&painter->font_manager, face, style);
1006 	return dir == HTML_DIRECTION_RTL ? font->cite_width_rtl : font->cite_width_ltr;
1007 }
1008 
1009 guint
html_painter_get_page_width(HTMLPainter * painter,HTMLEngine * e)1010 html_painter_get_page_width (HTMLPainter *painter,
1011                              HTMLEngine *e)
1012 {
1013 	return (* HP_CLASS (painter)->get_page_width) (painter, e);
1014 }
1015 
1016 guint
html_painter_get_page_height(HTMLPainter * painter,HTMLEngine * e)1017 html_painter_get_page_height (HTMLPainter *painter,
1018                               HTMLEngine *e)
1019 {
1020 	return (* HP_CLASS (painter)->get_page_height) (painter, e);
1021 }
1022 
1023 /**
1024  * html_painter_pango_to_engine:
1025  * @painter: a #HTMLPainter
1026  * @pango_units: distance in Pango units
1027  *
1028  * Convert a distance in Pango units (used for character layout) to
1029  * a distance in engine coordinates. Note that the computation is
1030  * only correct for positive values of @pango_units
1031  *
1032  * Return value: distance converted to engine coordinates.
1033  **/
1034 gint
html_painter_pango_to_engine(HTMLPainter * painter,gint pango_units)1035 html_painter_pango_to_engine (HTMLPainter *painter,
1036                               gint pango_units)
1037 {
1038 	gdouble tmp = 0.5 + pango_units / painter->engine_to_pango;
1039 	return (gint) CLAMP (tmp, G_MININT, G_MAXINT);
1040 }
1041 
1042 /**
1043  * html_painter_engine_to_pango:
1044  * @painter: a #HTMLPainter
1045  * @engine_coordiantes: distance in Pango units
1046  *
1047  * Convert a distance in engine coordinates to a distance in Pango
1048  * units (used for character layout). Note that the computation is
1049  * only correct for positive values of @pango_units
1050  *
1051  * Return value: distance converted to Pango units
1052  **/
1053 gint
html_painter_engine_to_pango(HTMLPainter * painter,gint engine_units)1054 html_painter_engine_to_pango (HTMLPainter *painter,
1055                               gint engine_units)
1056 {
1057 	gdouble tmp = 0.5 + engine_units * painter->engine_to_pango;
1058 	return (gint) CLAMP (tmp, G_MININT, G_MAXINT);
1059 }
1060 
1061 void
html_painter_set_focus(HTMLPainter * p,gboolean focus)1062 html_painter_set_focus (HTMLPainter *p,
1063                         gboolean focus)
1064 {
1065 	p->focus = focus;
1066 }
1067 
1068 void
html_painter_set_widget(HTMLPainter * painter,GtkWidget * widget)1069 html_painter_set_widget (HTMLPainter *painter,
1070                          GtkWidget *widget)
1071 {
1072 	(* HP_CLASS (painter)->set_widget) (painter, widget);
1073 }
1074 
1075 HTMLTextPangoInfo *
html_painter_text_itemize_and_prepare_glyphs(HTMLPainter * painter,PangoFontDescription * desc,const gchar * text,gint bytes,GList ** glyphs,PangoAttrList * attrs)1076 html_painter_text_itemize_and_prepare_glyphs (HTMLPainter *painter,
1077                                               PangoFontDescription *desc,
1078                                               const gchar *text,
1079                                               gint bytes,
1080                                               GList **glyphs,
1081                                               PangoAttrList *attrs)
1082 {
1083 	PangoAttribute *attr;
1084 	GList *items = NULL;
1085 	gboolean empty_attrs = (attrs == NULL);
1086 	HTMLTextPangoInfo *pi = NULL;
1087 
1088 	/* printf ("itemize + glyphs\n"); */
1089 
1090 	*glyphs = NULL;
1091 
1092 	if (empty_attrs) {
1093 		attrs = pango_attr_list_new ();
1094 		attr = pango_attr_font_desc_new (desc);
1095 		attr->start_index = 0;
1096 		attr->end_index = bytes;
1097 		pango_attr_list_insert (attrs, attr);
1098 	}
1099 
1100 	items = pango_itemize (painter->pango_context, text, 0, bytes, attrs, NULL);
1101 
1102 	if (empty_attrs)
1103 		pango_attr_list_unref (attrs);
1104 
1105 	if (items && items->data) {
1106 		PangoItem *item;
1107 		GList *il;
1108 		const gchar *end;
1109 		gint i = 0;
1110 
1111 		pi = html_text_pango_info_new (g_list_length (items));
1112 
1113 		for (il = items; il; il = il->next) {
1114 			item = (PangoItem *) il->data;
1115 			pi->entries[i].glyph_item.item = item;
1116 			end = g_utf8_offset_to_pointer (text, item->num_chars);
1117 			*glyphs = html_get_glyphs_non_tab (*glyphs, item, i, text, end - text, item->num_chars);
1118 			text = end;
1119 			i++;
1120 		}
1121 		*glyphs = g_list_reverse (*glyphs);
1122 		g_list_free (items);
1123 	}
1124 
1125 	return pi;
1126 }
1127 
1128 void
html_painter_glyphs_destroy(GList * glyphs)1129 html_painter_glyphs_destroy (GList *glyphs)
1130 {
1131 	GList *l;
1132 
1133 	for (l = glyphs; l; l = l->next->next)
1134 		pango_glyph_string_free ((PangoGlyphString *) l->data);
1135 	g_list_free (glyphs);
1136 }
1137 
1138 /**
1139  * html_pango_get_item_properties:
1140  * @item: a #PangoItem
1141  * @properties: a #HTMLPangoProperties structure
1142  *
1143  * Converts the list of extra attributes from @item into a more convenient
1144  * structure form.
1145  **/
1146 void
html_pango_get_item_properties(PangoItem * item,HTMLPangoProperties * properties)1147 html_pango_get_item_properties (PangoItem *item,
1148                                 HTMLPangoProperties *properties)
1149 {
1150 	GSList *tmp_list = item->analysis.extra_attrs;
1151 
1152 	properties->underline = FALSE;
1153 	properties->strikethrough = FALSE;
1154 	properties->fg_color = NULL;
1155 	properties->bg_color = NULL;
1156 
1157 	while (tmp_list) {
1158 		PangoAttribute *attr = tmp_list->data;
1159 
1160 		switch (attr->klass->type) {
1161 		case PANGO_ATTR_UNDERLINE:
1162 			properties->underline = ((PangoAttrInt *) attr)->value != PANGO_UNDERLINE_NONE;
1163 			break;
1164 
1165 		case PANGO_ATTR_STRIKETHROUGH:
1166 			properties->strikethrough = ((PangoAttrInt *) attr)->value;
1167 			break;
1168 
1169 		case PANGO_ATTR_FOREGROUND:
1170 			properties->fg_color = &((PangoAttrColor *) attr)->color;
1171 			break;
1172 
1173 		case PANGO_ATTR_BACKGROUND:
1174 			properties->bg_color = &((PangoAttrColor *) attr)->color;
1175 			break;
1176 
1177 		default:
1178 			break;
1179 		}
1180 		tmp_list = tmp_list->next;
1181 	}
1182 }
1183