(root)/
libredwg-0.13/
examples/
dwg2svg2.c
       1  /*****************************************************************************/
       2  /*  LibreDWG - free implementation of the DWG file format                    */
       3  /*                                                                           */
       4  /*  Copyright (C) 2013-2020,2023 Free Software Foundation, Inc.              */
       5  /*                                                                           */
       6  /*  This library is free software, licensed under the terms of the GNU       */
       7  /*  General Public License as published by the Free Software Foundation,     */
       8  /*  either version 3 of the License, or (at your option) any later version.  */
       9  /*  You should have received a copy of the GNU General Public License        */
      10  /*  along with this program.  If not, see <http://www.gnu.org/licenses/>.    */
      11  /*****************************************************************************/
      12  
      13  /*
      14   * dwg2svg2.c: convert a DWG to SVG via the API.
      15     if there are paperspace entities, only output them. else all modelspace
      16     entities.
      17   * written by Gaganjyot Singh
      18   * modified by Reini Urban
      19   */
      20  
      21  #include "../src/config.h"
      22  #include <stdio.h>
      23  #include <stdlib.h>
      24  #include <string.h>
      25  #include <unistd.h>
      26  #include <math.h>
      27  #include <getopt.h>
      28  
      29  #ifdef ENABLE_MIMALLOC
      30  #  include <mimalloc-override.h>
      31  #endif
      32  
      33  #include <dwg.h>
      34  #include <dwg_api.h>
      35  #include "geom.h"
      36  
      37  static int opts = 0;
      38  static dwg_data g_dwg;
      39  static double model_xmin, model_ymin;
      40  static double page_width, page_height, scale;
      41  
      42  static int
      43  usage (void)
      44  {
      45    printf ("\nUsage: dwg2svg2 [-v[0-9]] DWGFILE\n");
      46    return 1;
      47  }
      48  static int
      49  opt_version (void)
      50  {
      51    printf ("dwg2svg2 %s\n", PACKAGE_VERSION);
      52    return 0;
      53  }
      54  
      55  static int
      56  help (void)
      57  {
      58    printf ("\nUsage: dwg2svg2 [OPTION]... DWGFILE >file.svg\n");
      59    printf ("Example to use the DWG api\n"
      60            "\n");
      61  #ifdef HAVE_GETOPT_LONG
      62    printf ("  -v[0-9], --verbose [0-9]  verbosity\n");
      63    printf ("           --help           display this help and exit\n");
      64    printf ("           --version        output version information and exit\n"
      65            "\n");
      66  #else
      67    printf ("  -v[0-9]     verbosity\n");
      68    printf ("  -h          display this help and exit\n");
      69    printf ("  -i          output version information and exit\n"
      70            "\n");
      71  #endif
      72    printf ("GNU LibreDWG online manual: "
      73            "<https://www.gnu.org/software/libredwg/>\n");
      74    return 0;
      75  }
      76  
      77  #define log_if_error(msg)                                                     \
      78    if (error)                                                                  \
      79      {                                                                         \
      80        fprintf (stderr, "ERROR: %s", msg);                                     \
      81        exit (1);                                                               \
      82      }
      83  #define log_error(msg)                                                        \
      84    {                                                                           \
      85      fprintf (stderr, "ERROR: %s", msg);                                       \
      86      exit (1);                                                                 \
      87    }
      88  #define dynget(obj, name, field, var)                                         \
      89    if (!dwg_dynapi_entity_value (obj, "" name, "" field, var, NULL))           \
      90      {                                                                         \
      91        fprintf (stderr, "ERROR: %s.%s", name, field);                          \
      92        exit (1);                                                               \
      93      }
      94  #define dynget_utf8(obj, name, field, var)                                    \
      95    if (!dwg_dynapi_entity_utf8text (obj, "" name, "" field, var, &isnew,       \
      96                                     NULL))                                     \
      97      {                                                                         \
      98        fprintf (stderr, "ERROR: %s.%s", name, field);                          \
      99        exit (1);                                                               \
     100      }
     101  
     102  static double
     103  transform_X (double x)
     104  {
     105    return x - model_xmin;
     106  }
     107  
     108  static double
     109  transform_Y (double y)
     110  {
     111    return page_height - (y - model_ymin);
     112  }
     113  
     114  static void output_SVG (dwg_data *dwg);
     115  
     116  static int
     117  test_SVG (char *filename)
     118  {
     119    int error;
     120  
     121    memset (&g_dwg, 0, sizeof (dwg_data));
     122    g_dwg.opts = opts;
     123    error = dwg_read_file (filename, &g_dwg);
     124    if (error < DWG_ERR_CRITICAL)
     125      output_SVG (&g_dwg);
     126  
     127    dwg_free (&g_dwg);
     128    /* This value is the return value for `main',
     129       so clamp it to either 0 or 1.  */
     130    return error < DWG_ERR_CRITICAL ? 0 : 1;
     131  }
     132  
     133  static void
     134  output_TEXT (dwg_object *obj)
     135  {
     136    int error, index;
     137    dwg_point_2d ins_pt;
     138    Dwg_Entity_TEXT *text;
     139    char *text_value;
     140    double fontsize;
     141    const Dwg_Version_Type dwg_version = obj->parent->header.version;
     142    int isnew = 0;
     143  
     144    index = dwg_object_get_index (obj, &error);
     145    log_if_error ("object_get_index");
     146    text = dwg_object_to_TEXT (obj);
     147    if (!text)
     148      log_error ("dwg_object_to_TEXT");
     149    dynget_utf8 (text, "TEXT", "text_value", &text_value);
     150    dynget (text, "TEXT", "ins_pt", &ins_pt);
     151    dynget (text, "TEXT", "height", &fontsize);
     152  
     153    printf ("\t<text id=\"dwg-object-%d\" x=\"%f\" y=\"%f\" "
     154            "font-family=\"Verdana\" font-size=\"%f\" fill=\"blue\">%s</text>\n",
     155            index, transform_X (ins_pt.x), transform_Y (ins_pt.y), fontsize,
     156            text_value);
     157  
     158    if (text_value && isnew)
     159      free (text_value);
     160  }
     161  
     162  static void
     163  output_LINE (dwg_object *obj)
     164  {
     165    int error, index;
     166    Dwg_Entity_LINE *line;
     167    dwg_point_3d start, end;
     168  
     169    index = dwg_object_get_index (obj, &error);
     170    log_if_error ("object_get_index");
     171    line = dwg_object_to_LINE (obj);
     172    if (!line)
     173      log_error ("dwg_object_to_LINE");
     174    if (!dwg_get_LINE (line, "start", &start))
     175      log_error ("LINE.start");
     176    if (!dwg_get_LINE (line, "end", &end))
     177      log_error ("LINE.end");
     178  
     179    printf ("\t<path id=\"dwg-object-%d\" d=\"M %f,%f %f,%f\" "
     180            "style=\"fill:none;stroke:blue;stroke-width:0.1px\" />\n",
     181            index, transform_X (start.x), transform_Y (start.y),
     182            transform_X (end.x), transform_Y (end.y));
     183  }
     184  
     185  static void
     186  output_CIRCLE (dwg_object *obj)
     187  {
     188    Dwg_Entity_CIRCLE *circle;
     189    int error, index;
     190    double radius;
     191    dwg_point_3d center;
     192  
     193    index = dwg_object_get_index (obj, &error);
     194    log_if_error ("object_get_index");
     195    circle = dwg_object_to_CIRCLE (obj);
     196    if (!circle)
     197      log_error ("dwg_object_to_CIRCLE");
     198    if (!dwg_get_CIRCLE (circle, "center", &center))
     199      log_error ("CIRCLE.center");
     200    if (!dwg_get_CIRCLE (circle, "radius", &radius))
     201      log_error ("CIRCLE.radius");
     202  
     203    printf ("\t<circle id=\"dwg-object-%d\" cx=\"%f\" cy=\"%f\" r=\"%f\" "
     204            "fill=\"none\" stroke=\"blue\" stroke-width=\"0.1px\" />\n",
     205            index, transform_X (center.x), transform_Y (center.y), radius);
     206  }
     207  
     208  static void
     209  output_ARC (dwg_object *obj)
     210  {
     211    Dwg_Entity_ARC *arc;
     212    int error, index;
     213    double radius, start_angle, end_angle;
     214    dwg_point_3d center;
     215    double x_start, y_start, x_end, y_end;
     216    int large_arc;
     217  
     218    index = dwg_object_get_index (obj, &error);
     219    log_if_error ("object_get_index");
     220    arc = dwg_object_to_ARC (obj);
     221    if (!arc)
     222      log_error ("dwg_object_to_ARC");
     223    dynget (arc, "ARC", "radius", &radius);
     224    dynget (arc, "ARC", "center", &center);
     225    dynget (arc, "ARC", "start_angle", &start_angle);
     226    dynget (arc, "ARC", "end_angle", &end_angle);
     227  
     228    x_start = center.x + radius * cos (start_angle);
     229    y_start = center.y + radius * sin (start_angle);
     230    x_end = center.x + radius * cos (end_angle);
     231    y_end = center.y + radius * sin (end_angle);
     232    // Assuming clockwise arcs.
     233    large_arc = (end_angle - start_angle < M_PI) ? 0 : 1;
     234  
     235    printf ("\t<path id=\"dwg-object-%d\" d=\"M %f,%f A %f,%f 0 %d 0 %f,%f\" "
     236            "fill=\"none\" stroke=\"blue\" stroke-width=\"%f\" />\n",
     237            index, transform_X (x_start), transform_Y (y_start), radius, radius,
     238            large_arc, transform_X (x_end), transform_Y (y_end), 0.1);
     239  }
     240  
     241  static void
     242  output_INSERT (dwg_object *obj)
     243  {
     244    int index, error;
     245    BITCODE_RL abs_ref;
     246    double rotation;
     247    dwg_ent_insert *insert;
     248    dwg_point_3d ins_pt, _scale;
     249    dwg_handle *obj_handle, *ins_handle;
     250    Dwg_Data *dwg;
     251  
     252    insert = dwg_object_to_INSERT (obj);
     253    if (!insert)
     254      log_error ("dwg_object_to_INSERT");
     255    dwg = obj->parent;
     256    index = dwg_object_get_index (obj, &error);
     257    log_if_error ("object_get_index");
     258    dynget (insert, "INSERT", "rotation", &rotation);
     259    dynget (insert, "INSERT", "ins_pt", &ins_pt);
     260    dynget (insert, "INSERT", "scale", &_scale);
     261    obj_handle = dwg_object_get_handle (obj, &error);
     262    log_if_error ("get_handle");
     263    if (!insert->block_header)
     264      log_error ("insert->block_header");
     265    abs_ref = insert->block_header->absolute_ref;
     266  
     267    if (insert->block_header->handleref.code == 5 || dwg->header.version < R_13)
     268      {
     269        printf ("\t<use id=\"dwg-object-%d\" transform=\"translate(%f %f) "
     270                "rotate(%f) scale(%f %f)\" xlink:href=\"#symbol-%X\" /><!-- "
     271                "block_header->handleref: " FORMAT_H " -->\n",
     272                index, transform_X (ins_pt.x), transform_Y (ins_pt.y),
     273                (180.0 / M_PI) * rotation, _scale.x, _scale.y, abs_ref,
     274                ARGS_H (*obj_handle));
     275      }
     276    else
     277      {
     278        printf ("\n\n<!-- WRONG INSERT(" FORMAT_H ") -->\n",
     279                ARGS_H (*obj_handle));
     280      }
     281  }
     282  
     283  static int
     284  output_object (dwg_object *obj)
     285  {
     286    int i = 0;
     287    if (!obj)
     288      {
     289        fprintf (stderr, "object is NULL\n");
     290        return 0;
     291      }
     292  
     293    if (dwg_object_get_fixedtype (obj) == DWG_TYPE_INSERT)
     294      {
     295        i++;
     296        output_INSERT (obj);
     297      }
     298    else if (dwg_object_get_fixedtype (obj) == DWG_TYPE_LINE)
     299      {
     300        i++;
     301        output_LINE (obj);
     302      }
     303    else if (dwg_object_get_fixedtype (obj) == DWG_TYPE_CIRCLE)
     304      {
     305        i++;
     306        output_CIRCLE (obj);
     307      }
     308    else if (dwg_object_get_fixedtype (obj) == DWG_TYPE_TEXT)
     309      {
     310        i++;
     311        output_TEXT (obj);
     312      }
     313    else if (dwg_object_get_fixedtype (obj) == DWG_TYPE_ARC)
     314      {
     315        i++;
     316        output_ARC (obj);
     317      }
     318    return i;
     319  }
     320  
     321  static int
     322  output_BLOCK_HEADER (dwg_object_ref *ref)
     323  {
     324    dwg_object *hdr, *obj;
     325    dwg_obj_block_header *_hdr;
     326    int error;
     327    BITCODE_RL abs_ref;
     328    char *name;
     329    int i = 0;
     330  
     331    if (!ref)
     332      {
     333        fprintf (stderr,
     334                 "Empty BLOCK."
     335                 " Could not output an SVG symbol for this BLOCK_HEADER\n");
     336        return 0;
     337      }
     338    hdr = dwg_ref_get_object (ref, &error);
     339    if (!hdr || error)
     340      {
     341        abs_ref = dwg_ref_get_absref (ref, &error);
     342        fprintf (stderr, "Failed to resolve BLOCK handle %X.\n",
     343                 (unsigned)abs_ref);
     344        return 0;
     345      }
     346    abs_ref = dwg_ref_get_absref (ref, &error);
     347  
     348    _hdr = dwg_object_to_BLOCK_HEADER (hdr);
     349    if (_hdr)
     350      {
     351        i++;
     352        dynget (_hdr, "BLOCK_HEADER", "name", &name);
     353        // name = dwg_obj_block_header_get_name (_hdr, &error);
     354        printf ("\t<g id=\"symbol-%X\" >\n\t\t<!-- %s -->\n",
     355                abs_ref ? abs_ref : 0, name ? name : "");
     356        if (name != NULL && name != _hdr->name
     357            && hdr->parent->header.version >= R_2007)
     358          free (name);
     359      }
     360    else
     361      printf ("\t<g id=\"symbol-%X\" >\n\t\t<!-- ? -->\n",
     362              abs_ref ? abs_ref : 0);
     363  
     364    obj = get_first_owned_entity (hdr);
     365    while (obj)
     366      {
     367        i += output_object (obj);
     368        obj = get_next_owned_entity (hdr, obj);
     369      }
     370    printf ("\t</g>\n");
     371    return i;
     372  }
     373  
     374  static void
     375  output_SVG (dwg_data *dwg)
     376  {
     377    unsigned int i, num_hdr_objs;
     378    int error;
     379    dwg_obj_block_control *_ctrl;
     380    dwg_object_ref *hdr;
     381    dwg_object_ref **hdr_refs;
     382    dwg_object_ref *ms = dwg_model_space_ref (dwg);
     383    dwg_object_ref *ps = dwg_paper_space_ref (dwg);
     384  
     385    double dx = dwg_model_x_max (dwg) - dwg_model_x_min (dwg);
     386    double dy = dwg_model_y_max (dwg) - dwg_model_y_min (dwg);
     387    double pdx = dwg->header_vars.PLIMMAX.x - dwg->header_vars.PLIMMIN.x;
     388    double pdy = dwg->header_vars.PLIMMAX.y - dwg->header_vars.PLIMMIN.y;
     389    double scale_x = dx / (pdx == 0.0 ? 1.0 : pdx);
     390    double scale_y = dy / (pdy == 0.0 ? 1.0 : pdy);
     391    scale = 25.4 / 72.0; // pt:mm TODO
     392  
     393    model_xmin = dwg_model_x_min (dwg);
     394    model_ymin = dwg_model_y_min (dwg);
     395    page_width = dx;
     396    page_height = dy;
     397    scale *= (scale_x > scale_y ? scale_x : scale_y);
     398  
     399    _ctrl = dwg_block_control (dwg);
     400    hdr_refs = dwg_obj_block_control_get_block_headers (_ctrl, &error);
     401    log_if_error ("block_control_get_block_headers");
     402    num_hdr_objs = dwg_obj_block_control_get_num_entries (_ctrl, &error);
     403    log_if_error ("block_control_get_num_entries");
     404  
     405    printf ("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n"
     406            "<svg\n"
     407            "   xmlns:svg=\"http://www.w3.org/2000/svg\"\n"
     408            "   xmlns=\"http://www.w3.org/2000/svg\"\n"
     409            "   xmlns:xlink=\"http://www.w3.org/1999/xlink\"\n"
     410            "   version=\"1.1\"\n"
     411            "   width=\"%f\"\n"
     412            "   height=\"%f\"\n"
     413            ">\n",
     414            page_width, page_height);
     415  
     416    printf ("\t<defs>\n");
     417    for (i = 0; i < num_hdr_objs; i++)
     418      {
     419        hdr = hdr_refs[i];
     420        if (hdr == ms || hdr == ps)
     421          continue;
     422        output_BLOCK_HEADER (hdr_refs[i]);
     423      }
     424    printf ("\t</defs>\n");
     425  
     426    if (ps)
     427      i = output_BLOCK_HEADER (ps);
     428    if (!ps || !i)
     429      output_BLOCK_HEADER (ms);
     430    free (hdr_refs);
     431  
     432    printf ("</svg>\n");
     433  }
     434  
     435  int
     436  main (int argc, char *argv[])
     437  {
     438    int i = 1;
     439    int c;
     440  #ifdef HAVE_GETOPT_LONG
     441    int option_index = 0;
     442    static struct option long_options[]
     443        = { { "verbose", 1, &opts, 1 }, // optional
     444            { "help", 0, 0, 0 },
     445            { "version", 0, 0, 0 },
     446            { NULL, 0, NULL, 0 } };
     447  #endif
     448  
     449    if (argc < 2)
     450      return usage ();
     451  
     452    while
     453  #ifdef HAVE_GETOPT_LONG
     454        ((c = getopt_long (argc, argv, ":v::h", long_options, &option_index))
     455         != -1)
     456  #else
     457        ((c = getopt (argc, argv, ":v::hi")) != -1)
     458  #endif
     459      {
     460        if (c == -1)
     461          break;
     462        switch (c)
     463          {
     464          case ':': // missing arg
     465            if (optarg && !strcmp (optarg, "v"))
     466              {
     467                opts = 1;
     468                break;
     469              }
     470            fprintf (stderr, "%s: option '-%c' requires an argument\n", argv[0],
     471                     optopt);
     472            break;
     473  #ifdef HAVE_GETOPT_LONG
     474          case 0:
     475            /* This option sets a flag */
     476            if (!strcmp (long_options[option_index].name, "verbose"))
     477              {
     478                if (opts < 0 || opts > 9)
     479                  return usage ();
     480  #  if defined(USE_TRACING) && defined(HAVE_SETENV)
     481                {
     482                  char v[2];
     483                  *v = opts + '0';
     484                  *(v + 1) = 0;
     485                  setenv ("LIBREDWG_TRACE", v, 1);
     486                }
     487  #  endif
     488                break;
     489              }
     490            if (!strcmp (long_options[option_index].name, "version"))
     491              return opt_version ();
     492            if (!strcmp (long_options[option_index].name, "help"))
     493              return help ();
     494            break;
     495  #else
     496          case 'i':
     497            return opt_version ();
     498  #endif
     499          case 'v': // support -v3 and -v
     500            i = (optind > 0 && optind < argc) ? optind - 1 : 1;
     501            if (!memcmp (argv[i], "-v", 2))
     502              {
     503                opts = argv[i][2] ? argv[i][2] - '0' : 1;
     504              }
     505            if (opts < 0 || opts > 9)
     506              return usage ();
     507  #if defined(USE_TRACING) && defined(HAVE_SETENV)
     508            {
     509              char v[2];
     510              *v = opts + '0';
     511              *(v + 1) = 0;
     512              setenv ("LIBREDWG_TRACE", v, 1);
     513            }
     514  #endif
     515            break;
     516          case 'h':
     517            return help ();
     518          case '?':
     519            fprintf (stderr, "%s: invalid option '-%c' ignored\n", argv[0],
     520                     optopt);
     521            break;
     522          default:
     523            return usage ();
     524          }
     525      }
     526    i = optind;
     527    if (i >= argc)
     528      return usage ();
     529  
     530    return test_SVG (argv[i]);
     531  }