2006-12-03 Dimitris Glezos <dimitris@glezos.com>
[dia.git] / lib / textline.c
blob1988e76b3289251cf5bd97a35d03bf2416b272f1
1 /* Dia -- an diagram creation/manipulation program
2 * Copyright (C) 1998 Alexander Larsson
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18 #include <config.h>
20 #include <stdio.h>
21 #include <stdlib.h>
22 #include <string.h>
23 #include <glib.h>
24 #include <math.h>
26 #include <gdk/gdkkeysyms.h>
28 #include "propinternals.h"
29 #include "text.h"
30 #include "message.h"
31 #include "diarenderer.h"
32 #include "diagramdata.h"
33 #include "objchange.h"
34 #include "textline.h"
36 static void text_line_dirty_cache(TextLine *text_line);
37 static void text_line_cache_values(TextLine *text_line);
39 /** Sets this object to display a particular string.
40 * @param text_line The object to change.
41 * @param string The string to display. This string will be copied.
43 void
44 text_line_set_string(TextLine *text_line, const gchar *string)
46 if (text_line->chars == NULL ||
47 strcmp(text_line->chars, string)) {
48 if (text_line->chars != NULL) {
49 g_free(text_line->chars);
52 text_line->chars = g_strdup(string);
54 text_line_dirty_cache(text_line);
58 /** Sets the font used by this object.
59 * @param text_line The object to change.
60 * @param font The font to use for displaying this object.
62 void
63 text_line_set_font(TextLine *text_line, DiaFont *font)
65 if (text_line->font != font) {
66 DiaFont *old_font = text_line->font;
67 dia_font_ref(font);
68 text_line->font = font;
69 if (old_font != NULL) {
70 dia_font_unref(old_font);
72 text_line_dirty_cache(text_line);
76 /** Sets the font height used by this object.
77 * @param text_line The object to change.
78 * @param height The font height to use for displaying this object
79 * (in cm, from baseline to baseline)
81 void
82 text_line_set_height(TextLine *text_line, real height)
84 if (fabs(text_line->height - height) > 0.00001) {
85 text_line->height = height;
86 text_line_dirty_cache(text_line);
90 /** Creates a new TextLine object from its components.
91 * @param string the string to display
92 * @param font the font to display the string with.
93 * @param height the height of the font, in cm from baseline to baseline.
95 TextLine *
96 text_line_new(const gchar *string, DiaFont *font, real height)
98 TextLine *text_line = g_new0(TextLine, 1);
100 text_line_set_string(text_line, string);
101 text_line_set_font(text_line, font);
102 text_line_set_height(text_line, height);
104 return text_line;
107 TextLine *
108 text_line_copy(const TextLine *text_line)
110 return text_line_new(text_line->chars, text_line->font, text_line->height);
113 /** Destroy a text_line object, deallocating all memory used and unreffing
114 * reffed objects.
115 * @param text_line the object to kill.
117 void
118 text_line_destroy(TextLine *text_line)
120 if (text_line->chars != NULL) {
121 g_free(text_line->chars);
123 if (text_line->font != NULL) {
124 dia_font_unref(text_line->font);
126 /* TODO: Handle renderer's cached content. */
127 g_free(text_line);
130 /** Calculate the bounding box size of this object. Since a text object has no
131 * position or alignment, this collapses to just a size.
132 * @param text_line
133 * @param size A place to store the width and height of the text.
135 void
136 text_line_calc_boundingbox_size(TextLine *text_line, Point *size)
138 text_line_cache_values(text_line);
140 size->x = text_line->width;
141 size->y = text_line->ascent + text_line->descent;
144 /** Draw a line of text at a given position and in a given color.
145 * @param renderer
146 * @param text_line
147 * @param pos
148 * @param color
150 void
151 text_line_draw(DiaRenderer *renderer, TextLine *text_line,
152 Point *pos, Color *color)
154 DIA_RENDERER_GET_CLASS(renderer)->draw_text_line(renderer, text_line,
155 pos, color);
158 gchar *
159 text_line_get_string(const TextLine *text_line)
161 return text_line->chars;
164 DiaFont *
165 text_line_get_font(const TextLine *text_line)
167 return text_line->font;
170 real
171 text_line_get_height(const TextLine *text_line)
173 return text_line->height;
176 real
177 text_line_get_width(TextLine *text_line)
179 text_line_cache_values(text_line);
180 return text_line->width;
183 real
184 text_line_get_ascent(TextLine *text_line)
186 text_line_cache_values(text_line);
187 return text_line->ascent;
190 real
191 text_line_get_descent(TextLine *text_line)
193 text_line_cache_values(text_line);
194 return text_line->descent;
197 /** Set some cache data for the renderer object. This data will get
198 * freed if this textline is ever changed or viewed at a different size.
199 * Any cache already set, regardless of identity, will be freed.
200 * @param text_line
201 * @param renderer
202 * @param free_func A function for freeing the data.
203 * @param scale The zooming scale factor (as defined by the renderer) used
204 * to make these data. If scale independent, just use 0.0.
205 * @param data
207 void
208 text_line_set_renderer_cache(TextLine *text_line, DiaRenderer *renderer,
209 RendererCacheFreeFunc free_func, real scale,
210 gpointer data) {
211 RendererCache *cache;
212 if (text_line->renderer_cache != NULL) {
213 (*text_line->renderer_cache->free_func)(text_line->renderer_cache);
214 text_line->renderer_cache = NULL;
216 cache = g_new(RendererCache, 1);
217 cache->renderer = renderer;
218 cache->free_func = free_func;
219 cache->scale = scale;
220 cache->data = data;
223 /** Get any renderer cache data that might be around.
224 * @param text_line
225 * @param renderer
226 * @param scale The scale we want text rendered at, or 0.0 if this renderer
227 * is scale independent.
228 * @returns Previously cached data (which shouldn't be freed) for the
229 * same text rendering.
231 gpointer
232 text_line_get_renderer_cache(TextLine *text_line, DiaRenderer *renderer,
233 real scale) {
234 if (text_line->clean && text_line->renderer_cache != NULL &&
235 text_line->renderer_cache->renderer == renderer &&
236 fabs(text_line->renderer_cache->scale - scale) < 0.0000001) {
237 return text_line->renderer_cache->data;
238 } else {
239 return NULL;
243 /** Return the amount this text line would need to be shifted in order to
244 * implement the given alignment.
245 * @param text_line a line of text
246 * @param alignment how to align it.
247 * @returns The amount (in diagram lengths) to shift the x positiion of
248 * rendering this such that it looks aligned when printed with x at the left.
249 * Always a positive number.
251 real
252 text_line_get_alignment_adjustment(TextLine *text_line, Alignment alignment)
254 text_line_cache_values(text_line);
255 switch (alignment) {
256 case ALIGN_CENTER:
257 return text_line->width / 2;
258 case ALIGN_RIGHT:
259 return text_line->width;
260 default:
261 return 0.0;
265 /* **** Private functions **** */
266 /** Mark this object as needing update before usage.
267 * @param text_line the object that has changed.
269 static void
270 text_line_dirty_cache(TextLine *text_line)
272 text_line->clean = FALSE;
275 static void
276 text_line_cache_values(TextLine *text_line)
278 if (!text_line->clean ||
279 text_line->chars != text_line->chars_cache ||
280 text_line->font != text_line->font_cache ||
281 text_line->height != text_line->height_cache) {
282 int n_offsets;
284 if (text_line->offsets != NULL) {
285 g_free(text_line->offsets);
286 text_line->offsets = NULL;
288 if (text_line->renderer_cache != NULL) {
289 (*text_line->renderer_cache->free_func)(text_line->renderer_cache);
290 text_line->renderer_cache = NULL;
292 if (text_line->layout_offsets != NULL) {
293 GSList *runs = text_line->layout_offsets->runs;
295 for (; runs != NULL; runs = g_slist_next(runs)) {
296 PangoGlyphItem *run = (PangoGlyphItem *) runs->data;
297 int i;
299 g_free(run->glyphs->glyphs);
300 g_free(run->glyphs);
302 g_slist_free(runs);
303 g_free(text_line->layout_offsets);
304 text_line->layout_offsets = NULL;
307 if (text_line->chars == NULL ||
308 text_line->chars[0] == '\0') {
309 text_line->offsets = g_new(real, 0);
310 text_line->layout_offsets = NULL;
311 text_line->ascent = text_line->height * .5;
312 text_line->descent = text_line->height * .5;
313 text_line->width = 0;
314 } else {
315 text_line->offsets =
316 dia_font_get_sizes(text_line->chars, text_line->font, text_line->height,
317 &text_line->width, &text_line->ascent,
318 &text_line->descent, &n_offsets,
319 &text_line->layout_offsets);
321 text_line->clean = TRUE;
322 text_line->chars_cache = text_line->chars;
323 text_line->font_cache = text_line->font;
324 text_line->height_cache = text_line->height;
328 /** Adjust a line of glyphs to match the sizes stored in the TextLine
329 * @param line The TextLine object that corresponds to the glyphs.
330 * @param glyphs The one set of glyphs contained in layout created for
331 * this TextLine during rendering. The values in here will be changed.
332 * @param scale The relative height of the font in glyphs.
334 void
335 text_line_adjust_glyphs(TextLine *line, PangoGlyphString *glyphs, real scale)
337 int i;
339 for (i = 0; i < glyphs->num_glyphs; i++) {
341 printf("Glyph %d: width %d, offset %f, textwidth %f\n",
342 i, new_glyphs->glyphs[i].geometry.width, line->offsets[i],
343 line->offsets[i] * scale * 20.0 * PANGO_SCALE);
345 glyphs->glyphs[i].geometry.width =
346 (int)(line->offsets[i] * scale * 20.0 * PANGO_SCALE);
350 /** Adjust a layout line to match the more fine-grained values stored in the
351 * textline. This circumvents the rounding errors in Pango and ensures a
352 * linear scaling for zooming and export filters.
353 * @param line The TextLine object that corresponds to the glyphs.
354 * @param layoutline The one set of glyphs contained in the TextLine's layout.
355 * @param scale The relative height of the font in glyphs.
356 * @return An adjusted glyphstring, which should be freed by the caller.
358 void
359 text_line_adjust_layout_line(TextLine *line, PangoLayoutLine *layoutline,
360 real scale)
362 GSList *layoutruns = layoutline->runs;
363 GSList *runs;
365 if (line->layout_offsets == NULL) {
366 return;
369 runs = line->layout_offsets->runs;
371 if (g_slist_length(runs) != g_slist_length(layoutruns)) {
372 printf("Runs length error: %d != %d\n",
373 g_slist_length(line->layout_offsets->runs),
374 g_slist_length(layoutline->runs));
376 for (; runs != NULL && layoutruns != NULL; runs = g_slist_next(runs),
377 layoutruns = g_slist_next(layoutruns)) {
378 PangoGlyphString *glyphs = ((PangoLayoutRun *) runs->data)->glyphs;
379 PangoGlyphString *layoutglyphs =
380 ((PangoLayoutRun *) layoutruns->data)->glyphs;
381 int i;
383 for (i = 0; i < glyphs->num_glyphs && i < layoutglyphs->num_glyphs; i++) {
384 layoutglyphs->glyphs[i].geometry.width =
385 (int)(glyphs->glyphs[i].geometry.width * scale / 20.0);
386 layoutglyphs->glyphs[i].geometry.x_offset =
387 (int)(glyphs->glyphs[i].geometry.x_offset * scale / 20.0);
388 layoutglyphs->glyphs[i].geometry.y_offset =
389 (int)(glyphs->glyphs[i].geometry.y_offset * scale / 20.0);
391 if (glyphs->num_glyphs != layoutglyphs->num_glyphs) {
392 printf("Glyph length error: %d != %d\n",
393 glyphs->num_glyphs, layoutglyphs->num_glyphs);