source: git/src/export.cc @ 0b1d7d1

RELEASE/1.0
Last change on this file since 0b1d7d1 was 0b1d7d1, checked in by Olly Betts <olly@…>, 14 years ago

NEWS,src/export.cc: aven: Improve SVG output compatibility. Put a
5mm border around exported SVG files to allow for station markers
and non-zero width lines. Fix crash when exporting as SVG or Sketch
if labels or surface data was turned on. Backports from 1.1.13.

git-svn-id: file:///home/survex-svn/survex/branches/1.0@3466 4b37db11-9a0c-4f06-9ece-9ab7cdaee568

  • Property mode set to 100644
File size: 32.9 KB
Line 
1/* export.cc
2 * Export to CAD-like formats (DXF, Sketch, SVG, EPS) and also Compass PLT.
3 */
4
5/* Copyright (C) 1994-2004,2005,2008 Olly Betts
6 * Copyright (C) 2004 John Pybus (SVG Output code)
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
21 */
22
23/* #define DEBUG_CAD3D */
24
25#ifdef HAVE_CONFIG_H
26#include <config.h>
27#endif
28
29#include "wx.h"
30#include <wx/utils.h>
31#include "mainfrm.h"
32
33#include <float.h>
34#include <math.h>
35#include <stdio.h>
36#include <stdlib.h>
37#include <string.h>
38
39#if defined(HAVE_GETPWUID) && !defined(__DJGPP__)
40# include <pwd.h>
41# include <sys/types.h>
42# include <unistd.h>
43#endif
44
45#include "cmdline.h"
46#include "debug.h"
47#include "filename.h"
48#include "hash.h"
49#include "img.h"
50#include "message.h"
51#include "useful.h"
52
53/* default values - can be overridden with --htext and --msize respectively */
54#define TEXT_HEIGHT     0.6
55#define MARKER_SIZE     0.8
56
57#define GRID_SPACING    100
58
59#define POINTS_PER_INCH 72.0
60#define POINTS_PER_MM (POINTS_PER_INCH / MM_PER_INCH)
61
62#define SQRT_2          1.41421356237309504880168872420969
63
64#define LEGS 1
65#define STNS 4
66#define LABELS 8
67
68static const char *layer_names[] = {
69   NULL,
70   "Legs",      // LEGS
71   NULL,
72   NULL,
73   "Stations",  // STNS
74   NULL,
75   NULL,
76   NULL,
77   "Labels"     // LABELS
78};
79
80/* default to DXF */
81#define FMT_DEFAULT FMT_DXF
82
83static FILE *fh;
84
85/* bounds */
86static double min_x, min_y, min_z, max_x, max_y, max_z;
87
88static double text_height; /* for station labels */
89static double marker_size; /* for station markers */
90static double grid; /* grid spacing (or 0 for no grid) */
91static double scale = 500.0;
92static double factor;
93static const char *unit = "mm";
94const double SVG_MARGIN = 5.0; // In units of "unit".
95
96static const char *survey = NULL;
97
98static void
99dxf_header(const char *)
100{
101   fprintf(fh, "0\nSECTION\n"
102               "2\nHEADER\n");
103   fprintf(fh, "9\n$EXTMIN\n"); /* lower left corner of drawing */
104   fprintf(fh, "10\n%#-.6f\n", min_x); /* x */
105   fprintf(fh, "20\n%#-.6f\n", min_y); /* y */
106   fprintf(fh, "30\n%#-.6f\n", min_z); /* min z */
107   fprintf(fh, "9\n$EXTMAX\n"); /* upper right corner of drawing */
108   fprintf(fh, "10\n%#-.6f\n", max_x); /* x */
109   fprintf(fh, "20\n%#-.6f\n", max_y); /* y */
110   fprintf(fh, "30\n%#-.6f\n", max_z); /* max z */
111   fprintf(fh, "9\n$PDMODE\n70\n3\n"); /* marker style as CROSS */
112   fprintf(fh, "9\n$PDSIZE\n40\n%6.2f\n", marker_size); /* marker size */
113   fprintf(fh, "0\nENDSEC\n");
114
115   fprintf(fh, "0\nSECTION\n"
116               "2\nTABLES\n");
117   fprintf(fh, "0\nTABLE\n" /* Define CONTINUOUS and DASHED line types. */
118               "2\nLTYPE\n"
119               "70\n10\n"
120               "0\nLTYPE\n"
121               "2\nCONTINUOUS\n"
122               "70\n64\n"
123               "3\nContinuous\n"
124               "72\n65\n"
125               "73\n0\n"
126               "40\n0.0\n"
127               "0\nLTYPE\n"
128               "2\nDASHED\n"
129               "70\n64\n"
130               "3\nDashed\n"
131               "72\n65\n"
132               "73\n2\n"
133               "40\n2.5\n"
134               "49\n1.25\n"
135               "49\n-1.25\n"
136               "0\nENDTAB\n");
137   fprintf(fh, "0\nTABLE\n"
138               "2\nLAYER\n");
139   fprintf(fh, "70\n10\n"); /* max # off layers in this DXF file : 10 */
140   /* First Layer: CentreLine */
141   fprintf(fh, "0\nLAYER\n2\nCentreLine\n");
142   fprintf(fh, "70\n64\n"); /* shows layer is referenced by entities */
143   fprintf(fh, "62\n5\n"); /* color: kept the same used by SpeleoGen */
144   fprintf(fh, "6\nCONTINUOUS\n"); /* linetype */
145   /* Next Layer: Stations */
146   fprintf(fh, "0\nLAYER\n2\nStations\n");
147   fprintf(fh, "70\n64\n"); /* shows layer is referenced by entities */
148   fprintf(fh, "62\n7\n"); /* color: kept the same used by SpeleoGen */
149   fprintf(fh, "6\nCONTINUOUS\n"); /* linetype */
150   /* Next Layer: Labels */
151   fprintf(fh, "0\nLAYER\n2\nLabels\n");
152   fprintf(fh, "70\n64\n"); /* shows layer is referenced by entities */
153   fprintf(fh, "62\n7\n"); /* color: kept the same used by SpeleoGen */
154   fprintf(fh, "6\nCONTINUOUS\n"); /* linetype */
155   /* Next Layer: Surface */
156   fprintf(fh, "0\nLAYER\n2\nSurface\n");
157   fprintf(fh, "70\n64\n"); /* shows layer is referenced by entities */
158   fprintf(fh, "62\n5\n"); /* color */
159   fprintf(fh, "6\nDASHED\n"); /* linetype */
160   /* Next Layer: SurfaceStations */
161   fprintf(fh, "0\nLAYER\n2\nSurfaceStations\n");
162   fprintf(fh, "70\n64\n"); /* shows layer is referenced by entities */
163   fprintf(fh, "62\n7\n"); /* color */
164   fprintf(fh, "6\nCONTINUOUS\n"); /* linetype */
165   /* Next Layer: SurfaceLabels */
166   fprintf(fh, "0\nLAYER\n2\nSurfaceLabels\n");
167   fprintf(fh, "70\n64\n"); /* shows layer is referenced by entities */
168   fprintf(fh, "62\n7\n"); /* color */
169   fprintf(fh, "6\nCONTINUOUS\n"); /* linetype */
170   if (grid > 0) {
171      /* Next Layer: Grid */
172      fprintf(fh, "0\nLAYER\n2\nGrid\n");
173      fprintf(fh, "70\n64\n"); /* shows layer is referenced by entities */
174      fprintf(fh, "62\n7\n"); /* color: kept the same used by SpeleoGen */
175      fprintf(fh, "6\nCONTINUOUS\n"); /* linetype */
176   }
177   fprintf(fh, "0\nENDTAB\n"
178               "0\nENDSEC\n");
179
180   fprintf(fh, "0\nSECTION\n"
181               "2\nENTITIES\n");
182
183   if (grid > 0) {
184      double x, y;
185      x = floor(min_x / grid) * grid + grid;
186      y = floor(min_y / grid) * grid + grid;
187#ifdef DEBUG_CAD3D
188      printf("x_min: %f  y_min: %f\n", x, y);
189#endif
190      while (x < max_x) {
191         /* horizontal line */
192         fprintf(fh, "0\nLINE\n");
193         fprintf(fh, "8\nGrid\n"); /* Layer */
194         fprintf(fh, "10\n%6.2f\n", x);
195         fprintf(fh, "20\n%6.2f\n", min_y);
196         fprintf(fh, "30\n0\n");
197         fprintf(fh, "11\n%6.2f\n", x);
198         fprintf(fh, "21\n%6.2f\n", max_y);
199         fprintf(fh, "31\n0\n");
200         x += grid;
201      }
202      while (y < max_y) {
203         /* vertical line */
204         fprintf(fh, "0\nLINE\n");
205         fprintf(fh, "8\nGrid\n"); /* Layer */
206         fprintf(fh, "10\n%6.2f\n", min_x);
207         fprintf(fh, "20\n%6.2f\n", y);
208         fprintf(fh, "30\n0\n");
209         fprintf(fh, "11\n%6.2f\n", max_x);
210         fprintf(fh, "21\n%6.2f\n", y);
211         fprintf(fh, "31\n0\n");
212         y += grid;
213      }
214   }
215}
216
217static void
218dxf_start_pass(int layer)
219{
220   layer = layer;
221}
222
223static void
224dxf_line(const img_point *p1, const img_point *p, bool fSurface, bool fPendingMove)
225{
226   fPendingMove = fPendingMove; /* unused */
227   fprintf(fh, "0\nLINE\n");
228   fprintf(fh, fSurface ? "8\nSurface\n" : "8\nCentreLine\n"); /* Layer */
229   fprintf(fh, "10\n%6.2f\n", p1->x);
230   fprintf(fh, "20\n%6.2f\n", p1->y);
231   fprintf(fh, "30\n%6.2f\n", p1->z);
232   fprintf(fh, "11\n%6.2f\n", p->x);
233   fprintf(fh, "21\n%6.2f\n", p->y);
234   fprintf(fh, "31\n%6.2f\n", p->z);
235}
236
237static void
238dxf_label(const img_point *p, const char *s, bool fSurface)
239{
240   /* write station label to dxf file */
241   fprintf(fh, "0\nTEXT\n");
242   fprintf(fh, fSurface ? "8\nSurfaceLabels\n" : "8\nLabels\n"); /* Layer */
243   fprintf(fh, "10\n%6.2f\n", p->x);
244   fprintf(fh, "20\n%6.2f\n", p->y);
245   fprintf(fh, "30\n%6.2f\n", p->z);
246   fprintf(fh, "40\n%6.2f\n", text_height);
247   fprintf(fh, "1\n%s\n", s);
248}
249
250static void
251dxf_cross(const img_point *p, bool fSurface)
252{
253   /* write station marker to dxf file */
254   fprintf(fh, "0\nPOINT\n");
255   fprintf(fh, fSurface ? "8\nSurfaceStations\n" : "8\nStations\n"); /* Layer */
256   fprintf(fh, "10\n%6.2f\n", p->x);
257   fprintf(fh, "20\n%6.2f\n", p->y);
258   fprintf(fh, "30\n%6.2f\n", p->z);
259}
260
261static void
262dxf_footer(void)
263{
264   fprintf(fh, "000\nENDSEC\n");
265   fprintf(fh, "000\nEOF\n");
266}
267
268static void
269sketch_header(const char *)
270{
271   fprintf(fh, "##Sketch 1 2\n"); /* Sketch file version */
272   fprintf(fh, "document()\n");
273   fprintf(fh, "layout((%.3f,%.3f),0)\n",
274           (max_x - min_x) * factor, (max_y - min_y) * factor);
275}
276
277static void
278sketch_start_pass(int layer)
279{
280   fprintf(fh, "layer('%s',1,1,0,0,(0,0,0))\n", layer_names[layer]);
281}
282
283static void
284sketch_line(const img_point *p1, const img_point *p, bool fSurface, bool fPendingMove)
285{
286   fSurface = fSurface; /* unused */
287   if (fPendingMove) {
288       fprintf(fh, "b()\n");
289       fprintf(fh, "bs(%.3f,%.3f,%.3f)\n", p1->x * factor, p1->y * factor, 0.0);
290   }
291   fprintf(fh, "bs(%.3f,%.3f,%.3f)\n", p->x * factor, p->y * factor, 0.0);
292}
293
294static void
295sketch_label(const img_point *p, const char *s, bool fSurface)
296{
297   fSurface = fSurface; /* unused */
298   fprintf(fh, "fp((0,0,0))\n");
299   fprintf(fh, "le()\n");
300   fprintf(fh, "Fn('Times-Roman')\n");
301   fprintf(fh, "Fs(5)\n");
302   fprintf(fh, "txt('");
303   while (*s) {
304      int ch = *s++;
305      if (ch == '\'' || ch == '\\') putc('\\', fh);
306      putc(ch, fh);
307   }
308   fprintf(fh, "',(%.3f,%.3f))\n", p->x * factor, p->y * factor);
309}
310
311static void
312sketch_cross(const img_point *p, bool fSurface)
313{
314   fSurface = fSurface; /* unused */
315   fprintf(fh, "b()\n");
316   fprintf(fh, "bs(%.3f,%.3f,%.3f)\n",
317           p->x * factor - MARKER_SIZE, p->y * factor - MARKER_SIZE, 0.0);
318   fprintf(fh, "bs(%.3f,%.3f,%.3f)\n",
319           p->x * factor + MARKER_SIZE, p->y * factor + MARKER_SIZE, 0.0);
320   fprintf(fh, "bn()\n");
321   fprintf(fh, "bs(%.3f,%.3f,%.3f)\n",
322           p->x * factor + MARKER_SIZE, p->y * factor - MARKER_SIZE, 0.0);
323   fprintf(fh, "bs(%.3f,%.3f,%.3f)\n",
324           p->x * factor - MARKER_SIZE, p->y * factor + MARKER_SIZE, 0.0);
325}
326
327static void
328sketch_footer(void)
329{
330   fprintf(fh, "guidelayer('Guide Lines',1,0,0,1,(0,0,1))\n");
331   if (grid) {
332      fprintf(fh, "grid((0,0,%.3f,%.3f),1,(0,0,1),'Grid')\n",
333              grid * factor, grid * factor);
334   }
335}
336
337typedef struct point {
338   img_point p;
339   const char *label;
340   struct point *next;
341} point;
342
343#define HTAB_SIZE 0x2000
344
345static point **htab;
346
347static void
348set_name(const img_point *p, const char *s)
349{
350   int hash;
351   point *pt;
352   union {
353      char data[sizeof(int) * 3];
354      int x[3];
355   } u;
356
357   u.x[0] = (int)(p->x * 100);
358   u.x[1] = (int)(p->y * 100);
359   u.x[2] = (int)(p->z * 100);
360   hash = (hash_data(u.data, sizeof(int) * 3) & (HTAB_SIZE - 1));
361   for (pt = htab[hash]; pt; pt = pt->next) {
362      if (pt->p.x == p->x && pt->p.y == p->y && pt->p.z == p->z) {
363         /* already got name for these coordinates */
364         /* FIXME: what about multiple names for the same station? */
365         return;
366      }
367   }
368
369   pt = osnew(point);
370   pt->label = osstrdup(s);
371   pt->p = *p;
372   pt->next = htab[hash];
373   htab[hash] = pt;
374
375   return;
376}
377
378static const char *
379find_name(const img_point *p)
380{
381   int hash;
382   point *pt;
383   union {
384      char data[sizeof(int) * 3];
385      int x[3];
386   } u;
387   SVX_ASSERT(p);
388
389   u.x[0] = (int)(p->x * 100);
390   u.x[1] = (int)(p->y * 100);
391   u.x[2] = (int)(p->z * 100);
392   hash = (hash_data(u.data, sizeof(int) * 3) & (HTAB_SIZE - 1));
393   for (pt = htab[hash]; pt; pt = pt->next) {
394      if (pt->p.x == p->x && pt->p.y == p->y && pt->p.z == p->z)
395         return pt->label;
396   }
397   return "?";
398}
399
400static bool to_close = false;
401static bool close_g = false;
402
403static void
404svg_header(const char *)
405{
406   size_t i;
407   htab = (point **)osmalloc(HTAB_SIZE * ossizeof(point *));
408   for (i = 0; i < HTAB_SIZE; ++i) htab[i] = NULL;
409   fprintf(fh, "<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>\n");
410   double width = (max_x - min_x) * factor + SVG_MARGIN * 2;
411   double height = (max_y - min_y) * factor + SVG_MARGIN * 2;
412   fprintf(fh, "<svg version=\"1.1\" baseProfile=\"full\"\n"
413               "xmlns=\"http://www.w3.org/2000/svg\"\n"
414               "xmlns:xlink=\"http://www.w3.org/1999/xlink\"\n"
415               "xmlns:ev=\"http://www.w3.org/2001/xml-events\"\n"
416               "width=\"%.3f%s\" height=\"%.3f%s\"\n"
417               "viewBox=\"0 0 %0.3f %0.3f\">\n",
418           width, unit, height, unit, width, height);
419   fprintf(fh, "<g transform=\"translate(%.3f %.3f)\">\n",
420           SVG_MARGIN - min_x * factor, SVG_MARGIN + max_y * factor);
421   to_close = false;
422   close_g = false;
423}
424
425static void
426svg_start_pass(int layer)
427{
428   if (to_close) {
429      fprintf(fh, "\"/>\n");
430      to_close = false;
431   }
432   if (close_g) {
433      fprintf(fh, "</g>\n");
434   }
435   fprintf(fh, "<g id=\"%s\"", layer_names[layer]);
436   if (layer & LEGS)
437      fprintf(fh, " stroke=\"black\" fill=\"none\" stroke-width=\"0.4px\"");
438   else if (layer & STNS)
439      fprintf(fh, " stroke=\"black\" fill=\"none\" stroke-width=\"0.05px\"");
440   else if (layer & LABELS)
441      fprintf(fh, " font-size=\"%.3fem\"", text_height);
442   fprintf(fh, ">\n");
443
444   close_g = true;
445}
446
447static void
448svg_line(const img_point *p1, const img_point *p, bool fSurface, bool fPendingMove)
449{
450   fSurface = fSurface; /* unused */
451   if (fPendingMove) {
452       if (to_close) {
453           fprintf(fh, "\"/>\n");
454       }
455       fprintf(fh, "<path d=\"M%.3f %.3f", p1->x * factor, p1->y * -factor);
456   }
457   fprintf(fh, "L%.3f %.3f", p->x * factor, p->y * -factor);
458   to_close = true;
459}
460
461static void
462svg_label(const img_point *p, const char *s, bool fSurface)
463{
464   fSurface = fSurface; /* unused */
465   fprintf(fh, "<text transform=\"translate(%.3f %.3f)\">",
466           p->x * factor, p->y * -factor);
467   fputs(s, fh);
468   fputs("</text>\n", fh);
469   set_name(p, s);
470}
471
472static void
473svg_cross(const img_point *p, bool fSurface)
474{
475   fSurface = fSurface; /* unused */
476   fprintf(fh, "<circle id=\"%s\" cx=\"%.3f\" cy=\"%.3f\" r=\"%.3f\"/>\n",
477           find_name(p), p->x * factor, p->y * -factor, MARKER_SIZE * SQRT_2);
478   fprintf(fh, "<path d=\"M%.3f %.3fL%.3f %.3fM%.3f %.3fL%.3f %.3f\"/>\n",
479           p->x * factor - MARKER_SIZE, p->y * -factor - MARKER_SIZE,
480           p->x * factor + MARKER_SIZE, p->y * -factor + MARKER_SIZE,
481           p->x * factor + MARKER_SIZE, p->y * -factor - MARKER_SIZE,
482           p->x * factor - MARKER_SIZE, p->y * -factor + MARKER_SIZE );
483}
484
485static void
486svg_footer(void)
487{
488   if (to_close) {
489      fprintf(fh, "\"/>\n");
490      to_close = false;
491   }
492   if (close_g) {
493      fprintf(fh, "</g>\n");
494      close_g = false;
495   }
496   fprintf(fh, "</g>\n</svg>\n");
497}
498
499static void
500plt_header(const char *title)
501{
502   size_t i;
503   htab = (point **)osmalloc(HTAB_SIZE * ossizeof(point *));
504   for (i = 0; i < HTAB_SIZE; ++i) htab[i] = NULL;
505   /* Survex is E, N, Alt - PLT file is N, E, Alt */
506   fprintf(fh, "Z %.3f %.3f %.3f %.3f %.3f %.3f\r\n",
507           min_y / METRES_PER_FOOT, max_y / METRES_PER_FOOT,
508           min_x / METRES_PER_FOOT, max_x / METRES_PER_FOOT,
509           min_z / METRES_PER_FOOT, max_z / METRES_PER_FOOT);
510   fprintf(fh, "N%s D 1 1 1 C%s\r\n", survey ? survey : "X",
511           (title && title[0]) ? title : "X");
512}
513
514static void
515plt_start_pass(int layer)
516{
517   layer = layer;
518}
519
520static void
521plt_line(const img_point *p1, const img_point *p, bool fSurface, bool fPendingMove)
522{
523   fSurface = fSurface; /* unused */
524   if (fPendingMove) {
525       /* Survex is E, N, Alt - PLT file is N, E, Alt */
526       fprintf(fh, "M %.3f %.3f %.3f ",
527               p1->y / METRES_PER_FOOT, p1->x / METRES_PER_FOOT, p1->z / METRES_PER_FOOT);
528       /* dummy passage dimensions are required to avoid compass bug */
529       fprintf(fh, "S%s P -9 -9 -9 -9\r\n", find_name(p1));
530   }
531   /* Survex is E, N, Alt - PLT file is N, E, Alt */
532   fprintf(fh, "D %.3f %.3f %.3f ",
533           p->y / METRES_PER_FOOT, p->x / METRES_PER_FOOT, p->z / METRES_PER_FOOT);
534   /* dummy passage dimensions are required to avoid compass bug */
535   fprintf(fh, "S%s P -9 -9 -9 -9\r\n", find_name(p));
536}
537
538static void
539plt_label(const img_point *p, const char *s, bool fSurface)
540{
541   fSurface = fSurface; /* unused */
542   /* FIXME: also ctrl characters - ought to remap them, not give up */
543   if (strchr(s, ' ')) {
544      fprintf(stderr, "PLT format can't cope with spaces in station names\n");
545      exit(1);
546   }
547   set_name(p, s);
548}
549
550static void
551plt_cross(const img_point *p, bool fSurface)
552{
553   fSurface = fSurface; /* unused */
554   p = p; /* unused */
555}
556
557static void
558plt_footer(void)
559{
560   /* Survex is E, N, Alt - PLT file is N, E, Alt */
561   fprintf(fh, "X %.3f %.3f %.3f %.3f %.3f %.3f\r\n",
562           min_y / METRES_PER_FOOT, max_y / METRES_PER_FOOT,
563           min_x / METRES_PER_FOOT, max_x / METRES_PER_FOOT,
564           min_z / METRES_PER_FOOT, max_z / METRES_PER_FOOT);
565   /* Yucky DOS "end of textfile" marker */
566   putc('\x1a', fh);
567}
568
569static void
570eps_header(const char *title)
571{
572   const char * fontname_labels = "helvetica"; // FIXME
573   int fontsize_labels = 10; // FIXME
574   fputs("%!PS-Adobe-2.0 EPSF-1.2\n", fh);
575   fputs("%%Creator: Survex "VERSION" EPS Output Filter\n", fh);
576
577   if (title && title[0]) fprintf(fh, "%%%%Title: %s\n", title);
578
579   char buf[64];
580   time_t now = time(NULL);
581   if (strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S %Z\n", localtime(&now))) {
582      fputs("%%CreationDate: ", fh);
583      fputs(buf, fh);
584   }
585
586   wxString name;
587#if defined(HAVE_GETPWUID) && !defined(__DJGPP__)
588   struct passwd * ent = getpwuid(getuid());
589   if (ent && ent->pw_gecos[0]) name = ent->pw_gecos;
590#endif
591   if (name.empty()) {
592       name = ::wxGetUserName();
593       if (name.empty()) {
594           name = ::wxGetUserId();
595       }
596   }
597   if (name) {
598       fprintf(fh, "%%%%For: %s\n", name.c_str());
599   }
600
601   fprintf(fh, "%%%%BoundingBox: %d %d %d %d\n",
602           int(floor(min_x * factor)), int(floor(min_y * factor)),
603           int(ceil(max_x * factor)), int(ceil(max_y * factor)));
604   fprintf(fh, "%%%%HiResBoundingBox: %.4f %.4f %.4f %.4f\n",
605           min_x * factor, min_y * factor, max_x * factor, max_y * factor);
606   fputs("%%LanguageLevel: 1\n"
607         "%%PageOrder: Ascend\n"
608         "%%Pages: 1\n"
609         "%%Orientation: Portrait\n", fh);
610
611   fprintf(fh, "%%%%DocumentFonts: %s\n", fontname_labels);
612
613   fputs("%%EndComments\n"
614         "%%Page 1 1\n"
615         "save countdictstack mark\n", fh);
616
617   /* this code adapted from a2ps */
618   fputs("%%BeginResource: encoding ISO88591Encoding\n"
619         "/ISO88591Encoding [\n", fh);
620   fputs("/.notdef /.notdef /.notdef /.notdef\n", fh);
621   fputs("/.notdef /.notdef /.notdef /.notdef\n", fh);
622   fputs("/.notdef /.notdef /.notdef /.notdef\n", fh);
623   fputs("/.notdef /.notdef /.notdef /.notdef\n", fh);
624   fputs("/.notdef /.notdef /.notdef /.notdef\n", fh);
625   fputs("/.notdef /.notdef /.notdef /.notdef\n", fh);
626   fputs("/.notdef /.notdef /.notdef /.notdef\n", fh);
627   fputs("/.notdef /.notdef /.notdef /.notdef\n", fh);
628   fputs(
629"/space /exclam /quotedbl /numbersign\n"
630"/dollar /percent /ampersand /quoteright\n"
631"/parenleft /parenright /asterisk /plus\n"
632"/comma /minus /period /slash\n"
633"/zero /one /two /three\n"
634"/four /five /six /seven\n"
635"/eight /nine /colon /semicolon\n"
636"/less /equal /greater /question\n"
637"/at /A /B /C /D /E /F /G\n"
638"/H /I /J /K /L /M /N /O\n"
639"/P /Q /R /S /T /U /V /W\n"
640"/X /Y /Z /bracketleft\n"
641"/backslash /bracketright /asciicircum /underscore\n"
642"/quoteleft /a /b /c /d /e /f /g\n"
643"/h /i /j /k /l /m /n /o\n"
644"/p /q /r /s /t /u /v /w\n"
645"/x /y /z /braceleft\n"
646"/bar /braceright /asciitilde /.notdef\n", fh);
647   fputs("/.notdef /.notdef /.notdef /.notdef\n", fh);
648   fputs("/.notdef /.notdef /.notdef /.notdef\n", fh);
649   fputs("/.notdef /.notdef /.notdef /.notdef\n", fh);
650   fputs("/.notdef /.notdef /.notdef /.notdef\n", fh);
651   fputs("/.notdef /.notdef /.notdef /.notdef\n", fh);
652   fputs("/.notdef /.notdef /.notdef /.notdef\n", fh);
653   fputs("/.notdef /.notdef /.notdef /.notdef\n", fh);
654   fputs("/.notdef /.notdef /.notdef /.notdef\n", fh);
655   fputs(
656"/space /exclamdown /cent /sterling\n"
657"/currency /yen /brokenbar /section\n"
658"/dieresis /copyright /ordfeminine /guillemotleft\n"
659"/logicalnot /hyphen /registered /macron\n"
660"/degree /plusminus /twosuperior /threesuperior\n"
661"/acute /mu /paragraph /bullet\n"
662"/cedilla /onesuperior /ordmasculine /guillemotright\n"
663"/onequarter /onehalf /threequarters /questiondown\n"
664"/Agrave /Aacute /Acircumflex /Atilde\n"
665"/Adieresis /Aring /AE /Ccedilla\n"
666"/Egrave /Eacute /Ecircumflex /Edieresis\n"
667"/Igrave /Iacute /Icircumflex /Idieresis\n"
668"/Eth /Ntilde /Ograve /Oacute\n"
669"/Ocircumflex /Otilde /Odieresis /multiply\n"
670"/Oslash /Ugrave /Uacute /Ucircumflex\n"
671"/Udieresis /Yacute /Thorn /germandbls\n"
672"/agrave /aacute /acircumflex /atilde\n"
673"/adieresis /aring /ae /ccedilla\n"
674"/egrave /eacute /ecircumflex /edieresis\n"
675"/igrave /iacute /icircumflex /idieresis\n"
676"/eth /ntilde /ograve /oacute\n"
677"/ocircumflex /otilde /odieresis /divide\n"
678"/oslash /ugrave /uacute /ucircumflex\n"
679"/udieresis /yacute /thorn /ydieresis\n"
680"] def\n"
681"%%EndResource\n", fh);
682
683   /* this code adapted from a2ps */
684   fputs(
685"/reencode {\n" /* def */
686"dup length 5 add dict begin\n"
687"{\n" /* forall */
688"1 index /FID ne\n"
689"{ def }{ pop pop } ifelse\n"
690"} forall\n"
691"/Encoding exch def\n"
692
693/* Use the font's bounding box to determine the ascent, descent,
694 * and overall height; don't forget that these values have to be
695 * transformed using the font's matrix.
696 * We use `load' because sometimes BBox is executable, sometimes not.
697 * Since we need 4 numbers and not an array avoid BBox from being executed
698 */
699"/FontBBox load aload pop\n"
700"FontMatrix transform /Ascent exch def pop\n"
701"FontMatrix transform /Descent exch def pop\n"
702"/FontHeight Ascent Descent sub def\n"
703
704/* Define these in case they're not in the FontInfo (also, here
705 * they're easier to get to.
706 */
707"/UnderlinePosition 1 def\n"
708"/UnderlineThickness 1 def\n"
709
710/* Get the underline position and thickness if they're defined. */
711"currentdict /FontInfo known {\n"
712"FontInfo\n"
713
714"dup /UnderlinePosition known {\n"
715"dup /UnderlinePosition get\n"
716"0 exch FontMatrix transform exch pop\n"
717"/UnderlinePosition exch def\n"
718"} if\n"
719
720"dup /UnderlineThickness known {\n"
721"/UnderlineThickness get\n"
722"0 exch FontMatrix transform exch pop\n"
723"/UnderlineThickness exch def\n"
724"} if\n"
725
726"} if\n"
727"currentdict\n"
728"end\n"
729"} bind def\n", fh);
730
731   fprintf(fh, "/lab ISO88591Encoding /%s findfont reencode definefont pop\n",
732           fontname_labels);
733
734   fprintf(fh, "/lab findfont %d scalefont setfont\n", int(fontsize_labels));
735
736   fprintf(fh, "0.1 setlinewidth\n");
737
738#if 0
739   /* C<digit> changes colour */
740   /* FIXME: read from ini */
741   {
742      size_t i;
743      for (i = 0; i < sizeof(colour) / sizeof(colour[0]); ++i) {
744         fprintf(fh, "/C%u {stroke %.3f %.3f %.3f setrgbcolor} def\n", i,
745                 (double)(colour[i] & 0xff0000) / 0xff0000,
746                 (double)(colour[i] & 0xff00) / 0xff00,
747                 (double)(colour[i] & 0xff) / 0xff);
748      }
749   }
750   fputs("C0\n", fh);
751#endif
752
753   /* Postscript definition for drawing a cross */
754   fprintf(fh, "/X {stroke moveto %.2f %.2f rmoveto %.2f %.2f rlineto "
755           "%.2f 0 rmoveto %.2f %.2f rlineto %.2f %.2f rmoveto} def\n",
756           -marker_size, -marker_size,  marker_size * 2, marker_size * 2,
757           -marker_size * 2,  marker_size * 2, -marker_size * 2,
758           -marker_size, marker_size );
759
760   /* define some functions to keep file short */
761   fputs("/M {stroke moveto} def\n"
762         "/L {lineto} def\n"
763/*       "/R {rlineto} def\n" */
764         "/S {show} def\n", fh);
765
766   fprintf(fh, "gsave %.8f dup scale\n", factor);
767#if 0
768   if (grid > 0) {
769      double x, y;
770      x = floor(min_x / grid) * grid + grid;
771      y = floor(min_y / grid) * grid + grid;
772#ifdef DEBUG_CAD3D
773      printf("x_min: %f  y_min: %f\n", x, y);
774#endif
775      while (x < max_x) {
776         /* horizontal line */
777         fprintf(fh, "0\nLINE\n");
778         fprintf(fh, "8\nGrid\n"); /* Layer */
779         fprintf(fh, "10\n%6.2f\n", x);
780         fprintf(fh, "20\n%6.2f\n", min_y);
781         fprintf(fh, "30\n0\n");
782         fprintf(fh, "11\n%6.2f\n", x);
783         fprintf(fh, "21\n%6.2f\n", max_y);
784         fprintf(fh, "31\n0\n");
785         x += grid;
786      }
787      while (y < max_y) {
788         /* vertical line */
789         fprintf(fh, "0\nLINE\n");
790         fprintf(fh, "8\nGrid\n"); /* Layer */
791         fprintf(fh, "10\n%6.2f\n", min_x);
792         fprintf(fh, "20\n%6.2f\n", y);
793         fprintf(fh, "30\n0\n");
794         fprintf(fh, "11\n%6.2f\n", max_x);
795         fprintf(fh, "21\n%6.2f\n", y);
796         fprintf(fh, "31\n0\n");
797         y += grid;
798      }
799   }
800#endif
801}
802
803static void
804eps_start_pass(int layer)
805{
806   layer = layer;
807}
808
809static void
810eps_line(const img_point *p1, const img_point *p, bool fSurface, bool fPendingMove)
811{
812   fSurface = fSurface; /* unused */
813   if (fPendingMove) {
814       fprintf(fh, "%.2f %.2f M\n", p1->x, p1->y);
815   }
816   fprintf(fh, "%.2f %.2f L\n", p->x, p->y);
817}
818
819static void
820eps_label(const img_point *p, const char *s, bool fSurface)
821{
822   fprintf(fh, "%.2f %.2f M\n", p->x, p->y);
823   putc('(', fh);
824   while (*s) {
825       unsigned char ch = *s++;
826       switch (ch) {
827           case '(': case ')': case '\\': /* need to escape these characters */
828               putc('\\', fh);
829               putc(ch, fh);
830               break;
831           default:
832               putc(ch, fh);
833               break;
834       }
835   }
836   fputs(") S\n", fh);
837}
838
839static void
840eps_cross(const img_point *p, bool fSurface)
841{
842   fSurface = fSurface; /* unused */
843   fprintf(fh, "%.2f %.2f X\n", p->x, p->y);
844}
845
846static void
847eps_footer(void)
848{
849   fputs("stroke showpage grestore\n"
850         "%%Trailer\n"
851         "cleartomark countdictstack exch sub { end } repeat restore\n"
852         "%%EOF\n", fh);
853}
854
855static int dxf_passes[] = { LEGS|STNS|LABELS, 0 };
856static int sketch_passes[] = { LEGS, STNS, LABELS, 0 };
857static int plt_passes[] = { LABELS, LEGS, 0 };
858static int svg_passes[] = { LEGS, LABELS, STNS, 0 };
859static int eps_passes[] = { LEGS|STNS|LABELS, 0 };
860
861typedef enum {
862    FMT_DXF = 0, FMT_SVG, FMT_SKETCH, FMT_PLT, FMT_EPS, FMT_ENDMARKER
863} export_format;
864static const char *extensions[] = { "dxf", "svg", "sk", "plt", "eps" };
865
866bool
867Export(const wxString &fnm_out, const wxString &title, const MainFrm * mainfrm,
868       double pan, double tilt, bool labels, bool crosses, bool legs,
869       bool surface)
870{
871   int fSeenMove = 0;
872   int fPendingMove = 0;
873   img_point p, p1;
874   double s = 0, c = 0;
875   export_format format = FMT_DXF;
876   const int *pass;
877   bool elevation = (tilt == 0.0);
878   double elev_angle = pan;
879
880   void (*header)(const char *);
881   void (*start_pass)(int);
882   void (*line)(const img_point *, const img_point *, bool, bool);
883   void (*label)(const img_point *, const char *, bool);
884   void (*cross)(const img_point *, bool);
885   void (*footer)(void);
886   const char *mode = "w"; /* default to text output */
887
888   /* Defaults */
889   grid = 0;
890   text_height = TEXT_HEIGHT;
891   marker_size = MARKER_SIZE;
892#if 0
893   cmdline_init(argc, argv, short_opts, long_opts, NULL, help, 1, 2);
894   while (1) {
895      int opt = cmdline_getopt();
896      if (opt == EOF) break;
897      switch (opt) {
898       case 'e': /* Elevation */
899         elevation = 1;
900         elev_angle = cmdline_double_arg();
901         break;
902       case 'c': /* Crosses */
903         crosses = 0;
904         break;
905       case 'n': /* Labels */
906         labels = 0;
907         break;
908       case 'l': /* Legs */
909         legs = 0;
910         break;
911       case 'g': /* Grid */
912         if (optarg) {
913            grid = cmdline_double_arg();
914         } else {
915            grid = (double)GRID_SPACING;
916         }
917         break;
918       case 'r': /* Reduction factor */
919         scale = cmdline_double_arg();
920         break;
921       case 't': /* Text height */
922         text_height = cmdline_double_arg();
923#ifdef DEBUG_CAD3D
924         printf("Text Height: `%s' input, converted to %6.2f\n", optarg, text_height);
925#endif
926         break;
927       case 'm': /* Marker size */
928         marker_size = cmdline_double_arg();
929#ifdef DEBUG_CAD3D
930         printf("Marker Size: `%s', converted to %6.2f\n", optarg, marker_size);
931#endif
932         break;
933       case 'D':
934         format = FMT_DXF;
935         break;
936       case 'S':
937         format = FMT_SKETCH;
938         break;
939       case 'P':
940         format = FMT_PLT;
941         break;
942       case 'V':
943         format = FMT_SVG;
944         break;
945       case 's':
946         survey = optarg;
947         break;
948#ifdef DEBUG_CAD3D
949       default:
950         printf("Internal Error: 'getopt' returned '%c' %d\n", opt, opt);
951#endif
952      }
953   }
954#endif
955
956   {
957      size_t i;
958      size_t len = fnm_out.length();
959      for (i = 0; i < FMT_ENDMARKER; ++i) {
960         size_t l = strlen(extensions[i]);
961         if (len > l + 1 && fnm_out[len - l - 1] == FNM_SEP_EXT &&
962             strcasecmp(fnm_out.c_str() + len - l, extensions[i]) == 0) {
963            format = export_format(i);
964            break;
965         }
966      }
967   }
968   switch (format) {
969    case FMT_DXF:
970      header = dxf_header;
971      start_pass = dxf_start_pass;
972      line = dxf_line;
973      label = dxf_label;
974      cross = dxf_cross;
975      footer = dxf_footer;
976      pass = dxf_passes;
977      break;
978    case FMT_SKETCH:
979      header = sketch_header;
980      start_pass = sketch_start_pass;
981      line = sketch_line;
982      label = sketch_label;
983      cross = sketch_cross;
984      footer = sketch_footer;
985      pass = sketch_passes;
986      factor = POINTS_PER_MM * 1000.0 / scale;
987      mode = "wb"; /* Binary file output */
988      break;
989    case FMT_PLT:
990      header = plt_header;
991      start_pass = plt_start_pass;
992      line = plt_line;
993      label = plt_label;
994      cross = plt_cross;
995      footer = plt_footer;
996      pass = plt_passes;
997      mode = "wb"; /* Binary file output */
998      break;
999    case FMT_SVG:
1000      header = svg_header;
1001      start_pass = svg_start_pass;
1002      line = svg_line;
1003      label = svg_label;
1004      cross = svg_cross;
1005      footer = svg_footer;
1006      pass = svg_passes;
1007      factor = 1000.0 / scale;
1008      mode = "wb"; /* Binary file output */
1009      break;
1010    case FMT_EPS:
1011      header = eps_header;
1012      start_pass = eps_start_pass;
1013      line = eps_line;
1014      label = eps_label;
1015      cross = eps_cross;
1016      footer = eps_footer;
1017      pass = eps_passes;
1018      factor = POINTS_PER_MM * 1000.0 / scale;
1019      mode = "wb"; /* Binary file output */
1020      break;
1021    default:
1022      exit(1);
1023   }
1024
1025   fh = fopen(fnm_out.c_str(), mode);
1026   if (!fh) return false;
1027
1028   if (elevation) {
1029      s = sin(rad(elev_angle));
1030      c = cos(rad(elev_angle));
1031   }
1032
1033   /* Get drawing corners */
1034   min_x = min_y = min_z = HUGE_VAL;
1035   max_x = max_y = max_z = -HUGE_VAL;
1036   for (int band = 0; band < mainfrm->GetNumDepthBands(); ++band) {
1037        list<PointInfo*>::const_iterator pos = mainfrm->GetPoints(band);
1038        list<PointInfo*>::const_iterator end = mainfrm->GetPointsEnd(band);
1039        for ( ; pos != end; ++pos) {
1040            p.x = (*pos)->GetX();
1041            p.y = (*pos)->GetY();
1042            p.z = (*pos)->GetZ();
1043
1044            if (elevation) {
1045                double xnew = p.x * c - p.y * s;
1046                double znew = - p.x * s - p.y * c;
1047                p.y = p.z;
1048                p.z = znew;
1049                p.x = xnew;
1050            }
1051
1052            if (p.x < min_x) min_x = p.x;
1053            if (p.x > max_x) max_x = p.x;
1054            if (p.y < min_y) min_y = p.y;
1055            if (p.y > max_y) max_y = p.y;
1056            if (p.z < min_z) min_z = p.z;
1057            if (p.z > max_z) max_z = p.z;
1058        }
1059   }
1060   {
1061        list<LabelInfo*>::const_iterator pos = mainfrm->GetLabels();
1062        list<LabelInfo*>::const_iterator end = mainfrm->GetLabelsEnd();
1063        for ( ; pos != end; ++pos) {
1064            p.x = (*pos)->GetX();
1065            p.y = (*pos)->GetY();
1066            p.z = (*pos)->GetZ();
1067
1068            if (elevation) {
1069                double xnew = p.x * c - p.y * s;
1070                double znew = - p.x * s - p.y * c;
1071                p.y = p.z;
1072                p.z = znew;
1073                p.x = xnew;
1074            }
1075
1076            if (p.x < min_x) min_x = p.x;
1077            if (p.x > max_x) max_x = p.x;
1078            if (p.y < min_y) min_y = p.y;
1079            if (p.y > max_y) max_y = p.y;
1080            if (p.z < min_z) min_z = p.z;
1081            if (p.z > max_z) max_z = p.z;
1082        }
1083   }
1084
1085   if (grid > 0) {
1086      min_x -= grid / 2;
1087      max_x += grid / 2;
1088      min_y -= grid / 2;
1089      max_y += grid / 2;
1090   }
1091
1092   /* handle empty file gracefully */
1093   if (min_x > max_x) {
1094      min_x = min_y = min_z = 0;
1095      max_x = max_y = max_z = 0;
1096   }
1097
1098   /* Header */
1099   header(title.c_str());
1100
1101   p1.x = p1.y = p1.z = 0; /* avoid compiler warning */
1102
1103   for ( ; *pass; ++pass) {
1104      bool legs_this_pass = ((*pass & LEGS) && legs);
1105      bool crosses_this_pass = ((*pass & STNS) && crosses);
1106      bool labels_this_pass = ((*pass & LABELS) && labels);
1107      if (!(legs_this_pass || crosses_this_pass || labels_this_pass))
1108          continue;
1109      start_pass(*pass);
1110      if (legs_this_pass) {
1111         for (int band = 0; band < mainfrm->GetNumDepthBands(); ++band) {
1112             list<PointInfo*>::const_iterator pos = mainfrm->GetPoints(band);
1113             list<PointInfo*>::const_iterator end = mainfrm->GetPointsEnd(band);
1114             for ( ; pos != end; ++pos) {
1115                 p.x = (*pos)->GetX();
1116                 p.y = (*pos)->GetY();
1117                 p.z = (*pos)->GetZ();
1118
1119                 if (format == FMT_SKETCH) {
1120                     p.x -= min_x;
1121                     p.y -= min_y;
1122                     p.z -= min_z;
1123                 }
1124
1125                 if (elevation) {
1126                     double xnew = p.x * c - p.y * s;
1127                     double znew = - p.x * s - p.y * c;
1128                     p.y = p.z;
1129                     p.z = znew;
1130                     p.x = xnew;
1131                 }
1132
1133                 if (!(*pos)->IsLine()) {
1134#ifdef DEBUG_CAD3D
1135                     printf("move to %9.2f %9.2f %9.2f\n",x,y,z);
1136#endif
1137                     fPendingMove = 1;
1138                     fSeenMove = 1;
1139                 } else {
1140#ifdef DEBUG_CAD3D
1141                     printf("line to %9.2f %9.2f %9.2f\n", p.x, p.y, p.z);
1142#endif
1143                     if (!fSeenMove) {
1144                         p1 = p;
1145                         fPendingMove = 1;
1146                         fSeenMove = 1;
1147                     }
1148                     if (surface || !(*pos)->IsSurface()) {
1149                         line(&p1, &p, (*pos)->IsSurface(), fPendingMove);
1150                         fPendingMove = 0;
1151                     } else {
1152                         fPendingMove = 1;
1153                     }
1154                 }
1155                 p1 = p;
1156             }
1157         }
1158      }
1159      if (crosses_this_pass || labels_this_pass) {
1160        list<LabelInfo*>::const_iterator pos = mainfrm->GetLabels();
1161        list<LabelInfo*>::const_iterator end = mainfrm->GetLabelsEnd();
1162        for ( ; pos != end; ++pos) {
1163            p.x = (*pos)->GetX();
1164            p.y = (*pos)->GetY();
1165            p.z = (*pos)->GetZ();
1166
1167            if (format == FMT_SKETCH) {
1168               p.x -= min_x;
1169               p.y -= min_y;
1170               p.z -= min_z;
1171            }
1172
1173            if (elevation) {
1174                double xnew = p.x * c - p.y * s;
1175                double znew = - p.x * s - p.y * c;
1176                p.y = p.z;
1177                p.z = znew;
1178                p.x = xnew;
1179            }
1180#ifdef DEBUG_CAD3D
1181            printf("label '%s' at %9.2f %9.2f %9.2f\n",(*pos)->GetText(),x,y,z);
1182#endif
1183            /* Use !UNDERGROUND as the criterion - we want stations where
1184             * a surface and underground survey meet to be in the
1185             * underground layer */
1186            if (labels_this_pass)
1187                label(&p, (*pos)->GetText(), !(*pos)->IsUnderground());
1188            if (crosses_this_pass)
1189                cross(&p, !(*pos)->IsUnderground());
1190        }
1191      }
1192   }
1193   footer();
1194   safe_fclose(fh);
1195   osfree(htab);
1196   htab = NULL;
1197   return true;
1198}
Note: See TracBrowser for help on using the repository browser.