]> gitweb.michael.orlitzky.com - libsvgtiny.git/blob - examples/svgtiny_display_x11.c
examples/svgtiny_display_x11.c: use opacity information
[libsvgtiny.git] / examples / svgtiny_display_x11.c
1 /*
2 * This file is part of Libsvgtiny
3 * Licensed under the MIT License,
4 * http://opensource.org/licenses/mit-license.php
5 * Copyright 2009-2010 James Bursa <james@semichrome.net>
6 */
7
8 /*
9 * This example loads an SVG using libsvgtiny and then displays it in an X11
10 * window using cairo.
11 *
12 * Functions of interest for libsvgtiny use are:
13 * main() - loads an SVG using svgtiny_create() and svgtiny_parse()
14 * event_diagram_expose() - renders the SVG by stepping through the shapes
15 *
16 * Compile using:
17 * gcc -g -W -Wall -o svgtiny_display_x11 svgtiny_display_x11.c \
18 * `pkg-config --cflags --libs libsvgtiny cairo` -lX11
19 */
20
21
22 #include <libgen.h>
23 #include <math.h>
24 #include <stdbool.h>
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <string.h>
28 #include <sys/types.h>
29 #include <sys/stat.h>
30 #include <unistd.h>
31 #include <X11/Xlib.h>
32 #include <X11/keysym.h>
33 #include <cairo.h>
34 #include <cairo-xlib.h>
35 #include <svgtiny.h>
36
37
38 struct svgtiny_diagram *diagram;
39 Display *display;
40 Window diagram_window;
41 Atom wm_protocols_atom, wm_delete_window_atom;
42 char *svg_path;
43 float scale = 1.0;
44 bool quit = false;
45
46
47 void gui_init(void);
48 void gui_quit(void);
49 void update_window_title(void);
50 void gui_poll(void);
51 void event_diagram_key_press(XKeyEvent *key_event);
52 void event_diagram_expose(const XExposeEvent *expose_event);
53 void render_path(cairo_t *cr, float scale, struct svgtiny_shape *path);
54 void die(const char *message);
55
56
57 /**
58 * Main program.
59 */
60 int main(int argc, char *argv[])
61 {
62 FILE *fd;
63 struct stat sb;
64 char *buffer;
65 size_t size;
66 size_t n;
67 svgtiny_code code;
68
69 if (argc != 2) {
70 fprintf(stderr, "Usage: %s FILE\n", argv[0]);
71 return 1;
72 }
73 svg_path = argv[1];
74
75 /* load file into memory buffer */
76 fd = fopen(svg_path, "rb");
77 if (!fd) {
78 perror(svg_path);
79 return 1;
80 }
81
82 if (stat(svg_path, &sb)) {
83 perror(svg_path);
84 return 1;
85 }
86 size = sb.st_size;
87
88 buffer = malloc(size);
89 if (!buffer) {
90 fprintf(stderr, "Unable to allocate %lld bytes\n",
91 (long long) size);
92 return 1;
93 }
94
95 n = fread(buffer, 1, size, fd);
96 if (n != size) {
97 perror(svg_path);
98 return 1;
99 }
100
101 fclose(fd);
102
103 /* create svgtiny object */
104 diagram = svgtiny_create();
105 if (!diagram) {
106 fprintf(stderr, "svgtiny_create failed\n");
107 return 1;
108 }
109
110 /* parse */
111 code = svgtiny_parse(diagram, buffer, size, svg_path, 1000, 1000);
112 if (code != svgtiny_OK) {
113 fprintf(stderr, "svgtiny_parse failed: ");
114 switch (code) {
115 case svgtiny_OUT_OF_MEMORY:
116 fprintf(stderr, "svgtiny_OUT_OF_MEMORY");
117 break;
118 case svgtiny_LIBDOM_ERROR:
119 fprintf(stderr, "svgtiny_LIBDOM_ERROR");
120 break;
121 case svgtiny_NOT_SVG:
122 fprintf(stderr, "svgtiny_NOT_SVG");
123 break;
124 case svgtiny_SVG_ERROR:
125 fprintf(stderr, "svgtiny_SVG_ERROR: line %i: %s",
126 diagram->error_line,
127 diagram->error_message);
128 break;
129 case svgtiny_LIBCSS_ERROR:
130 fprintf(stderr, "svgtiny_LIBCSS_ERROR");
131 break;
132 default:
133 fprintf(stderr, "unknown svgtiny_code %i", code);
134 break;
135 }
136 fprintf(stderr, "\n");
137 }
138
139 free(buffer);
140
141 /*printf("viewbox 0 0 %u %u\n",
142 diagram->width, diagram->height);*/
143
144 gui_init();
145
146 while (!quit) {
147 gui_poll();
148 }
149
150 gui_quit();
151 svgtiny_free(diagram);
152
153 return 0;
154 }
155
156
157 /**
158 * Initialize X11 interface.
159 */
160 void gui_init(void)
161 {
162 display = XOpenDisplay(NULL);
163 if (!display)
164 die("XOpenDisplay failed: is DISPLAY set?");
165
166 diagram_window = XCreateSimpleWindow(display,
167 DefaultRootWindow(display),
168 0, 0, diagram->width, diagram->height, 0, 0, 0);
169
170 update_window_title();
171
172 XMapWindow(display, diagram_window);
173 XSelectInput(display, diagram_window,
174 KeyPressMask |
175 ButtonPressMask |
176 ExposureMask |
177 StructureNotifyMask);
178
179 wm_protocols_atom = XInternAtom(display, "WM_PROTOCOLS", False);
180 wm_delete_window_atom = XInternAtom(display, "WM_DELETE_WINDOW", False);
181 XSetWMProtocols(display, diagram_window, &wm_delete_window_atom, 1);
182 }
183
184
185 /**
186 * Free X11 interface.
187 */
188 void gui_quit(void)
189 {
190 XCloseDisplay(display);
191 }
192
193
194 /**
195 * Update window title to show current state.
196 */
197 void update_window_title(void)
198 {
199 char title[100];
200 char *svg_path_copy;
201 char *base_name;
202
203 svg_path_copy = strdup(svg_path);
204 if (!svg_path_copy) {
205 fprintf(stderr, "out of memory\n");
206 return;
207 }
208
209 base_name = basename(svg_path_copy);
210
211 snprintf(title, sizeof title, "%s (%i%%) - svgtiny",
212 base_name, (int) roundf(scale * 100.0));
213
214 XStoreName(display, diagram_window, title);
215
216 free(svg_path_copy);
217 }
218
219
220 /**
221 * Handle an X11 event.
222 */
223 void gui_poll(void)
224 {
225 XEvent event;
226 XNextEvent(display, &event);
227
228 switch (event.type) {
229 case KeyPress:
230 if (event.xkey.window == diagram_window) {
231 event_diagram_key_press(&event.xkey);
232 }
233 break;
234 case Expose:
235 if (event.xexpose.window == diagram_window) {
236 event_diagram_expose(&event.xexpose);
237 }
238 break;
239 case ClientMessage:
240 if (event.xclient.message_type == wm_protocols_atom &&
241 event.xclient.format == 32 &&
242 (Atom) event.xclient.data.l[0] ==
243 wm_delete_window_atom)
244 quit = true;
245 break;
246 default:
247 /*printf("unknown event %i\n", event.type);*/
248 break;
249 }
250 }
251
252
253 /**
254 * Handle an X11 KeyPress event in the diagram window.
255 */
256 void event_diagram_key_press(XKeyEvent *key_event)
257 {
258 KeySym key_sym;
259 float new_scale = scale;
260 unsigned int width, height;
261
262 key_sym = XLookupKeysym(key_event, 0);
263
264 switch (key_sym) {
265 case XK_q:
266 case XK_Escape:
267 quit = true;
268 break;
269
270 case XK_minus:
271 case XK_KP_Subtract:
272 new_scale -= 0.1;
273 break;
274
275 case XK_equal:
276 case XK_plus:
277 case XK_KP_Add:
278 new_scale += 0.1;
279 break;
280
281 case XK_1:
282 case XK_KP_Multiply:
283 case XK_KP_1:
284 new_scale = 1;
285 break;
286
287 case XK_2:
288 case XK_KP_2:
289 new_scale = 2;
290 break;
291
292 default:
293 break;
294 }
295
296 if (new_scale < 0.1)
297 new_scale = 0.1;
298 else if (5 < new_scale)
299 new_scale = 5;
300
301 if (new_scale == scale)
302 return;
303
304 scale = new_scale;
305 width = diagram->width * scale;
306 height = diagram->height * scale;
307 if (width < 400)
308 width = 400;
309 if (height < 400)
310 height = 400;
311 XResizeWindow(display, diagram_window, width, height);
312 XClearArea(display, diagram_window, 0, 0, 0, 0, True);
313 update_window_title();
314 }
315
316
317 /**
318 * Handle an X11 Expose event of the diagram window.
319 */
320 void event_diagram_expose(const XExposeEvent *expose_event)
321 {
322 cairo_surface_t *surface;
323 cairo_t *cr;
324 cairo_status_t status;
325 unsigned int i;
326
327 if (expose_event->count != 0)
328 return;
329
330 surface = cairo_xlib_surface_create(display, diagram_window,
331 DefaultVisual(display, DefaultScreen(display)),
332 diagram->width * scale, diagram->height * scale);
333 if (!surface) {
334 fprintf(stderr, "cairo_xlib_surface_create failed\n");
335 return;
336 }
337
338 cr = cairo_create(surface);
339 status = cairo_status(cr);
340 if (status != CAIRO_STATUS_SUCCESS) {
341 fprintf(stderr, "cairo_create failed: %s\n",
342 cairo_status_to_string(status));
343 cairo_destroy(cr);
344 cairo_surface_destroy(surface);
345 return;
346 }
347
348 cairo_set_source_rgba(cr, 1, 1, 1, 1);
349 cairo_paint(cr);
350
351 for (i = 0; i != diagram->shape_count; i++) {
352 if (diagram->shape[i].path) {
353 render_path(cr, scale, &diagram->shape[i]);
354
355 } else if (diagram->shape[i].text) {
356 cairo_set_source_rgba(cr,
357 svgtiny_RED(diagram->shape[i].stroke) / 255.0,
358 svgtiny_GREEN(diagram->shape[i].stroke) / 255.0,
359 svgtiny_BLUE(diagram->shape[i].stroke) / 255.0,
360 diagram->shape[i].stroke_opacity);
361 cairo_move_to(cr,
362 scale * diagram->shape[i].text_x,
363 scale * diagram->shape[i].text_y);
364 cairo_show_text(cr, diagram->shape[i].text);
365 }
366 }
367
368 status = cairo_status(cr);
369 if (status != CAIRO_STATUS_SUCCESS) {
370 fprintf(stderr, "cairo error: %s\n",
371 cairo_status_to_string(status));
372 cairo_destroy(cr);
373 cairo_surface_destroy(surface);
374 return;
375 }
376
377 cairo_destroy(cr);
378 cairo_surface_destroy(surface);
379 }
380
381
382 /**
383 * Render an svgtiny path using cairo.
384 */
385 void render_path(cairo_t *cr, float scale, struct svgtiny_shape *path)
386 {
387 unsigned int j;
388
389 cairo_new_path(cr);
390 for (j = 0; j != path->path_length; ) {
391 switch ((int) path->path[j]) {
392 case svgtiny_PATH_MOVE:
393 cairo_move_to(cr,
394 scale * path->path[j + 1],
395 scale * path->path[j + 2]);
396 j += 3;
397 break;
398 case svgtiny_PATH_CLOSE:
399 cairo_close_path(cr);
400 j += 1;
401 break;
402 case svgtiny_PATH_LINE:
403 cairo_line_to(cr,
404 scale * path->path[j + 1],
405 scale * path->path[j + 2]);
406 j += 3;
407 break;
408 case svgtiny_PATH_BEZIER:
409 cairo_curve_to(cr,
410 scale * path->path[j + 1],
411 scale * path->path[j + 2],
412 scale * path->path[j + 3],
413 scale * path->path[j + 4],
414 scale * path->path[j + 5],
415 scale * path->path[j + 6]);
416 j += 7;
417 break;
418 default:
419 printf("error ");
420 j += 1;
421 }
422 }
423 if (path->fill != svgtiny_TRANSPARENT) {
424 cairo_set_source_rgba(cr,
425 svgtiny_RED(path->fill) / 255.0,
426 svgtiny_GREEN(path->fill) / 255.0,
427 svgtiny_BLUE(path->fill) / 255.0,
428 path->fill_opacity);
429 cairo_fill_preserve(cr);
430 }
431 if (path->stroke != svgtiny_TRANSPARENT) {
432 cairo_set_source_rgba(cr,
433 svgtiny_RED(path->stroke) / 255.0,
434 svgtiny_GREEN(path->stroke) / 255.0,
435 svgtiny_BLUE(path->stroke) / 255.0,
436 path->stroke_opacity);
437 cairo_set_line_width(cr, scale * path->stroke_width);
438 cairo_stroke_preserve(cr);
439 }
440 }
441
442
443 /**
444 * Exit with fatal error.
445 */
446 void die(const char *message)
447 {
448 fprintf(stderr, "%s\n", message);
449 exit(1);
450 }
451