* lib/text.h: Added text_get_line() declaration
[dia.git] / lib / textline.c
blob703b672a23cbc397afc32678d8c8d999416071eb
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 /* Non-debugged code for when we have multiple runs in a line.
294 GSList *runs = text_line->layout_offsets->runs;
296 for (; runs != NULL; runs = g_slist_next(runs)) {
297 PangoGlyphItem *run = (PangoGlyphItem *) runs->data;
298 int i;
300 for (i = 0; i < run->item->num_chars; i++) {
301 g_free(run->glyphs[i].glyphs);
303 g_free(run->glyphs);
305 g_slist_free(runs);
306 g_free(text_line->layout_offsets);
310 if (text_line->chars == NULL ||
311 text_line->chars[0] == '\0') {
312 text_line->offsets = g_new(real, 0);
313 text_line->layout_offsets = NULL;
314 text_line->ascent = text_line->height * .5;
315 text_line->descent = text_line->height * .5;
316 text_line->width = 0;
317 } else {
318 text_line->offsets =
319 dia_font_get_sizes(text_line->chars, text_line->font, text_line->height,
320 &text_line->width, &text_line->ascent,
321 &text_line->descent, &n_offsets, NULL);
323 text_line->clean = TRUE;
324 text_line->chars_cache = text_line->chars;
325 text_line->font_cache = text_line->font;
326 text_line->height_cache = text_line->height;
330 /** Adjust a line of glyphs to match the sizes stored in the TextLine
331 * @param line The TextLine object that corresponds to the glyphs.
332 * @param glyphs The one set of glyphs contained in layout created for
333 * this TextLine during rendering. The values in here will be changed.
334 * @param scale The relative height of the font in glyphs.
336 void
337 text_line_adjust_glyphs(TextLine *line, PangoGlyphString *glyphs, real scale)
339 int i;
341 for (i = 0; i < glyphs->num_glyphs; i++) {
343 printf("Glyph %d: width %d, offset %f, textwidth %f\n",
344 i, new_glyphs->glyphs[i].geometry.width, line->offsets[i],
345 line->offsets[i] * scale * 20.0 * PANGO_SCALE);
347 glyphs->glyphs[i].geometry.width =
348 (int)(line->offsets[i] * scale * 20.0 * PANGO_SCALE);
352 /** Adjust a layout line to match the more fine-grained values stored in the
353 * textline. This circumvents the rounding errors in Pango and ensures a
354 * linear scaling for zooming and export filters.
355 * @param line The TextLine object that corresponds to the glyphs.
356 * @param layoutline The one set of glyphs contained in the TextLine's layout.
357 * @param scale The relative height of the font in glyphs.
358 * @return An adjusted glyphstring, which should be freed by the caller.
360 void
361 text_line_adjust_layout_line(TextLine *line, PangoLayoutLine *layoutline,
362 real scale)
365 /* This is a one-run version that uses tried-and-true offset arrays. */
366 if (line->offsets == NULL) {
367 return;
368 } else {
369 PangoGlyphString *glyphs;
370 PangoGlyphItem *glyphItem;
372 if (g_slist_length(layoutline->runs) != 1) {
373 message_warning("Unexpected %d runs in %s\n",
374 g_slist_length(layoutline->runs),
375 line->chars);
378 glyphItem = (PangoGlyphItem*)layoutline->runs->data;
379 glyphs = glyphItem->glyphs;
380 text_line_adjust_glyphs(line, glyphItem->glyphs, scale);
383 /* More advanced version.
384 * Commented out until we can have more than one run in a line.
385 GSList *layoutruns = layoutline->runs;
386 GSList *runs;
388 if (line->layout_offsets == NULL) {
389 return;
392 runs = line->layout_offsets->runs;
394 for (; runs != NULL && layoutruns != NULL; runs = g_slist_next(runs),
395 layoutruns = g_slist_next(layoutruns)) {
396 PangoGlyphString *glyphs = ((PangoLayoutRun *) runs->data)->glyphs;
397 PangoGlyphString *layoutglyphs =
398 ((PangoLayoutRun *) layoutruns->data)->glyphs;
399 int i;
401 for (i = 0; i < glyphs->num_glyphs && i < layoutglyphs->num_glyphs; i++) {
402 layoutglyphs->glyphs[i].geometry.width =
403 (int)(glyphs->glyphs[i].geometry.width * scale * 20.0 * PANGO_SCALE);
404 layoutglyphs->glyphs[i].geometry.x_offset =
405 (int)(glyphs->glyphs[i].geometry.x_offset * scale * 20.0 * PANGO_SCALE);
406 layoutglyphs->glyphs[i].geometry.y_offset =
407 (int)(glyphs->glyphs[i].geometry.y_offset * scale * 20.0 * PANGO_SCALE);
409 if (glyphs->num_glyphs != layoutglyphs->num_glyphs) {
410 printf("Glyph length error: %d != %d\n",
411 glyphs->num_glyphs, layoutglyphs->num_glyphs);
414 if (runs != layoutruns) {
415 printf("Runs length error: %d != %d\n",
416 g_slist_length(line->layout_offsets->runs),
417 g_slist_length(layoutline->runs));