532 lines
16 KiB
C
532 lines
16 KiB
C
/*****************************************************************************/
|
|
/* LibreDWG - free implementation of the DWG file format */
|
|
/* */
|
|
/* Copyright (C) 2013-2020,2023 Free Software Foundation, Inc. */
|
|
/* */
|
|
/* This library is free software, licensed under the terms of the GNU */
|
|
/* General Public License as published by the Free Software Foundation, */
|
|
/* either version 3 of the License, or (at your option) any later version. */
|
|
/* You should have received a copy of the GNU General Public License */
|
|
/* along with this program. If not, see <http://www.gnu.org/licenses/>. */
|
|
/*****************************************************************************/
|
|
|
|
/*
|
|
* dwg2svg2.c: convert a DWG to SVG via the API.
|
|
if there are paperspace entities, only output them. else all modelspace
|
|
entities.
|
|
* written by Gaganjyot Singh
|
|
* modified by Reini Urban
|
|
*/
|
|
|
|
#include "../src/config.h"
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
#include <math.h>
|
|
#include <getopt.h>
|
|
|
|
#ifdef ENABLE_MIMALLOC
|
|
# include <mimalloc-override.h>
|
|
#endif
|
|
|
|
#include <dwg.h>
|
|
#include <dwg_api.h>
|
|
#include "geom.h"
|
|
|
|
static int opts = 0;
|
|
static dwg_data g_dwg;
|
|
static double model_xmin, model_ymin;
|
|
static double page_width, page_height, scale;
|
|
|
|
static int
|
|
usage (void)
|
|
{
|
|
printf ("\nUsage: dwg2svg2 [-v[0-9]] DWGFILE\n");
|
|
return 1;
|
|
}
|
|
static int
|
|
opt_version (void)
|
|
{
|
|
printf ("dwg2svg2 %s\n", PACKAGE_VERSION);
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
help (void)
|
|
{
|
|
printf ("\nUsage: dwg2svg2 [OPTION]... DWGFILE >file.svg\n");
|
|
printf ("Example to use the DWG api\n"
|
|
"\n");
|
|
#ifdef HAVE_GETOPT_LONG
|
|
printf (" -v[0-9], --verbose [0-9] verbosity\n");
|
|
printf (" --help display this help and exit\n");
|
|
printf (" --version output version information and exit\n"
|
|
"\n");
|
|
#else
|
|
printf (" -v[0-9] verbosity\n");
|
|
printf (" -h display this help and exit\n");
|
|
printf (" -i output version information and exit\n"
|
|
"\n");
|
|
#endif
|
|
printf ("GNU LibreDWG online manual: "
|
|
"<https://www.gnu.org/software/libredwg/>\n");
|
|
return 0;
|
|
}
|
|
|
|
#define log_if_error(msg) \
|
|
if (error) \
|
|
{ \
|
|
fprintf (stderr, "ERROR: %s", msg); \
|
|
exit (1); \
|
|
}
|
|
#define log_error(msg) \
|
|
{ \
|
|
fprintf (stderr, "ERROR: %s", msg); \
|
|
exit (1); \
|
|
}
|
|
#define dynget(obj, name, field, var) \
|
|
if (!dwg_dynapi_entity_value (obj, "" name, "" field, var, NULL)) \
|
|
{ \
|
|
fprintf (stderr, "ERROR: %s.%s", name, field); \
|
|
exit (1); \
|
|
}
|
|
#define dynget_utf8(obj, name, field, var) \
|
|
if (!dwg_dynapi_entity_utf8text (obj, "" name, "" field, var, &isnew, \
|
|
NULL)) \
|
|
{ \
|
|
fprintf (stderr, "ERROR: %s.%s", name, field); \
|
|
exit (1); \
|
|
}
|
|
|
|
static double
|
|
transform_X (double x)
|
|
{
|
|
return x - model_xmin;
|
|
}
|
|
|
|
static double
|
|
transform_Y (double y)
|
|
{
|
|
return page_height - (y - model_ymin);
|
|
}
|
|
|
|
static void output_SVG (dwg_data *dwg);
|
|
|
|
static int
|
|
test_SVG (char *filename)
|
|
{
|
|
int error;
|
|
|
|
memset (&g_dwg, 0, sizeof (dwg_data));
|
|
g_dwg.opts = opts;
|
|
error = dwg_read_file (filename, &g_dwg);
|
|
if (error < DWG_ERR_CRITICAL)
|
|
output_SVG (&g_dwg);
|
|
|
|
dwg_free (&g_dwg);
|
|
/* This value is the return value for `main',
|
|
so clamp it to either 0 or 1. */
|
|
return error < DWG_ERR_CRITICAL ? 0 : 1;
|
|
}
|
|
|
|
static void
|
|
output_TEXT (dwg_object *obj)
|
|
{
|
|
int error, index;
|
|
dwg_point_2d ins_pt;
|
|
Dwg_Entity_TEXT *text;
|
|
char *text_value;
|
|
double fontsize;
|
|
const Dwg_Version_Type dwg_version = obj->parent->header.version;
|
|
int isnew = 0;
|
|
|
|
index = dwg_object_get_index (obj, &error);
|
|
log_if_error ("object_get_index");
|
|
text = dwg_object_to_TEXT (obj);
|
|
if (!text)
|
|
log_error ("dwg_object_to_TEXT");
|
|
dynget_utf8 (text, "TEXT", "text_value", &text_value);
|
|
dynget (text, "TEXT", "ins_pt", &ins_pt);
|
|
dynget (text, "TEXT", "height", &fontsize);
|
|
|
|
printf ("\t<text id=\"dwg-object-%d\" x=\"%f\" y=\"%f\" "
|
|
"font-family=\"Verdana\" font-size=\"%f\" fill=\"blue\">%s</text>\n",
|
|
index, transform_X (ins_pt.x), transform_Y (ins_pt.y), fontsize,
|
|
text_value);
|
|
|
|
if (text_value && isnew)
|
|
free (text_value);
|
|
}
|
|
|
|
static void
|
|
output_LINE (dwg_object *obj)
|
|
{
|
|
int error, index;
|
|
Dwg_Entity_LINE *line;
|
|
dwg_point_3d start, end;
|
|
|
|
index = dwg_object_get_index (obj, &error);
|
|
log_if_error ("object_get_index");
|
|
line = dwg_object_to_LINE (obj);
|
|
if (!line)
|
|
log_error ("dwg_object_to_LINE");
|
|
if (!dwg_get_LINE (line, "start", &start))
|
|
log_error ("LINE.start");
|
|
if (!dwg_get_LINE (line, "end", &end))
|
|
log_error ("LINE.end");
|
|
|
|
printf ("\t<path id=\"dwg-object-%d\" d=\"M %f,%f %f,%f\" "
|
|
"style=\"fill:none;stroke:blue;stroke-width:0.1px\" />\n",
|
|
index, transform_X (start.x), transform_Y (start.y),
|
|
transform_X (end.x), transform_Y (end.y));
|
|
}
|
|
|
|
static void
|
|
output_CIRCLE (dwg_object *obj)
|
|
{
|
|
Dwg_Entity_CIRCLE *circle;
|
|
int error, index;
|
|
double radius;
|
|
dwg_point_3d center;
|
|
|
|
index = dwg_object_get_index (obj, &error);
|
|
log_if_error ("object_get_index");
|
|
circle = dwg_object_to_CIRCLE (obj);
|
|
if (!circle)
|
|
log_error ("dwg_object_to_CIRCLE");
|
|
if (!dwg_get_CIRCLE (circle, "center", ¢er))
|
|
log_error ("CIRCLE.center");
|
|
if (!dwg_get_CIRCLE (circle, "radius", &radius))
|
|
log_error ("CIRCLE.radius");
|
|
|
|
printf ("\t<circle id=\"dwg-object-%d\" cx=\"%f\" cy=\"%f\" r=\"%f\" "
|
|
"fill=\"none\" stroke=\"blue\" stroke-width=\"0.1px\" />\n",
|
|
index, transform_X (center.x), transform_Y (center.y), radius);
|
|
}
|
|
|
|
static void
|
|
output_ARC (dwg_object *obj)
|
|
{
|
|
Dwg_Entity_ARC *arc;
|
|
int error, index;
|
|
double radius, start_angle, end_angle;
|
|
dwg_point_3d center;
|
|
double x_start, y_start, x_end, y_end;
|
|
int large_arc;
|
|
|
|
index = dwg_object_get_index (obj, &error);
|
|
log_if_error ("object_get_index");
|
|
arc = dwg_object_to_ARC (obj);
|
|
if (!arc)
|
|
log_error ("dwg_object_to_ARC");
|
|
dynget (arc, "ARC", "radius", &radius);
|
|
dynget (arc, "ARC", "center", ¢er);
|
|
dynget (arc, "ARC", "start_angle", &start_angle);
|
|
dynget (arc, "ARC", "end_angle", &end_angle);
|
|
|
|
x_start = center.x + radius * cos (start_angle);
|
|
y_start = center.y + radius * sin (start_angle);
|
|
x_end = center.x + radius * cos (end_angle);
|
|
y_end = center.y + radius * sin (end_angle);
|
|
// Assuming clockwise arcs.
|
|
large_arc = (end_angle - start_angle < M_PI) ? 0 : 1;
|
|
|
|
printf ("\t<path id=\"dwg-object-%d\" d=\"M %f,%f A %f,%f 0 %d 0 %f,%f\" "
|
|
"fill=\"none\" stroke=\"blue\" stroke-width=\"%f\" />\n",
|
|
index, transform_X (x_start), transform_Y (y_start), radius, radius,
|
|
large_arc, transform_X (x_end), transform_Y (y_end), 0.1);
|
|
}
|
|
|
|
static void
|
|
output_INSERT (dwg_object *obj)
|
|
{
|
|
int index, error;
|
|
BITCODE_RL abs_ref;
|
|
double rotation;
|
|
dwg_ent_insert *insert;
|
|
dwg_point_3d ins_pt, _scale;
|
|
dwg_handle *obj_handle, *ins_handle;
|
|
Dwg_Data *dwg;
|
|
|
|
insert = dwg_object_to_INSERT (obj);
|
|
if (!insert)
|
|
log_error ("dwg_object_to_INSERT");
|
|
dwg = obj->parent;
|
|
index = dwg_object_get_index (obj, &error);
|
|
log_if_error ("object_get_index");
|
|
dynget (insert, "INSERT", "rotation", &rotation);
|
|
dynget (insert, "INSERT", "ins_pt", &ins_pt);
|
|
dynget (insert, "INSERT", "scale", &_scale);
|
|
obj_handle = dwg_object_get_handle (obj, &error);
|
|
log_if_error ("get_handle");
|
|
if (!insert->block_header)
|
|
log_error ("insert->block_header");
|
|
abs_ref = insert->block_header->absolute_ref;
|
|
|
|
if (insert->block_header->handleref.code == 5 || dwg->header.version < R_13)
|
|
{
|
|
printf ("\t<use id=\"dwg-object-%d\" transform=\"translate(%f %f) "
|
|
"rotate(%f) scale(%f %f)\" xlink:href=\"#symbol-%X\" /><!-- "
|
|
"block_header->handleref: " FORMAT_H " -->\n",
|
|
index, transform_X (ins_pt.x), transform_Y (ins_pt.y),
|
|
(180.0 / M_PI) * rotation, _scale.x, _scale.y, abs_ref,
|
|
ARGS_H (*obj_handle));
|
|
}
|
|
else
|
|
{
|
|
printf ("\n\n<!-- WRONG INSERT(" FORMAT_H ") -->\n",
|
|
ARGS_H (*obj_handle));
|
|
}
|
|
}
|
|
|
|
static int
|
|
output_object (dwg_object *obj)
|
|
{
|
|
int i = 0;
|
|
if (!obj)
|
|
{
|
|
fprintf (stderr, "object is NULL\n");
|
|
return 0;
|
|
}
|
|
|
|
if (dwg_object_get_fixedtype (obj) == DWG_TYPE_INSERT)
|
|
{
|
|
i++;
|
|
output_INSERT (obj);
|
|
}
|
|
else if (dwg_object_get_fixedtype (obj) == DWG_TYPE_LINE)
|
|
{
|
|
i++;
|
|
output_LINE (obj);
|
|
}
|
|
else if (dwg_object_get_fixedtype (obj) == DWG_TYPE_CIRCLE)
|
|
{
|
|
i++;
|
|
output_CIRCLE (obj);
|
|
}
|
|
else if (dwg_object_get_fixedtype (obj) == DWG_TYPE_TEXT)
|
|
{
|
|
i++;
|
|
output_TEXT (obj);
|
|
}
|
|
else if (dwg_object_get_fixedtype (obj) == DWG_TYPE_ARC)
|
|
{
|
|
i++;
|
|
output_ARC (obj);
|
|
}
|
|
return i;
|
|
}
|
|
|
|
static int
|
|
output_BLOCK_HEADER (dwg_object_ref *ref)
|
|
{
|
|
dwg_object *hdr, *obj;
|
|
dwg_obj_block_header *_hdr;
|
|
int error;
|
|
BITCODE_RL abs_ref;
|
|
char *name;
|
|
int i = 0;
|
|
|
|
if (!ref)
|
|
{
|
|
fprintf (stderr,
|
|
"Empty BLOCK."
|
|
" Could not output an SVG symbol for this BLOCK_HEADER\n");
|
|
return 0;
|
|
}
|
|
hdr = dwg_ref_get_object (ref, &error);
|
|
if (!hdr || error)
|
|
{
|
|
abs_ref = dwg_ref_get_absref (ref, &error);
|
|
fprintf (stderr, "Failed to resolve BLOCK handle %X.\n",
|
|
(unsigned)abs_ref);
|
|
return 0;
|
|
}
|
|
abs_ref = dwg_ref_get_absref (ref, &error);
|
|
|
|
_hdr = dwg_object_to_BLOCK_HEADER (hdr);
|
|
if (_hdr)
|
|
{
|
|
i++;
|
|
dynget (_hdr, "BLOCK_HEADER", "name", &name);
|
|
// name = dwg_obj_block_header_get_name (_hdr, &error);
|
|
printf ("\t<g id=\"symbol-%X\" >\n\t\t<!-- %s -->\n",
|
|
abs_ref ? abs_ref : 0, name ? name : "");
|
|
if (name != NULL && name != _hdr->name
|
|
&& hdr->parent->header.version >= R_2007)
|
|
free (name);
|
|
}
|
|
else
|
|
printf ("\t<g id=\"symbol-%X\" >\n\t\t<!-- ? -->\n",
|
|
abs_ref ? abs_ref : 0);
|
|
|
|
obj = get_first_owned_entity (hdr);
|
|
while (obj)
|
|
{
|
|
i += output_object (obj);
|
|
obj = get_next_owned_entity (hdr, obj);
|
|
}
|
|
printf ("\t</g>\n");
|
|
return i;
|
|
}
|
|
|
|
static void
|
|
output_SVG (dwg_data *dwg)
|
|
{
|
|
unsigned int i, num_hdr_objs;
|
|
int error;
|
|
dwg_obj_block_control *_ctrl;
|
|
dwg_object_ref *hdr;
|
|
dwg_object_ref **hdr_refs;
|
|
dwg_object_ref *ms = dwg_model_space_ref (dwg);
|
|
dwg_object_ref *ps = dwg_paper_space_ref (dwg);
|
|
|
|
double dx = dwg_model_x_max (dwg) - dwg_model_x_min (dwg);
|
|
double dy = dwg_model_y_max (dwg) - dwg_model_y_min (dwg);
|
|
double pdx = dwg->header_vars.PLIMMAX.x - dwg->header_vars.PLIMMIN.x;
|
|
double pdy = dwg->header_vars.PLIMMAX.y - dwg->header_vars.PLIMMIN.y;
|
|
double scale_x = dx / (pdx == 0.0 ? 1.0 : pdx);
|
|
double scale_y = dy / (pdy == 0.0 ? 1.0 : pdy);
|
|
scale = 25.4 / 72.0; // pt:mm TODO
|
|
|
|
model_xmin = dwg_model_x_min (dwg);
|
|
model_ymin = dwg_model_y_min (dwg);
|
|
page_width = dx;
|
|
page_height = dy;
|
|
scale *= (scale_x > scale_y ? scale_x : scale_y);
|
|
|
|
_ctrl = dwg_block_control (dwg);
|
|
hdr_refs = dwg_obj_block_control_get_block_headers (_ctrl, &error);
|
|
log_if_error ("block_control_get_block_headers");
|
|
num_hdr_objs = dwg_obj_block_control_get_num_entries (_ctrl, &error);
|
|
log_if_error ("block_control_get_num_entries");
|
|
|
|
printf ("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n"
|
|
"<svg\n"
|
|
" xmlns:svg=\"http://www.w3.org/2000/svg\"\n"
|
|
" xmlns=\"http://www.w3.org/2000/svg\"\n"
|
|
" xmlns:xlink=\"http://www.w3.org/1999/xlink\"\n"
|
|
" version=\"1.1\"\n"
|
|
" width=\"%f\"\n"
|
|
" height=\"%f\"\n"
|
|
">\n",
|
|
page_width, page_height);
|
|
|
|
printf ("\t<defs>\n");
|
|
for (i = 0; i < num_hdr_objs; i++)
|
|
{
|
|
hdr = hdr_refs[i];
|
|
if (hdr == ms || hdr == ps)
|
|
continue;
|
|
output_BLOCK_HEADER (hdr_refs[i]);
|
|
}
|
|
printf ("\t</defs>\n");
|
|
|
|
if (ps)
|
|
i = output_BLOCK_HEADER (ps);
|
|
if (!ps || !i)
|
|
output_BLOCK_HEADER (ms);
|
|
free (hdr_refs);
|
|
|
|
printf ("</svg>\n");
|
|
}
|
|
|
|
int
|
|
main (int argc, char *argv[])
|
|
{
|
|
int i = 1;
|
|
int c;
|
|
#ifdef HAVE_GETOPT_LONG
|
|
int option_index = 0;
|
|
static struct option long_options[]
|
|
= { { "verbose", 1, &opts, 1 }, // optional
|
|
{ "help", 0, 0, 0 },
|
|
{ "version", 0, 0, 0 },
|
|
{ NULL, 0, NULL, 0 } };
|
|
#endif
|
|
|
|
if (argc < 2)
|
|
return usage ();
|
|
|
|
while
|
|
#ifdef HAVE_GETOPT_LONG
|
|
((c = getopt_long (argc, argv, ":v::h", long_options, &option_index))
|
|
!= -1)
|
|
#else
|
|
((c = getopt (argc, argv, ":v::hi")) != -1)
|
|
#endif
|
|
{
|
|
if (c == -1)
|
|
break;
|
|
switch (c)
|
|
{
|
|
case ':': // missing arg
|
|
if (optarg && !strcmp (optarg, "v"))
|
|
{
|
|
opts = 1;
|
|
break;
|
|
}
|
|
fprintf (stderr, "%s: option '-%c' requires an argument\n", argv[0],
|
|
optopt);
|
|
break;
|
|
#ifdef HAVE_GETOPT_LONG
|
|
case 0:
|
|
/* This option sets a flag */
|
|
if (!strcmp (long_options[option_index].name, "verbose"))
|
|
{
|
|
if (opts < 0 || opts > 9)
|
|
return usage ();
|
|
# if defined(USE_TRACING) && defined(HAVE_SETENV)
|
|
{
|
|
char v[2];
|
|
*v = opts + '0';
|
|
*(v + 1) = 0;
|
|
setenv ("LIBREDWG_TRACE", v, 1);
|
|
}
|
|
# endif
|
|
break;
|
|
}
|
|
if (!strcmp (long_options[option_index].name, "version"))
|
|
return opt_version ();
|
|
if (!strcmp (long_options[option_index].name, "help"))
|
|
return help ();
|
|
break;
|
|
#else
|
|
case 'i':
|
|
return opt_version ();
|
|
#endif
|
|
case 'v': // support -v3 and -v
|
|
i = (optind > 0 && optind < argc) ? optind - 1 : 1;
|
|
if (!memcmp (argv[i], "-v", 2))
|
|
{
|
|
opts = argv[i][2] ? argv[i][2] - '0' : 1;
|
|
}
|
|
if (opts < 0 || opts > 9)
|
|
return usage ();
|
|
#if defined(USE_TRACING) && defined(HAVE_SETENV)
|
|
{
|
|
char v[2];
|
|
*v = opts + '0';
|
|
*(v + 1) = 0;
|
|
setenv ("LIBREDWG_TRACE", v, 1);
|
|
}
|
|
#endif
|
|
break;
|
|
case 'h':
|
|
return help ();
|
|
case '?':
|
|
fprintf (stderr, "%s: invalid option '-%c' ignored\n", argv[0],
|
|
optopt);
|
|
break;
|
|
default:
|
|
return usage ();
|
|
}
|
|
}
|
|
i = optind;
|
|
if (i >= argc)
|
|
return usage ();
|
|
|
|
return test_SVG (argv[i]);
|
|
}
|