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
30 /* Gettext translation */
33 #include <libgeda/libgeda.h>
34 #include <libgeda/libgedaguile.h>
35 #include <libgedacairo/libgedacairo.h>
38 #include <glib/gstdio.h>
40 #include <cairo-svg.h>
41 #include <cairo-pdf.h>
44 static int export_text_rendered_bounds (void *user_data
,
47 int *right
, int *bottom
);
48 static void export_layout_page (PAGE
*page
, cairo_rectangle_t
*extents
,
50 static void export_draw_page (PAGE
*page
);
52 static void export_png (void);
53 static void export_postscript (gboolean is_eps
);
54 static void export_ps (void);
55 static void export_eps (void);
56 static void export_pdf (void);
57 static void export_svg (void);
59 static gdouble
export_parse_dist (const gchar
*dist
);
60 static gboolean
export_parse_scale (const gchar
*scale
);
61 static gboolean
export_parse_layout (const gchar
*layout
);
62 static gboolean
export_parse_margins (const gchar
*margins
);
63 static gboolean
export_parse_paper (const gchar
*paper
);
64 static gboolean
export_parse_size (const gchar
*size
);
65 static void export_config (void);
66 static void export_usage (void);
67 static void export_command_line (int argc
, char * const *argv
);
69 /* Default pixels-per-inch for raster outputs */
70 #define DEFAULT_DPI 96
71 /* Default margin width in points */
72 #define DEFAULT_MARGIN 18
74 enum ExportFormatFlags
{
81 gchar
*name
; /* UTF-8 */
82 gchar
*alias
; /* UTF-8 */
87 enum ExportOrientation
{
89 ORIENTATION_LANDSCAPE
,
93 struct ExportSettings
{
96 char * const *infilev
; /* Filename encoding */
97 const char *outfile
; /* Filename encoding */
98 gchar
*format
; /* UTF-8 */
100 enum ExportOrientation layout
;
103 gdouble scale
; /* Output scale; defaults to 1 mil per 1 gschem point*/
104 gdouble size
[2]; /* Points */
105 gdouble margins
[4]; /* Points. Top, right, bottom, left. */
106 gdouble align
[2]; /* 0.0 < align < 1.0 for halign and valign */
110 gchar
*font
; /* UTF-8 */
113 static struct ExportFormat formats
[] =
115 {"Portable Network Graphics (PNG)", "png", OUTPUT_PIXELS
, export_png
},
116 {"Postscript (PS)", "ps", OUTPUT_POINTS
| OUTPUT_MULTIPAGE
, export_ps
},
117 {"Encapsulated Postscript (EPS)", "eps", OUTPUT_POINTS
, export_eps
},
118 {"Portable Document Format (PDF)", "pdf", OUTPUT_POINTS
| OUTPUT_MULTIPAGE
, export_pdf
},
119 {"Scalable Vector Graphics (SVG)", "svg", OUTPUT_POINTS
, export_svg
},
120 {NULL
, NULL
, 0, NULL
},
123 static EdaRenderer
*renderer
= NULL
;
124 static TOPLEVEL
*toplevel
= NULL
;
126 static struct ExportSettings settings
= {
145 #define bad_arg_msg _("ERROR: Bad argument '%s' to %s option.\n")
146 #define see_help_msg _("\nRun `gaf export --help' for more information.\n")
149 cmd_export_impl (void *data
, int argc
, char **argv
)
154 const gchar
*out_suffix
;
155 struct ExportFormat
*exporter
= NULL
;
156 GArray
*render_color_map
= NULL
;
157 gchar
*original_cwd
= g_get_current_dir ();
159 gtk_init_check (&argc
, &argv
);
162 scm_dynwind_begin (0);
163 toplevel
= s_toplevel_new ();
164 edascm_dynwind_toplevel (toplevel
);
166 /* Now load rc files, if necessary */
167 if (getenv ("GAF_INHIBIT_RCFILES") == NULL
) {
168 g_rc_parse (toplevel
, "gaf export", NULL
, NULL
);
170 i_vars_libgeda_set (toplevel
); /* Ugh */
172 /* Parse configuration files */
175 /* Parse command-line arguments */
176 export_command_line (argc
, argv
);
178 /* If no format was specified, try and guess from output
180 if (settings
.format
== NULL
) {
181 out_suffix
= strrchr (settings
.outfile
, '.');
182 if (out_suffix
!= NULL
) {
183 out_suffix
++; /* Skip '.' */
186 _("ERROR: Cannot infer output format from filename '%s'.\n"),
192 /* Try and find an exporter function */
193 tmp
= g_utf8_strdown ((settings
.format
== NULL
) ? out_suffix
: settings
.format
, -1);
194 for (i
= 0; formats
[i
].name
!= NULL
; i
++) {
195 if (strcmp (tmp
, formats
[i
].alias
) == 0) {
196 exporter
= &formats
[i
];
200 if (exporter
== NULL
) {
201 if (settings
.format
== NULL
) {
203 _("ERROR: Cannot find supported format for filename '%s'.\n"),
208 _("ERROR: Unsupported output format '%s'.\n"),
210 fprintf (stderr
, see_help_msg
);
216 /* If more than one schematic/symbol file was specified, check that
217 * exporter supports multipage output. */
218 if ((settings
.infilec
> 1) && !(exporter
->flags
& OUTPUT_MULTIPAGE
)) {
220 _("ERROR: Selected output format does not support multipage output\n"));
224 /* Load schematic files */
225 while (optind
< argc
) {
227 tmp
= argv
[optind
++];
229 page
= s_page_new (toplevel
, tmp
);
230 if (!f_open (toplevel
, page
, tmp
, &err
)) {
232 _("ERROR: Failed to load '%s': %s\n"), tmp
,
236 if (g_chdir (original_cwd
) != 0) {
238 _("ERROR: Failed to change directory to '%s': %s\n"),
239 original_cwd
, g_strerror (errno
));
244 /* Create renderer */
245 renderer
= eda_renderer_new (NULL
, NULL
);
246 if (settings
.font
!= NULL
) {
247 g_object_set (renderer
, "font-name", settings
.font
, NULL
);
250 /* Make sure libgeda knows how to calculate the bounds of text
251 * taking into account font etc. */
252 o_text_set_rendered_bounds_func (toplevel
,
253 export_text_rendered_bounds
,
256 /* Create color map */
258 g_array_sized_new (FALSE
, FALSE
, sizeof(COLOR
), MAX_COLORS
);
260 g_array_append_vals (render_color_map
, print_colors
, MAX_COLORS
);
261 if (!settings
.color
) {
262 /* Create a black and white color map. All non-background colors
264 COLOR white
= {~0, ~0, ~0, ~0, TRUE
};
265 COLOR black
= {0, 0, 0, ~0, TRUE
};
266 for (i
= 0; i
< MAX_COLORS
; i
++) {
267 COLOR
*c
= &g_array_index (render_color_map
, COLOR
, i
);
268 if (!c
->enabled
) continue;
275 if (i
== OUTPUT_BACKGROUND_COLOR
) {
282 eda_renderer_set_color_map (renderer
, render_color_map
);
291 /* Callback function registered with libgeda to allow the libgeda
292 * "bounds" functions to get text bounds using the renderer. If a
293 * "rendered bounds" function isn't provided, text objects don't get
294 * used when calculating the extents of the drawing. */
296 export_text_rendered_bounds (void *user_data
, OBJECT
*object
,
297 int *left
, int *top
, int *right
, int *bottom
)
301 EdaRenderer
*renderer
= EDA_RENDERER (user_data
);
302 result
= eda_renderer_get_user_bounds (renderer
, object
, &l
, &t
, &r
, &b
);
304 *left
= lrint (fmin (l
,r
));
305 *top
= lrint (fmin (t
, b
));
306 *right
= lrint (fmax (l
, r
));
307 *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_rectangle_t drawable
;
333 int wx_min
, wy_min
, wx_max
, wy_max
, w_width
, w_height
;
334 gboolean landscape
= FALSE
;
335 gdouble m
[4]; /* Calculated margins */
336 gdouble s
; /* Calculated scale */
337 gdouble slack
[2]; /* Calculated alignment slack */
340 const GList
*pages
= geda_list_get_glist (toplevel
->pages
);
341 g_assert (pages
!= NULL
&& pages
->data
!= NULL
);
342 page
= (PAGE
*) pages
->data
;
345 /* Set the margins. If none were provided by the user, get them
346 * from the paper size (if a paper size is being used) or just use a
347 * sensible default. */
348 if (settings
.margins
[0] >= 0) {
349 memcpy (m
, settings
.margins
, 4*sizeof(gdouble
));
350 } else if (settings
.paper
!= NULL
) {
351 m
[0] = gtk_paper_size_get_default_top_margin (settings
.paper
, GTK_UNIT_POINTS
);
352 m
[1] = gtk_paper_size_get_default_left_margin (settings
.paper
, GTK_UNIT_POINTS
);
353 m
[2] = gtk_paper_size_get_default_bottom_margin (settings
.paper
, GTK_UNIT_POINTS
);
354 m
[3] = gtk_paper_size_get_default_right_margin (settings
.paper
, GTK_UNIT_POINTS
);
356 m
[0] = DEFAULT_MARGIN
;
357 m
[1] = DEFAULT_MARGIN
;
358 m
[2] = DEFAULT_MARGIN
;
359 m
[3] = DEFAULT_MARGIN
;
362 /* Now calculate extents of objects within page */
363 if (!world_get_object_glist_bounds (toplevel
, s_page_objects (page
),
364 &wx_min
, &wy_min
, &wx_max
, &wy_max
))
365 wx_min
= wy_min
= wx_max
= wy_max
= 0;
366 w_width
= wx_max
- wx_min
;
367 w_height
= wy_max
- wy_min
;
369 /* If a size was specified, use it. Otherwise, use paper size, if
370 * provided. Fall back to just using the size of the drawing. */
371 extents
->x
= extents
->y
= 0;
372 if (settings
.size
[0] >= 0) {
373 /* get extents from size */
375 extents
->width
= settings
.size
[0];
376 extents
->height
= settings
.size
[1];
378 } else if (settings
.paper
!= NULL
) {
379 /* get extents from paper */
381 gdouble p_width
, p_height
;
383 /* Select orientation */
384 switch (settings
.layout
) {
385 case ORIENTATION_LANDSCAPE
:
388 case ORIENTATION_PORTRAIT
:
391 case ORIENTATION_AUTO
:
393 landscape
= (w_width
> w_height
);
397 p_width
= gtk_paper_size_get_width (settings
.paper
, GTK_UNIT_POINTS
);
398 p_height
= gtk_paper_size_get_height (settings
.paper
, GTK_UNIT_POINTS
);
401 extents
->width
= p_height
;
402 extents
->height
= p_width
;
404 extents
->width
= p_width
;
405 extents
->height
= p_height
;
408 /* get extents from drawing */
410 extents
->width
= w_width
* settings
.scale
; /* in points */
411 extents
->height
= w_height
* settings
.scale
; /* in points */
413 /* If the extents were obtained from the drawing, grow the extents
414 * rather than shrinking the drawable area. This ensures that the
415 * overall aspect ratio of the image remains correct. */
416 extents
->width
+= m
[1] + m
[3];
417 extents
->height
+= m
[0] + m
[2];
423 drawable
.width
= extents
->width
- m
[1] - m
[3];
424 drawable
.height
= extents
->height
- m
[0] - m
[2];
426 /* Calculate optimum scale */
427 s
= fmin (drawable
.width
/ w_width
, drawable
.height
/ w_height
);
429 /* Calculate alignment slack */
430 slack
[0] = fmin (1, fmax (0, settings
.align
[0])) * (drawable
.width
- w_width
* s
);
431 slack
[1] = fmin (1, fmax (0, settings
.align
[1])) * (drawable
.height
- w_height
* s
);
433 /* Finally, create and set a cairo transformation matrix that
434 * centres the drawing into the drawable area. */
435 cairo_matrix_init (mtx
, s
, 0, 0, -s
,
436 - wx_min
* s
+ drawable
.x
+ slack
[0],
437 (wy_min
+ w_height
) * s
+ drawable
.y
+ slack
[1]);
440 /* Actually draws a page. If page is NULL, uses the first open page. */
442 export_draw_page (PAGE
*page
)
444 const GList
*contents
;
448 cr
= eda_renderer_get_cairo_context (renderer
);
451 const GList
*pages
= geda_list_get_glist (toplevel
->pages
);
452 g_assert (pages
!= NULL
&& pages
->data
!= NULL
);
453 page
= (PAGE
*) pages
->data
;
456 /* Draw background */
457 eda_cairo_set_source_color (cr
, OUTPUT_BACKGROUND_COLOR
,
458 eda_renderer_get_color_map (renderer
));
461 /* Draw objects & cues */
462 contents
= s_page_objects (page
);
463 for (iter
= (GList
*) contents
; iter
!= NULL
; iter
= g_list_next (iter
))
464 eda_renderer_draw (renderer
, (OBJECT
*) iter
->data
);
465 for (iter
= (GList
*) contents
; iter
!= NULL
; iter
= g_list_next (iter
))
466 eda_renderer_draw_cues (renderer
, (OBJECT
*) iter
->data
);
472 cairo_surface_t
*surface
;
475 cairo_rectangle_t extents
;
476 cairo_status_t status
;
479 /* Create a dummy context to permit calculating extents taking text
481 surface
= cairo_image_surface_create (CAIRO_FORMAT_ARGB32
, 0, 0);
482 cr
= cairo_create (surface
);
483 cairo_surface_destroy (surface
);
485 g_object_set (renderer
,
487 "render-flags", EDA_RENDERER_FLAG_HINTING
,
490 /* Calculate page layout */
491 export_layout_page (NULL
, &extents
, &mtx
);
494 /* Create a rendering surface of the correct size. 'extents' is
495 * measured in points, so we need to use the DPI setting to
496 * transform to pixels. */
497 scale
= settings
.dpi
/ 72.0;
498 surface
= cairo_image_surface_create (CAIRO_FORMAT_ARGB32
,
499 (int) ceil (extents
.width
* scale
),
500 (int) ceil (extents
.height
* scale
));
502 /* Create a cairo context and set the transformation matrix. */
503 cr
= cairo_create (surface
);
504 cairo_scale (cr
, scale
, scale
);
505 cairo_transform (cr
, &mtx
);
507 /* Set up renderer. We need to enable subpixel hinting. */
508 g_object_set (renderer
, "cairo-context", cr
, NULL
);
511 export_draw_page (NULL
);
512 export_cairo_check_error (cairo_surface_status (surface
));
515 status
= cairo_surface_write_to_png (surface
, settings
.outfile
);
516 export_cairo_check_error (status
);
519 /* Worker function used by both export_ps and export_eps */
521 export_postscript (gboolean is_eps
)
523 cairo_surface_t
*surface
;
524 cairo_rectangle_t extents
;
529 /* Create a surface. To begin with, we don't know the size. */
530 surface
= cairo_ps_surface_create (settings
.outfile
, 1, 1);
531 cairo_ps_surface_set_eps (surface
, is_eps
);
532 cr
= cairo_create (surface
);
533 g_object_set (renderer
, "cairo-context", cr
, NULL
);
535 for (iter
= geda_list_get_glist (toplevel
->pages
);
537 iter
= g_list_next (iter
)) {
538 PAGE
*page
= (PAGE
*) iter
->data
;
540 export_layout_page (page
, &extents
, &mtx
);
541 cairo_ps_surface_set_size (surface
, extents
.width
, extents
.height
);
542 cairo_set_matrix (cr
, &mtx
);
543 export_draw_page (page
);
544 cairo_show_page (cr
);
547 cairo_surface_finish (surface
);
548 export_cairo_check_error (cairo_surface_status (surface
));
554 export_postscript (FALSE
);
560 export_postscript (TRUE
);
566 cairo_surface_t
*surface
;
567 cairo_rectangle_t extents
;
572 /* Create a surface. To begin with, we don't know the size. */
573 surface
= cairo_pdf_surface_create (settings
.outfile
, 1, 1);
574 cr
= cairo_create (surface
);
575 g_object_set (renderer
, "cairo-context", cr
, NULL
);
577 for (iter
= geda_list_get_glist (toplevel
->pages
);
579 iter
= g_list_next (iter
)) {
580 PAGE
*page
= (PAGE
*) iter
->data
;
582 export_layout_page (page
, &extents
, &mtx
);
583 cairo_pdf_surface_set_size (surface
, extents
.width
, extents
.height
);
584 cairo_set_matrix (cr
, &mtx
);
585 export_draw_page (page
);
586 cairo_show_page (cr
);
589 cairo_surface_finish (surface
);
590 export_cairo_check_error (cairo_surface_status (surface
));
596 cairo_surface_t
*surface
;
597 cairo_rectangle_t extents
;
601 /* Create a surface and run export_layout_page() to figure out
602 * the picture extents and set up the cairo transformation
603 * matrix. The surface is created only in order to force
604 * eda_renderer_default_get_user_bounds() to behave quietly. */
605 surface
= cairo_svg_surface_create (settings
.outfile
, 0, 0);
606 cr
= cairo_create (surface
);
607 g_object_set (renderer
, "cairo-context", cr
, NULL
);
608 export_layout_page (NULL
, &extents
, &mtx
);
611 /* Now create a new surface with the known extents. */
612 surface
= cairo_svg_surface_create (settings
.outfile
,
615 cr
= cairo_create (surface
);
616 g_object_set (renderer
, "cairo-context", cr
, NULL
);
618 cairo_set_matrix (cr
, &mtx
);
619 export_draw_page (NULL
);
621 cairo_show_page (cr
);
622 cairo_surface_finish (surface
);
623 export_cairo_check_error (cairo_surface_status (surface
));
626 /* Parse a distance specification. A distance specification consists
627 * of a floating point value followed by an optional two-character
628 * unit name (in, cm, mm, pc, px, or pt, same as CSS). If no unit is
629 * specified, assumes that the unit is pt. This is used for the
630 * --margins, --size and --scale command-line options. */
632 export_parse_dist (const gchar
*dist
)
637 base
= strtod(dist
, &unit
);
639 if (errno
!= 0) return -1;
641 if (g_strcmp0 (unit
, "in") == 0) {
643 } else if (g_strcmp0 (unit
, "cm") == 0) {
645 } else if (g_strcmp0 (unit
, "mm") == 0) {
647 } else if (g_strcmp0 (unit
, "pc") == 0) { /* Picas */
649 } else if (g_strcmp0 (unit
, "px") == 0) {
650 mult
= 72.0 / settings
.dpi
;
651 } else if (g_strcmp0 (unit
, "pt") == 0
655 return -1; /* Indicate that parsing unit failed */
661 /* Parse the --align command line option. */
663 export_parse_align (const gchar
*align
)
668 /* Automatic alignment case */
669 if (g_strcmp0 (align
, "auto") == 0 || align
[0] == 0) {
670 settings
.align
[0] = settings
.align
[1] = 0.5;
674 args
= g_strsplit_set (align
, ":; ", 2);
675 for (n
= 0; args
[n
] != NULL
; n
++) {
676 gdouble d
= strtod (args
[n
], NULL
);
677 if (d
< 0 || d
> 1) return FALSE
;
678 settings
.align
[n
] = d
;
682 if (n
!= 2) return FALSE
;
686 /* Parse the --layout command line option and the export.layout config
689 export_parse_layout (const gchar
*layout
)
691 if (g_strcmp0 (layout
, "landscape") == 0) {
692 settings
.layout
= ORIENTATION_LANDSCAPE
;
693 } else if (g_strcmp0 (layout
, "portrait") == 0) {
694 settings
.layout
= ORIENTATION_PORTRAIT
;
695 } else if (g_strcmp0 (layout
, "auto") == 0
698 settings
.layout
= ORIENTATION_AUTO
;
705 /* Parse the --margins command-line option. If the value is "auto" or
706 * empty, sets margins to be determined automatically from paper size
707 * or compiled-in defaults. Otherwise, expects a list of 1-4 distance
708 * specs; see export_parse_dist(). Rules if <4 distances are
709 * specified are as for 'margin' property in CSS. */
711 export_parse_margins (const gchar
*margins
)
716 g_assert (margins
!= NULL
);
718 /* Automatic margins case */
719 if (g_strcmp0 (margins
, "auto") == 0 || margins
[0] == 0) {
720 for (n
= 0; n
< 4; n
++) settings
.margins
[n
] = -1;
724 dists
= g_strsplit_set (margins
, ":; ", 4);
725 for (n
= 0; dists
[n
] != NULL
; n
++) {
726 gdouble d
= export_parse_dist (dists
[n
]);
727 if (d
< 0) return FALSE
;
728 settings
.margins
[n
] = d
;
733 /* If only one value is specified, it applies to all four sides. */
734 settings
.margins
[3] = settings
.margins
[2]
735 = settings
.margins
[1] = settings
.margins
[0];
737 /* If two values are specified, the first applies to the
738 top/bottom, and the second to left/right. */
739 settings
.margins
[2] = settings
.margins
[0];
740 settings
.margins
[3] = settings
.margins
[1];
742 /* If three values are specified, the first applies to the top,
743 the second to left/right, and the third to the bottom. */
744 settings
.margins
[3] = settings
.margins
[1];
746 return FALSE
; /* Must correctly specify 1-4 distances + units */
752 /* Parse the --paper option. Clears any size setting. */
754 export_parse_paper (const gchar
*paper
)
756 GtkPaperSize
*paper_size
= gtk_paper_size_new (paper
);
757 if (paper_size
== NULL
) return FALSE
;
759 if (settings
.paper
!= NULL
) gtk_paper_size_free (settings
.paper
);
760 settings
.paper
= paper_size
;
761 /* Must reset size setting to invalid or it will override paper
763 settings
.size
[0] = settings
.size
[1] = -1;
767 /* Parse the --size option, which must either be "auto" (i.e. obtain
768 * size from drawing) or a list of two distances (width/height). */
770 export_parse_size (const gchar
*size
)
775 /* Automatic size case */
776 if (g_strcmp0 (size
, "auto") == 0 || size
[0] == 0) {
777 settings
.size
[0] = settings
.size
[1] = -1;
781 dists
= g_strsplit_set (size
, ":; ", 2);
782 for (n
= 0; dists
[n
] != NULL
; n
++) {
783 gdouble d
= export_parse_dist (dists
[n
]);
784 if (d
< 0) return FALSE
;
785 settings
.size
[n
] = d
;
788 if (n
!= 2) return FALSE
;
793 /* Parse the --scale option. The value should be a distance
794 * corresponding to 100 points in gschem (1 default grid spacing). */
796 export_parse_scale (const gchar
*scale
)
798 gdouble d
= export_parse_dist (scale
);
799 if (d
<= 0) return FALSE
;
800 settings
.scale
= d
/100;
804 /* Initialise settings from config store. */
808 EdaConfig
*cfg
= eda_config_get_context_for_file (NULL
);
816 /* Parse orientation */
817 str
= eda_config_get_string (cfg
, "export", "layout", NULL
);
818 export_parse_layout (str
); /* Don't care if it works */
821 /* Parse paper size */
822 str
= eda_config_get_string (cfg
, "export", "paper", NULL
);
823 export_parse_paper (str
);
826 /* Parse specific size setting -- always in points */
827 if (eda_config_has_key (cfg
, "export", "size", NULL
)) {
828 lst
= eda_config_get_double_list (cfg
, "export", "size", &n
, NULL
);
831 memcpy (settings
.size
, lst
, 2*sizeof(gdouble
));
835 /* Since a specific size was provided, ditch the paper size
837 if (settings
.paper
!= NULL
) {
838 gtk_paper_size_free (settings
.paper
);
839 settings
.paper
= NULL
;
843 /* Parse margins -- always in points */
844 lst
= eda_config_get_double_list (cfg
, "export", "margins", &n
, NULL
);
846 if (n
>= 4) { /* In the config file all four sides must be specified */
847 memcpy (settings
.margins
, lst
, 4*sizeof(gdouble
));
852 /* Parse alignment */
853 lst
= eda_config_get_double_list (cfg
, "export", "align", &n
, NULL
);
855 if (n
>= 2) { /* Both halign and valign must be specified */
856 memcpy (settings
.align
, lst
, 2*sizeof(gdouble
));
862 dval
= eda_config_get_double (cfg
, "export", "dpi", &err
);
866 g_clear_error (&err
);
869 bval
= eda_config_get_boolean (cfg
, "export", "monochrome", &err
);
871 settings
.color
= !bval
;
873 g_clear_error (&err
);
876 str
= eda_config_get_string (cfg
, "export", "font", NULL
);
878 g_free (settings
.font
);
883 #define export_short_options "a:cd:f:F:hl:m:o:p:s:k:"
885 static struct option export_long_options
[] = {
886 {"no-color", 0, NULL
, 2},
887 {"align", 1, NULL
, 'a'},
888 {"color", 0, NULL
, 'c'},
889 {"dpi", 1, NULL
, 'd'},
890 {"format", 1, NULL
, 'f'},
891 {"font", 1, NULL
, 'F'},
892 {"help", 0, NULL
, 'h'},
893 {"layout", 1, NULL
, 'l'},
894 {"margins", 1, NULL
, 'm'},
895 {"output", 1, NULL
, 'o'},
896 {"paper", 1, NULL
, 'p'},
897 {"size", 1, NULL
, 's'},
898 {"scale", 1, NULL
, 'k'},
905 printf (_("Usage: gaf export [OPTION ...] -o OUTPUT [--] FILE ...\n"
907 "Export gEDA files in various image formats.\n"
909 " -f, --format=TYPE output format (normally autodetected)\n"
910 " -o, --output=OUTPUT output filename\n"
911 " -p, --paper=NAME select paper size by name\n"
912 " -s, --size=WIDTH;HEIGHT specify exact paper size\n"
913 " -k, --scale=FACTOR specify output scale factor\n"
914 " -l, --layout=ORIENT page orientation\n"
915 " -m, --margins=TOP;LEFT;BOTTOM;RIGHT\n"
916 " set page margins\n"
917 " -a, --align=HALIGN;VALIGN\n"
918 " set alignment of drawing within page\n"
919 " -d, --dpi=DPI pixels-per-inch for raster outputs\n"
920 " -c, --color enable color output\n"
921 " --no-color disable color output\n"
922 " -F, --font=NAME set font family for printing text\n"
923 " -h, --help display usage information and exit\n"
925 "Please report bugs to %s.\n"),
930 /* Helper function for checking that a command-line option value can
931 * be successfully converted to UTF-8. */
932 static inline gchar
*
933 export_command_line__utf8_check (gchar
*str
, gchar
*arg
)
938 g_assert (str
!= NULL
);
939 g_assert (arg
!= NULL
);
940 result
= g_locale_to_utf8 (str
, -1, NULL
, NULL
, &err
);
941 if (result
== NULL
) {
942 fprintf (stderr
, bad_arg_msg
, optarg
, arg
);
943 fprintf (stderr
, see_help_msg
);
950 export_command_line (int argc
, char * const *argv
)
955 /* Parse command-line arguments */
956 while ((c
= getopt_long (argc
, argv
, export_short_options
,
957 export_long_options
, NULL
)) != -1) {
960 /* This is a long-form-only flag option, and has already been
961 * dealt with by getopt_long(). */
964 case 2: /* --no-color */
965 settings
.color
= FALSE
;
969 str
= export_command_line__utf8_check (optarg
, "-a,--align");
970 if (!export_parse_align (str
)) {
971 fprintf (stderr
, bad_arg_msg
, optarg
, "-a,--align");
972 fprintf (stderr
, see_help_msg
);
979 settings
.color
= TRUE
;
983 settings
.dpi
= strtod (optarg
, NULL
);
984 if (settings
.dpi
<= 0) {
985 fprintf (stderr
, bad_arg_msg
, optarg
, "-d,--dpi");
986 fprintf (stderr
, see_help_msg
);
992 g_free (settings
.format
);
993 settings
.format
= export_command_line__utf8_check (optarg
, "-f,--format");
997 str
= export_command_line__utf8_check (optarg
, "-F,--font");
998 g_free (settings
.font
);
1007 str
= export_command_line__utf8_check (optarg
, "-k,--scale");
1008 if (!export_parse_scale (str
)) {
1009 fprintf (stderr
, bad_arg_msg
, optarg
, "-k,--scale");
1010 fprintf (stderr
, see_help_msg
);
1014 /* Since a specific scale was provided, ditch the paper size
1016 if (settings
.paper
!= NULL
) {
1017 gtk_paper_size_free (settings
.paper
);
1018 settings
.paper
= NULL
;
1023 if (!export_parse_layout (optarg
)) {
1024 fprintf (stderr
, bad_arg_msg
,
1025 optarg
, "-l,--layout");
1026 fprintf (stderr
, see_help_msg
);
1032 str
= export_command_line__utf8_check (optarg
, "-m,--margins");
1033 if (!export_parse_margins (str
)) {
1034 fprintf (stderr
, bad_arg_msg
, optarg
, "-m,--margins");
1035 fprintf (stderr
, see_help_msg
);
1042 settings
.outfile
= optarg
;
1046 str
= export_command_line__utf8_check (optarg
, "-p,--paper");
1047 if (!export_parse_paper (str
)) {
1048 fprintf (stderr
, bad_arg_msg
, optarg
, "-p,--paper");
1049 fprintf (stderr
, see_help_msg
);
1056 str
= export_command_line__utf8_check (optarg
, "-s,--size");
1057 if (!export_parse_size (str
)) {
1058 fprintf (stderr
, bad_arg_msg
, optarg
, "-s,--size");
1059 fprintf (stderr
, see_help_msg
);
1063 /* Since a specific size was provided, ditch the paper size
1065 if (settings
.paper
!= NULL
) {
1066 gtk_paper_size_free (settings
.paper
);
1067 settings
.paper
= NULL
;
1072 /* getopt_long already printed an error message */
1073 fprintf (stderr
, see_help_msg
);
1077 g_assert_not_reached ();
1081 /* Check that some schematic files to print were provided */
1082 if (argc
<= optind
) {
1084 _("ERROR: You must specify at least one input filename.\n"));
1085 fprintf (stderr
, see_help_msg
);
1088 settings
.infilec
= argc
- optind
;
1089 settings
.infilev
= &argv
[optind
];
1091 if (settings
.outfile
== NULL
) {
1093 _("ERROR: You must specify an output filename.\n"));
1094 fprintf (stderr
, see_help_msg
);
1099 /* Main function for `gaf export' */
1101 cmd_export (int argc
, char **argv
)
1103 scm_boot_guile (argc
, argv
, cmd_export_impl
, NULL
); /* Doesn't return */