2 * gEDA/gaf command-line utility
3 * Copyright (C) 2012 Peter Brett <peter@peter-b.co.uk>
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
31 /* Gettext translation */
34 #include <libgeda/libgeda.h>
35 #include <libgeda/libgedaguile.h>
36 #include <libgedacairo/libgedacairo.h>
39 #include <glib/gstdio.h>
41 #include <cairo-svg.h>
42 #include <cairo-pdf.h>
45 static int export_text_rendered_bounds (void *user_data
,
48 int *right
, int *bottom
);
49 static void export_layout_page (PAGE
*page
, cairo_rectangle_t
*extents
,
51 static void export_draw_page (PAGE
*page
);
53 static void export_png (void);
54 static void export_postscript (gboolean is_eps
);
55 static void export_ps (void);
56 static void export_eps (void);
57 static void export_pdf (void);
58 static void export_svg (void);
60 static gdouble
export_parse_dist (const gchar
*dist
);
61 static gboolean
export_parse_scale (const gchar
*scale
);
62 static gboolean
export_parse_layout (const gchar
*layout
);
63 static gboolean
export_parse_margins (const gchar
*margins
);
64 static gboolean
export_parse_paper (const gchar
*paper
);
65 static gboolean
export_parse_size (const gchar
*size
);
66 static void export_config (void);
67 static void export_usage (void);
68 static void export_command_line (int argc
, char * const *argv
);
70 /* Default pixels-per-inch for raster outputs */
71 #define DEFAULT_DPI 96
72 /* Default margin width in points */
73 #define DEFAULT_MARGIN 18
75 enum ExportFormatFlags
{
82 gchar
*name
; /* UTF-8 */
83 gchar
*alias
; /* UTF-8 */
88 enum ExportOrientation
{
90 ORIENTATION_LANDSCAPE
,
94 struct ExportSettings
{
97 char * const *infilev
; /* Filename encoding */
98 const char *outfile
; /* Filename encoding */
99 gchar
*format
; /* UTF-8 */
101 enum ExportOrientation layout
;
104 gdouble scale
; /* Output scale; defaults to 1 mil per 1 gschem point*/
105 gdouble size
[2]; /* Points */
106 gdouble margins
[4]; /* Points. Top, right, bottom, left. */
107 gdouble align
[2]; /* 0.0 < align < 1.0 for halign and valign */
111 gchar
*font
; /* UTF-8 */
114 static struct ExportFormat formats
[] =
116 {"Portable Network Graphics (PNG)", "png", OUTPUT_PIXELS
, export_png
},
117 {"Postscript (PS)", "ps", OUTPUT_POINTS
| OUTPUT_MULTIPAGE
, export_ps
},
118 {"Encapsulated Postscript (EPS)", "eps", OUTPUT_POINTS
, export_eps
},
119 {"Portable Document Format (PDF)", "pdf", OUTPUT_POINTS
| OUTPUT_MULTIPAGE
, export_pdf
},
120 {"Scalable Vector Graphics (SVG)", "svg", OUTPUT_POINTS
, export_svg
},
121 {NULL
, NULL
, 0, NULL
},
124 static EdaRenderer
*renderer
= NULL
;
125 static TOPLEVEL
*toplevel
= NULL
;
127 static struct ExportSettings settings
= {
146 #define bad_arg_msg _("ERROR: Bad argument '%s' to %s option.\n")
147 #define see_help_msg _("\nRun `gaf export --help' for more information.\n")
149 /* Main function for `gaf export' */
151 cmd_export (int argc
, char **argv
)
156 const gchar
*out_suffix
;
157 struct ExportFormat
*exporter
= NULL
;
158 GArray
*render_color_map
= NULL
;
159 gchar
*original_cwd
= g_get_current_dir ();
161 gtk_init_check (&argc
, &argv
);
164 scm_dynwind_begin (0);
165 toplevel
= s_toplevel_new ();
166 edascm_dynwind_toplevel (toplevel
);
168 /* Now load rc files, if necessary */
169 if (getenv ("GAF_INHIBIT_RCFILES") == NULL
) {
170 g_rc_parse (toplevel
, "gaf export", NULL
, NULL
);
172 i_vars_libgeda_set (toplevel
); /* Ugh */
174 /* Parse configuration files */
177 /* Parse command-line arguments */
178 export_command_line (argc
, argv
);
180 /* If no format was specified, try and guess from output
182 if (settings
.format
== NULL
) {
183 out_suffix
= strrchr (settings
.outfile
, '.');
184 if (out_suffix
!= NULL
) {
185 out_suffix
++; /* Skip '.' */
188 _("ERROR: Cannot infer output format from filename '%s'.\n"),
194 /* Try and find an exporter function */
195 tmp
= g_utf8_strdown ((settings
.format
== NULL
) ? out_suffix
: settings
.format
, -1);
196 for (i
= 0; formats
[i
].name
!= NULL
; i
++) {
197 if (strcmp (tmp
, formats
[i
].alias
) == 0) {
198 exporter
= &formats
[i
];
202 if (exporter
== NULL
) {
203 if (settings
.format
== NULL
) {
205 _("ERROR: Cannot find supported format for filename '%s'.\n"),
210 _("ERROR: Unsupported output format '%s'.\n"),
212 fprintf (stderr
, see_help_msg
);
218 /* If more than one schematic/symbol file was specified, check that
219 * exporter supports multipage output. */
220 if ((settings
.infilec
> 1) && !(exporter
->flags
& OUTPUT_MULTIPAGE
)) {
222 _("ERROR: Selected output format does not support multipage output\n"));
226 /* Load schematic files */
227 while (optind
< argc
) {
229 tmp
= argv
[optind
++];
231 page
= s_page_new (toplevel
, tmp
);
232 if (!f_open (toplevel
, page
, tmp
, &err
)) {
234 _("ERROR: Failed to load '%s': %s\n"), tmp
,
238 if (g_chdir (original_cwd
) != 0) {
240 _("ERROR: Failed to change directory to '%s': %s\n"),
241 original_cwd
, g_strerror (errno
));
246 /* Create renderer */
247 renderer
= eda_renderer_new (NULL
, NULL
);
248 if (settings
.font
!= NULL
) {
249 g_object_set (renderer
, "font-name", settings
.font
, NULL
);
252 /* Make sure libgeda knows how to calculate the bounds of text
253 * taking into account font etc. */
254 o_text_set_rendered_bounds_func (toplevel
,
255 export_text_rendered_bounds
,
258 /* Create color map */
260 g_array_sized_new (FALSE
, FALSE
, sizeof(COLOR
), MAX_COLORS
);
262 g_array_append_vals (render_color_map
, print_colors
, MAX_COLORS
);
263 if (!settings
.color
) {
264 /* Create a black and white color map. All non-background colors
266 COLOR white
= {~0, ~0, ~0, ~0, TRUE
};
267 COLOR black
= {0, 0, 0, ~0, TRUE
};
268 for (i
= 0; i
< MAX_COLORS
; i
++) {
269 COLOR
*c
= &g_array_index (render_color_map
, COLOR
, i
);
270 if (!c
->enabled
) continue;
277 if (i
== OUTPUT_BACKGROUND_COLOR
) {
284 eda_renderer_set_color_map (renderer
, render_color_map
);
293 /* Callback function registered with libgeda to allow the libgeda
294 * "bounds" functions to get text bounds using the renderer. If a
295 * "rendered bounds" function isn't provided, text objects don't get
296 * used when calculating the extents of the drawing. */
298 export_text_rendered_bounds (void *user_data
, OBJECT
*object
,
299 int *left
, int *top
, int *right
, int *bottom
)
303 EdaRenderer
*renderer
= EDA_RENDERER (user_data
);
304 result
= eda_renderer_get_user_bounds (renderer
, object
, &l
, &t
, &r
, &b
);
305 *left
= lrint (fmin (l
,r
));
306 *top
= lrint (fmin (t
, b
));
307 *right
= lrint (fmax (l
, r
));
308 *bottom
= lrint (fmax (t
, b
));
312 /* Prints a message and quits with error status if a cairo status
313 * value is not "success". */
315 export_cairo_check_error (cairo_status_t status
)
317 if (status
!= CAIRO_STATUS_SUCCESS
) {
318 fprintf (stderr
, _("ERROR: %s.\n"), cairo_status_to_string (status
));
323 /* Calculates a page layout. If page is NULL, uses the first page
324 * (this is convenient for single-page rendering). The required size
325 * of the page is returned in extents, and the cairo transformation
326 * matrix needed to fit the drawing into the page is returned in mtx.
327 * Takes into account all of the margin/orientation/paper settings,
328 * and the size of the drawing itself. */
330 export_layout_page (PAGE
*page
, cairo_rectangle_t
*extents
, cairo_matrix_t
*mtx
)
332 cairo_matrix_t tmp_mtx
;
333 cairo_rectangle_t drawable
;
335 int wx_min
, wy_min
, wx_max
, wy_max
, w_width
, w_height
;
336 gboolean landscape
= FALSE
;
337 gboolean size_from_paper
= FALSE
;
338 gboolean size_from_drawing
= FALSE
;
339 gdouble m
[4]; /* Calculated margins */
340 gdouble s
; /* Calculated scale */
341 gdouble slack
[2]; /* Calculated alignment slack */
343 cr
= eda_renderer_get_cairo_context (renderer
);
346 const GList
*pages
= geda_list_get_glist (toplevel
->pages
);
347 g_assert (pages
!= NULL
&& pages
->data
!= NULL
);
348 page
= (PAGE
*) pages
->data
;
351 /* Calculate extents of objects within page */
352 cairo_matrix_init (&tmp_mtx
, 1, 0, 0, -1, -1, -1); /* Very vague approximation */
353 cairo_set_matrix (cr
, &tmp_mtx
);
354 world_get_object_glist_bounds (toplevel
, s_page_objects (page
),
355 &wx_min
, &wy_min
, &wx_max
, &wy_max
);
356 w_width
= wx_max
- wx_min
;
357 w_height
= wy_max
- wy_min
;
359 /* If a size was specified, use it. Otherwise, use paper size, if
360 * provided. Fall back to just using the size of the drawing. */
361 extents
->x
= extents
->y
= 0;
362 if (settings
.size
[0] >= 0) {
364 extents
->width
= settings
.size
[0];
365 extents
->height
= settings
.size
[1];
367 } else if (settings
.paper
!= NULL
) {
368 gdouble p_width
, p_height
;
370 /* Select orientation */
371 switch (settings
.layout
) {
372 case ORIENTATION_LANDSCAPE
:
375 case ORIENTATION_PORTRAIT
:
378 case ORIENTATION_AUTO
:
380 landscape
= (w_width
> w_height
);
384 p_width
= gtk_paper_size_get_width (settings
.paper
, GTK_UNIT_POINTS
);
385 p_height
= gtk_paper_size_get_height (settings
.paper
, GTK_UNIT_POINTS
);
388 extents
->width
= p_height
;
389 extents
->height
= p_width
;
391 extents
->width
= p_width
;
392 extents
->height
= p_height
;
395 size_from_paper
= TRUE
;
399 extents
->width
= w_width
* settings
.scale
; /* in points */
400 extents
->height
= w_height
* settings
.scale
; /* in points */
402 size_from_drawing
= TRUE
;
405 /* Now set the margins. If none were provided by the user, get them
406 * from the paper size (if a paper size is being used) or just use a
407 * sensible default. */
408 if (settings
.margins
[0] >= 0) {
409 memcpy (m
, settings
.margins
, 4*sizeof(gdouble
));
410 } else if (size_from_paper
) {
411 m
[0] = gtk_paper_size_get_default_top_margin (settings
.paper
, GTK_UNIT_POINTS
);
412 m
[1] = gtk_paper_size_get_default_left_margin (settings
.paper
, GTK_UNIT_POINTS
);
413 m
[2] = gtk_paper_size_get_default_bottom_margin (settings
.paper
, GTK_UNIT_POINTS
);
414 m
[3] = gtk_paper_size_get_default_right_margin (settings
.paper
, GTK_UNIT_POINTS
);
416 m
[0] = DEFAULT_MARGIN
;
417 m
[1] = DEFAULT_MARGIN
;
418 m
[2] = DEFAULT_MARGIN
;
419 m
[3] = DEFAULT_MARGIN
;
425 /* If the extents were obtained from the drawing, grow the extents
426 * rather than shrinking the drawable area. This ensures that the
427 * overall aspect ratio of the image remains correct. */
428 if (size_from_drawing
) {
429 extents
->width
+= m
[1] + m
[3];
430 extents
->height
+= m
[0] + m
[2];
433 drawable
.width
= extents
->width
- m
[1] - m
[3];
434 drawable
.height
= extents
->height
- m
[0] - m
[2];
436 /* Calculate optimum scale */
437 s
= fmin (drawable
.width
/ w_width
, drawable
.height
/ w_height
);
439 /* Calculate alignment slack */
440 slack
[0] = fmin (1, fmax (0, settings
.align
[0])) * (drawable
.width
- w_width
* s
);
441 slack
[1] = fmin (1, fmax (0, settings
.align
[1])) * (drawable
.height
- w_height
* s
);
443 /* Finally, create and set a cairo transformation matrix that
444 * centres the drawing into the drawable area. */
445 cairo_matrix_init (mtx
, s
, 0, 0, -s
,
446 - wx_min
* s
+ drawable
.x
+ slack
[0],
447 (wy_min
+ w_height
) * s
+ drawable
.y
+ slack
[1]);
450 /* Actually draws a page. If page is NULL, uses the first open page. */
452 export_draw_page (PAGE
*page
)
454 const GList
*contents
;
458 cr
= eda_renderer_get_cairo_context (renderer
);
461 const GList
*pages
= geda_list_get_glist (toplevel
->pages
);
462 g_assert (pages
!= NULL
&& pages
->data
!= NULL
);
463 page
= (PAGE
*) pages
->data
;
466 /* Draw background */
467 eda_cairo_set_source_color (cr
, OUTPUT_BACKGROUND_COLOR
,
468 eda_renderer_get_color_map (renderer
));
471 /* Draw objects & cues */
472 contents
= s_page_objects (page
);
473 for (iter
= (GList
*) contents
; iter
!= NULL
; iter
= g_list_next (iter
))
474 eda_renderer_draw (renderer
, (OBJECT
*) iter
->data
);
475 for (iter
= (GList
*) contents
; iter
!= NULL
; iter
= g_list_next (iter
))
476 eda_renderer_draw_cues (renderer
, (OBJECT
*) iter
->data
);
482 cairo_surface_t
*surface
;
485 cairo_rectangle_t extents
;
486 cairo_status_t status
;
489 /* Create a dummy context to permit calculating extents taking text
491 surface
= cairo_image_surface_create (CAIRO_FORMAT_ARGB32
, 0, 0);
492 cr
= cairo_create (surface
);
493 cairo_surface_destroy (surface
);
495 g_object_set (renderer
,
497 "render-flags", EDA_RENDERER_FLAG_HINTING
,
500 /* Calculate page layout */
501 export_layout_page (NULL
, &extents
, &mtx
);
504 /* Create a rendering surface of the correct size. 'extents' is
505 * measured in points, so we need to use the DPI setting to
506 * transform to pixels. */
507 scale
= settings
.dpi
/ 72.0;
508 surface
= cairo_image_surface_create (CAIRO_FORMAT_ARGB32
,
509 (int) ceil (extents
.width
* scale
),
510 (int) ceil (extents
.height
* scale
));
512 /* Create a cairo context and set the transformation matrix. */
513 cr
= cairo_create (surface
);
514 cairo_scale (cr
, scale
, scale
);
515 cairo_transform (cr
, &mtx
);
517 /* Set up renderer. We need to enable subpixel hinting. */
518 g_object_set (renderer
, "cairo-context", cr
, NULL
);
521 export_draw_page (NULL
);
522 export_cairo_check_error (cairo_surface_status (surface
));
525 status
= cairo_surface_write_to_png (surface
, settings
.outfile
);
526 export_cairo_check_error (status
);
529 /* Worker function used by both export_ps and export_eps */
531 export_postscript (gboolean is_eps
)
533 cairo_surface_t
*surface
;
534 cairo_rectangle_t extents
;
539 /* Create a surface. To begin with, we don't know the size. */
540 surface
= cairo_ps_surface_create (settings
.outfile
, 1, 1);
541 cairo_ps_surface_set_eps (surface
, is_eps
);
542 cr
= cairo_create (surface
);
543 g_object_set (renderer
, "cairo-context", cr
, NULL
);
545 for (iter
= geda_list_get_glist (toplevel
->pages
);
547 iter
= g_list_next (iter
)) {
548 PAGE
*page
= (PAGE
*) iter
->data
;
550 export_layout_page (page
, &extents
, &mtx
);
551 cairo_ps_surface_set_size (surface
, extents
.width
, extents
.height
);
552 cairo_set_matrix (cr
, &mtx
);
553 export_draw_page (page
);
554 cairo_show_page (cr
);
557 cairo_surface_finish (surface
);
558 export_cairo_check_error (cairo_surface_status (surface
));
564 export_postscript (FALSE
);
570 export_postscript (TRUE
);
576 cairo_surface_t
*surface
;
577 cairo_rectangle_t extents
;
582 /* Create a surface. To begin with, we don't know the size. */
583 surface
= cairo_pdf_surface_create (settings
.outfile
, 1, 1);
584 cr
= cairo_create (surface
);
585 g_object_set (renderer
, "cairo-context", cr
, NULL
);
587 for (iter
= geda_list_get_glist (toplevel
->pages
);
589 iter
= g_list_next (iter
)) {
590 PAGE
*page
= (PAGE
*) iter
->data
;
592 export_layout_page (page
, &extents
, &mtx
);
593 cairo_pdf_surface_set_size (surface
, extents
.width
, extents
.height
);
594 cairo_set_matrix (cr
, &mtx
);
595 export_draw_page (page
);
596 cairo_show_page (cr
);
599 cairo_surface_finish (surface
);
600 export_cairo_check_error (cairo_surface_status (surface
));
606 cairo_surface_t
*surface
;
607 cairo_rectangle_t extents
;
611 /* Create a surface. To begin with, we don't know the size. */
612 surface
= cairo_svg_surface_create (settings
.outfile
, 1, 1);
613 cr
= cairo_create (surface
);
614 g_object_set (renderer
, "cairo-context", cr
, NULL
);
616 export_layout_page (NULL
, &extents
, &mtx
);
617 cairo_pdf_surface_set_size (surface
, extents
.width
, extents
.height
);
618 cairo_set_matrix (cr
, &mtx
);
619 export_draw_page (NULL
);
621 cairo_surface_finish (surface
);
622 export_cairo_check_error (cairo_surface_status (surface
));
625 /* Parse a distance specification. A distance specification consists
626 * of a floating point value followed by an optional two-character
627 * unit name (in, cm, mm, pc, px, or pt, same as CSS). If no unit is
628 * specified, assumes that the unit is pt. This is used for the
629 * --margins, --size and --scale command-line options. */
631 export_parse_dist (const gchar
*dist
)
636 base
= strtod(dist
, &unit
);
638 if (errno
!= 0) return -1;
640 if (g_strcmp0 (unit
, "in") == 0) {
642 } else if (g_strcmp0 (unit
, "cm") == 0) {
644 } else if (g_strcmp0 (unit
, "mm") == 0) {
646 } else if (g_strcmp0 (unit
, "pc") == 0) { /* Picas */
648 } else if (g_strcmp0 (unit
, "px") == 0) {
649 mult
= 72.0 / settings
.dpi
;
650 } else if (g_strcmp0 (unit
, "pt") == 0
654 return -1; /* Indicate that parsing unit failed */
660 /* Parse the --align command line option. */
662 export_parse_align (const gchar
*align
)
667 /* Automatic alignment case */
668 if (g_strcmp0 (align
, "auto") == 0 || align
[0] == 0) {
669 settings
.align
[0] = settings
.align
[1] = 0.5;
673 args
= g_strsplit_set (align
, ":; ", 2);
674 for (n
= 0; args
[n
] != NULL
; n
++) {
675 gdouble d
= strtod (args
[n
], NULL
);
676 if (d
< 0 || d
> 1) return FALSE
;
677 settings
.align
[n
] = d
;
681 if (n
!= 2) return FALSE
;
685 /* Parse the --layout command line option and the export.layout config
688 export_parse_layout (const gchar
*layout
)
690 if (g_strcmp0 (layout
, "landscape") == 0) {
691 settings
.layout
= ORIENTATION_LANDSCAPE
;
692 } else if (g_strcmp0 (layout
, "portrait") == 0) {
693 settings
.layout
= ORIENTATION_PORTRAIT
;
694 } else if (g_strcmp0 (layout
, "auto") == 0
697 settings
.layout
= ORIENTATION_AUTO
;
704 /* Parse the --margins command-line option. If the value is "auto" or
705 * empty, sets margins to be determined automatically from paper size
706 * or compiled-in defaults. Otherwise, expects a list of 1-4 distance
707 * specs; see export_parse_dist(). Rules if <4 distances are
708 * specified are as for 'margin' property in CSS. */
710 export_parse_margins (const gchar
*margins
)
715 g_assert (margins
!= NULL
);
717 /* Automatic margins case */
718 if (g_strcmp0 (margins
, "auto") == 0 || margins
[0] == 0) {
719 for (n
= 0; n
< 4; n
++) settings
.margins
[n
] = -1;
723 dists
= g_strsplit_set (margins
, ":; ", 4);
724 for (n
= 0; dists
[n
] != NULL
; n
++) {
725 gdouble d
= export_parse_dist (dists
[n
]);
726 if (d
< 0) return FALSE
;
727 settings
.margins
[n
] = d
;
732 /* If only one value is specified, it applies to all four sides. */
733 settings
.margins
[3] = settings
.margins
[2]
734 = settings
.margins
[1] = settings
.margins
[0];
736 /* If two values are specified, the first applies to the
737 top/bottom, and the second to left/right. */
738 settings
.margins
[2] = settings
.margins
[0];
739 settings
.margins
[3] = settings
.margins
[1];
741 /* If three values are specified, the first applies to the top,
742 the second to left/right, and the third to the bottom. */
743 settings
.margins
[3] = settings
.margins
[1];
745 return FALSE
; /* Must correctly specify 1-4 distances + units */
751 /* Parse the --paper option. Clears any size setting. */
753 export_parse_paper (const gchar
*paper
)
755 GtkPaperSize
*paper_size
= gtk_paper_size_new (paper
);
756 if (paper_size
== NULL
) return FALSE
;
758 if (settings
.paper
!= NULL
) gtk_paper_size_free (settings
.paper
);
759 settings
.paper
= paper_size
;
760 /* Must reset size setting to invalid or it will override paper
762 settings
.size
[0] = settings
.size
[1] = -1;
766 /* Parse the --size option, which must either be "auto" (i.e. obtain
767 * size from drawing) or a list of two distances (width/height). */
769 export_parse_size (const gchar
*size
)
774 /* Automatic size case */
775 if (g_strcmp0 (size
, "auto") == 0 || size
[0] == 0) {
776 settings
.size
[0] = settings
.size
[1] = -1;
780 dists
= g_strsplit_set (size
, ":; ", 2);
781 for (n
= 0; dists
[n
] != NULL
; n
++) {
782 gdouble d
= export_parse_dist (dists
[n
]);
783 if (d
< 0) return FALSE
;
784 settings
.size
[n
] = d
;
787 if (n
!= 2) return FALSE
;
792 /* Parse the --scale option. The value should be a distance
793 * corresponding to 100 points in gschem (1 default grid spacing). */
795 export_parse_scale (const gchar
*scale
)
797 gdouble d
= export_parse_dist (scale
);
798 if (d
<= 0) return FALSE
;
799 settings
.scale
= d
/100;
803 /* Initialise settings from config store. */
807 EdaConfig
*cfg
= eda_config_get_context_for_file (NULL
);
815 /* Parse orientation */
816 str
= eda_config_get_string (cfg
, "export", "layout", NULL
);
817 export_parse_layout (str
); /* Don't care if it works */
820 /* Parse paper size */
821 str
= eda_config_get_string (cfg
, "export", "paper", NULL
);
822 export_parse_paper (str
);
825 /* Parse specific size setting -- always in points */
826 if (eda_config_has_key (cfg
, "export", "size", NULL
)) {
827 lst
= eda_config_get_double_list (cfg
, "export", "size", &n
, NULL
);
830 memcpy (settings
.size
, lst
, 2*sizeof(gdouble
));
834 /* Since a specific size was provided, ditch the paper size
836 if (settings
.paper
!= NULL
) {
837 gtk_paper_size_free (settings
.paper
);
838 settings
.paper
= NULL
;
842 /* Parse margins -- always in points */
843 lst
= eda_config_get_double_list (cfg
, "export", "margins", &n
, NULL
);
845 if (n
>= 4) { /* In the config file all four sides must be specified */
846 memcpy (settings
.margins
, lst
, 4*sizeof(gdouble
));
851 /* Parse alignment */
852 lst
= eda_config_get_double_list (cfg
, "export", "align", &n
, NULL
);
854 if (n
>= 2) { /* Both halign and valign must be specified */
855 memcpy (settings
.align
, lst
, 2*sizeof(gdouble
));
861 dval
= eda_config_get_double (cfg
, "export", "dpi", &err
);
865 g_clear_error (&err
);
868 bval
= eda_config_get_boolean (cfg
, "export", "monochrome", &err
);
870 settings
.color
= !bval
;
872 g_clear_error (&err
);
875 str
= eda_config_get_string (cfg
, "export", "font", NULL
);
877 g_free (settings
.font
);
882 #define export_short_options "a:cd:f:F:hl:m:o:p:s:k:"
884 static struct option export_long_options
[] = {
885 {"no-color", 0, NULL
, 2},
886 {"align", 1, NULL
, 'a'},
887 {"color", 0, NULL
, 'c'},
888 {"dpi", 1, NULL
, 'd'},
889 {"format", 1, NULL
, 'f'},
890 {"font", 1, NULL
, 'F'},
891 {"help", 0, NULL
, 'h'},
892 {"layout", 0, NULL
, 'l'},
893 {"margins", 1, NULL
, 'm'},
894 {"output", 1, NULL
, 'o'},
895 {"paper", 1, NULL
, 'p'},
896 {"size", 1, NULL
, 's'},
897 {"scale", 1, NULL
, 'k'},
904 printf (_("Usage: gaf export [OPTION ...] -o OUTPUT [--] FILE ...\n"
906 "Export gEDA files in various image formats.\n"
908 " -f, --format=TYPE output format (normally autodetected)\n"
909 " -o, --output=OUTPUT output filename\n"
910 " -p, --paper=NAME select paper size by name\n"
911 " -s, --size=WIDTH;HEIGHT specify exact paper size\n"
912 " -k, --scale=FACTOR specify output scale factor\n"
913 " -l, --layout=ORIENT page orientation\n"
914 " -m, --margins=TOP;LEFT;BOTTOM;RIGHT\n"
915 " set page margins\n"
916 " -a, --align=HALIGN;VALIGN\n"
917 " set alignment of drawing within page\n"
918 " -d, --dpi=DPI pixels-per-inch for raster outputs\n"
919 " -c, --color enable color output\n"
920 " --no-color disable color output\n"
921 " -F, --font=NAME set font family for printing text\n"
922 " -h, --help display usage information and exit\n"
924 "Please report bugs to %s.\n"),
929 /* Helper function for checking that a command-line option value can
930 * be successfully converted to UTF-8. */
931 static inline gchar
*
932 export_command_line__utf8_check (gchar
*str
, gchar
*arg
)
937 g_assert (str
!= NULL
);
938 g_assert (arg
!= NULL
);
939 result
= g_locale_to_utf8 (str
, -1, NULL
, NULL
, &err
);
940 if (result
== NULL
) {
941 fprintf (stderr
, bad_arg_msg
, optarg
, arg
);
942 fprintf (stderr
, see_help_msg
);
949 export_command_line (int argc
, char * const *argv
)
954 /* Parse command-line arguments */
955 while ((c
= getopt_long (argc
, argv
, export_short_options
,
956 export_long_options
, NULL
)) != -1) {
959 /* This is a long-form-only flag option, and has already been
960 * dealt with by getopt_long(). */
963 case 2: /* --no-color */
964 settings
.color
= FALSE
;
968 str
= export_command_line__utf8_check (optarg
, "-a,--align");
969 if (!export_parse_align (str
)) {
970 fprintf (stderr
, bad_arg_msg
, optarg
, "-a,--align");
971 fprintf (stderr
, see_help_msg
);
978 settings
.color
= TRUE
;
982 settings
.dpi
= strtod (optarg
, NULL
);
983 if (settings
.dpi
<= 0) {
984 fprintf (stderr
, bad_arg_msg
, optarg
, "-d,--dpi");
985 fprintf (stderr
, see_help_msg
);
991 g_free (settings
.format
);
992 settings
.format
= export_command_line__utf8_check (optarg
, "-f,--format");
996 str
= export_command_line__utf8_check (optarg
, "-F,--font");
997 g_free (settings
.font
);
1006 str
= export_command_line__utf8_check (optarg
, "-k,--scale");
1007 if (!export_parse_scale (str
)) {
1008 fprintf (stderr
, bad_arg_msg
, optarg
, "-k,--scale");
1009 fprintf (stderr
, see_help_msg
);
1013 /* Since a specific scale was provided, ditch the paper size
1015 if (settings
.paper
!= NULL
) {
1016 gtk_paper_size_free (settings
.paper
);
1017 settings
.paper
= NULL
;
1022 if (!export_parse_layout (optarg
)) {
1023 fprintf (stderr
, bad_arg_msg
,
1024 optarg
, "-l,--layout");
1025 fprintf (stderr
, see_help_msg
);
1031 str
= export_command_line__utf8_check (optarg
, "-m,--margins");
1032 if (!export_parse_margins (str
)) {
1033 fprintf (stderr
, bad_arg_msg
, optarg
, "-m,--margins");
1034 fprintf (stderr
, see_help_msg
);
1041 settings
.outfile
= optarg
;
1045 str
= export_command_line__utf8_check (optarg
, "-p,--paper");
1046 if (!export_parse_paper (str
)) {
1047 fprintf (stderr
, bad_arg_msg
, optarg
, "-p,--paper");
1048 fprintf (stderr
, see_help_msg
);
1055 str
= export_command_line__utf8_check (optarg
, "-s,--size");
1056 if (!export_parse_size (str
)) {
1057 fprintf (stderr
, bad_arg_msg
, optarg
, "-s,--size");
1058 fprintf (stderr
, see_help_msg
);
1062 /* Since a specific size was provided, ditch the paper size
1064 if (settings
.paper
!= NULL
) {
1065 gtk_paper_size_free (settings
.paper
);
1066 settings
.paper
= NULL
;
1071 /* getopt_long already printed an error message */
1072 fprintf (stderr
, see_help_msg
);
1076 g_assert_not_reached ();
1080 /* Check that some schematic files to print were provided */
1081 if (argc
<= optind
) {
1083 _("ERROR: You must specify at least one input filename.\n"));
1084 fprintf (stderr
, see_help_msg
);
1087 settings
.infilec
= argc
- optind
;
1088 settings
.infilev
= &argv
[optind
];
1090 if (settings
.outfile
== NULL
) {
1092 _("ERROR: You must specify an output filename.\n"));
1093 fprintf (stderr
, see_help_msg
);