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