/* emit.c
Copyright (C) 2005,2006,2007 Eugene K. Ressler, Jr.
This file is part of Sketch, a small, simple system for making
3d drawings with LaTeX and the PSTricks or TikZ package.
Sketch is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3, or (at your option)
any later version.
Sketch is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Sketch; see the file COPYING.txt. If not, see
http://www.gnu.org/copyleft */
#include <math.h>
#include "emit.h"
#include "scene.h"
#include "version.h"
// ---- emit output -----------------------------------------------------------
char standard_us_doc_template_file_name_flag[] =
"<standard us doc template file name flag>";
char standard_euro_doc_template_file_name_flag[] =
"<standard euro doc template file name flag>";
// concise floating point output
// idea of %g but with fixed rather than relative precision
// removing excessive 0's often reduces output file size dramatically
// and also eases reading
char *
flt_str_fmt (char *fmt, char *buf, double f)
{
size_t i;
sprintf (buf, fmt, f);
// trim off useless zeros and decimals
for (i = strlen (buf); i > 0 && buf[i - 1] == '0'; i--)
/* skip */ ;
if (i > 0 && buf[i - 1] == '.')
i--;
buf[i] = '\0';
// remove leading zeros before decimal
if (buf[0] == '0' && buf[1] == '.')
for (i = 0; (buf[i] = buf[i + 1]) != '\0'; i++)
/* skip */ ;
else if (buf[0] == '-' && buf[1] == '0' && buf[2] == '.')
for (i = 1; (buf[i] = buf[i + 1]) != '\0'; i++)
/* skip */ ;
// fix -0
if (strcmp (buf, "-0") == 0)
strcpy (buf, "0");
return buf;
}
char *
flt_str (char *buf, double f)
{
return flt_str_fmt ("%.3f", buf, f);
}
// scan and return all the legal forms of special arg: ints, int range,
// *arg_len is set to number of chars consumed by scanning even if there are range errors
// return value is number of good arg indices scanned into *arg_index_1|2
static int
scan_special_arg (SPECIAL_OBJECT * special, int i, // start index
int *arg_len, // chars scanned
int *arg_index_1, // arg array indices (# - 1)
int *arg_index_2, SRC_LINE line) // line # for error messages
{
int i1, i2, len, n_args, n_errs;
// try two-arg cases and then one arg and then assume zero
if (sscanf (&special->code[i], "%d-%d%n", &i1, &i2, &len) >= 2 ||
sscanf (&special->code[i], "{%d-%d}%n", &i1, &i2, &len) >= 2)
{
*arg_len = len;
*arg_index_1 = i1 - 1;
*arg_index_2 = i2 - 1;
n_args = 2;
}
else if (sscanf (&special->code[i], "%d%n", &i1, &len) >= 1 ||
sscanf (&special->code[i], "{%d}%n", &i1, &len) >= 1)
{
*arg_len = len;
*arg_index_1 = i1 - 1;
n_args = 1;
}
else
{
*arg_len = 0;
n_args = 0;
}
n_errs = 0;
if (n_args >= 1 && (i1 < 1 || i1 > special->pts->n_pts))
{
err (line, "special arg #%d: out of range #[1-%d]", i1,
special->pts->n_pts);
++n_errs;
}
if (n_args >= 2 && (i2 < 1 || i2 > special->pts->n_pts))
{
err (line, "special arg #n-%d: out of range #[1-%d]", i2,
special->pts->n_pts);
++n_errs;
}
return n_errs > 0 ? 0 : n_args;
}
// TikZ only does integer angles
char *
fmt_angle_tikz (char *buf, double theta, SRC_LINE line)
{
int i_theta = (int) ((theta >= 0) ? (theta + 0.5) : (theta - 0.5));
double err = theta - i_theta;
if (fabs (err) >= 0.1)
warn (line, "TikZ angle rounding error is %.2 degrees", err);
return flt_str_fmt ("%1.f", buf, theta);
}
char *
fmt_angle_pst (char *buf, double theta, SRC_LINE line)
{
return flt_str (buf, theta);
}
typedef char *(*FMT_ANGLE_FUNC) (char *buf, double theta, SRC_LINE line);
FMT_ANGLE_FUNC fmt_angle_tbl[] = {
fmt_angle_pst,
fmt_angle_tikz,
fmt_angle_pst,
fmt_angle_tikz,
};
// this parses, substitues, notes any errors, and (if f is set) prints special output
// so it's used both for syntax checking during input and to generate output
void
process_special (FILE * f, SPECIAL_OBJECT * special, SRC_LINE line)
{
char ch, buf1[16], buf2[16];
int i_arg_prev, i_arg, arg_len, arg_index_1, arg_index_2;
i_arg_prev = i_arg = 0;
while ((ch = special->code[i_arg]) != '\0')
{
if (ch == '#')
{
if (special->code[i_arg + 1] == '#')
{
if (f)
fprintf (f, "%.*s#",
i_arg - i_arg_prev, &special->code[i_arg_prev]);
arg_len = 1;
}
else
{
switch (scan_special_arg
(special, i_arg + 1, &arg_len, &arg_index_1,
&arg_index_2, line))
{
case 2:
if (f)
fprintf (f, "%.*s{%s}", i_arg - i_arg_prev, // number of chars to write
&special->code[i_arg_prev], // start of chars
(*fmt_angle_tbl
[global_env->output_language])
(buf1,
180 / PI *
atan2 (special->pts->v[arg_index_2][Y] -
special->pts->v[arg_index_1][Y],
special->pts->v[arg_index_2][X] -
special->pts->v[arg_index_1][X]), line));
break;
case 1:
if (f)
fprintf (f, "%.*s(%s,%s)", i_arg - i_arg_prev, // number of chars to write
&special->code[i_arg_prev], // start of chars
flt_str (buf1,
special->pts->v[arg_index_1][X]),
flt_str (buf2, special->pts->v[arg_index_1][Y]));
break;
case 0:
if (arg_len == 0)
{ // couldn't scan an index at all
if (f)
fprintf (f, "%.*s#", i_arg - i_arg_prev,
&special->code[i_arg_prev]);
warn (line,
"use of '#' not as special arg (try ##)",
arg_len, &special->code[i_arg]);
}
break;
}
}
i_arg += (arg_len + 1);
i_arg_prev = i_arg;
}
else
{
++i_arg;
}
}
// print out the last stretch of code
if (f)
fprintf (f, "%s\n", &special->code[i_arg_prev]);
}
static void
emit_points_pst (FILE * f, POINT_LIST_3D * pts)
{
int i;
char buf1[16], buf2[16];
for (i = 0; i < pts->n_pts; i++)
fprintf (f, "(%s,%s)",
flt_str (buf1, pts->v[i][X]), flt_str (buf2, pts->v[i][Y]));
}
static void
emit_dots_pst (FILE * f, OBJECT * obj)
{
DOTS_OBJECT *dots = (DOTS_OBJECT *) obj;
fprintf (f, "\\psdots");
emit_opts (f, dots->opts, global_env->output_language);
emit_points_pst (f, dots->pts);
fprintf (f, "\n");
}
static void
emit_line_pst (FILE * f, OBJECT * obj)
{
LINE_OBJECT *line = (LINE_OBJECT *) obj;
fprintf (f, "\\psline");
emit_opts (f, line->opts, global_env->output_language);
emit_points_pst (f, line->pts);
fprintf (f, "\n");
}
static void
emit_curve_pst (FILE * f, OBJECT * obj)
{
CURVE_OBJECT *curve = (CURVE_OBJECT *) obj;
fprintf (f, "\\pscurve");
emit_opts (f, curve->opts, global_env->output_language);
emit_points_pst (f, curve->pts);
fprintf (f, "\n");
}
static void
emit_polygon_pst (FILE * f, OBJECT * obj)
{
POLYGON_OBJECT *poly = (POLYGON_OBJECT *) obj;
fprintf (f, "\\pspolygon");
emit_opts (f, poly->opts, global_env->output_language);
emit_points_pst (f, poly->pts);
fprintf (f, "\n");
}
static void
emit_special_pst (FILE * f, OBJECT * obj)
{
process_special (f, (SPECIAL_OBJECT *) obj, no_line);
}
typedef void (*EMIT_FUNC) (FILE * f, OBJECT *);
static EMIT_FUNC emit_tbl_pst[] = {
NULL, // O_BASE
NULL, // O_TAG_DEF
NULL, // O_OPTS_DEF
NULL, // O_SCALAR_DEF
NULL, // O_POINT_DEF
NULL, // O_VECTOR_DEF
NULL, // O_TRANSFORM_DEF
emit_dots_pst,
emit_line_pst,
emit_curve_pst,
emit_polygon_pst,
emit_special_pst,
NULL, // O_SWEEP (flattened)
NULL, // O_REPEAT (flattened)
NULL, // O_COMPOUND (flattened)
};
static void
emit_points_tkz (FILE * f, POINT_LIST_3D * pts, char *twixt, char *final)
{
int i;
char buf1[16], buf2[16];
for (i = 0; i < pts->n_pts; i++)
fprintf (f, "(%s,%s)%s",
flt_str (buf1, pts->v[i][X]),
flt_str (buf2, pts->v[i][Y]),
(i == pts->n_pts - 1) ? final : twixt);
}
static void
emit_dots_tkz (FILE * f, OBJECT * obj)
{
static char *skip[] = { "dotsize", NULL };
char *dotsize, *cmd;
DOTS_OBJECT *dots = (DOTS_OBJECT *) obj;
// An ugly hack because TikZ uses special syntax for circles...
dotsize = opt_val(dots->opts, "dotsize");
if (dotsize == NULL)
dotsize = "2pt";
cmd = safe_malloc(strlen(dotsize) + 100);
sprintf(cmd, " circle (%s)", dotsize);
fprintf (f, "\\filldraw");
emit_opts_with_exceptions (f, dots->opts, skip, global_env->output_language);
emit_points_tkz (f, dots->pts, cmd, cmd);
fprintf (f, ";\n");
safe_free(cmd);
}
static void
emit_line_tkz (FILE * f, OBJECT * obj)
{
LINE_OBJECT *line = (LINE_OBJECT *) obj;
fprintf (f, "\\draw");
emit_opts (f, line->opts, global_env->output_language);
emit_points_tkz (f, line->pts, "--", "");
fprintf (f, ";\n");
}
static void
emit_curve_tkz (FILE * f, OBJECT * obj)
{
CURVE_OBJECT *curve = (CURVE_OBJECT *) obj;
fprintf (f, "\\curve");
emit_opts (f, curve->opts, global_env->output_language);
emit_points_tkz (f, curve->pts, "--", "");
fprintf (f, ";\n");
}
static void
emit_polygon_tkz (FILE * f, OBJECT * obj)
{
POLYGON_OBJECT *poly = (POLYGON_OBJECT *) obj;
fprintf (f, "\\filldraw");
emit_opts (f, poly->opts, global_env->output_language);
emit_points_tkz (f, poly->pts, "--", "--cycle");
fprintf (f, ";\n");
}
static void
emit_special_tkz (FILE * f, OBJECT * obj)
{
process_special (f, (SPECIAL_OBJECT *) obj, no_line);
}
static EMIT_FUNC emit_tbl_tkz[] = {
NULL, // O_BASE
NULL, // O_TAG_DEF
NULL, // O_OPTS_DEF
NULL, // O_SCALAR_DEF
NULL, // O_POINT_DEF
NULL, // O_VECTOR_DEF
NULL, // O_TRANSFORM_DEF
emit_dots_tkz,
emit_line_tkz,
emit_curve_tkz,
emit_polygon_tkz,
emit_special_tkz,
NULL, // O_SWEEP (flattened)
NULL, // O_REPEAT (flattened)
NULL, // O_COMPOUND (flattened)
};
static EMIT_FUNC *emit_tbl_tbl[] = {
emit_tbl_pst,
emit_tbl_tkz,
emit_tbl_pst,
emit_tbl_tkz,
};
#define DOC_TEMPLATE_ESCAPE_STRING "%%SKETCH_OUTPUT%%"
#define DOC_TEMPLATE_ESCAPE_STRING_LEN (sizeof(DOC_TEMPLATE_ESCAPE_STRING) - 1)
char standard_us_doc_template_tikz_latex[] =
"\\documentclass[letterpaper,12pt]{article}\n"
"\\usepackage[x11names,rgb]{xcolor}\n"
"\\usepackage{tikz}\n"
"\\usetikzlibrary{snakes}\n"
"\\usetikzlibrary{arrows}\n"
"\\usetikzlibrary{shapes}\n"
"\\usetikzlibrary{backgrounds}\n"
"\\usepackage{amsmath}\n"
"\\oddsidemargin 0in\n"
"\\evensidemargin 0in\n"
"\\topmargin 0in\n"
"\\headheight 0in\n"
"\\headsep 0in\n"
"\\textheight 9in\n"
"\\textwidth 6.5in\n"
"\\begin{document}\n"
"\\pagestyle{empty}\n"
"\\vspace*{\\fill}\n"
"\\begin{center}\n"
DOC_TEMPLATE_ESCAPE_STRING "\n"
"\\end{center}\n" "\\vspace*{\\fill}\n" "\\end{document}\n";
char standard_euro_doc_template_tikz_latex[] =
"\\documentclass[a4paper,12pt]{article}\n"
"\\usepackage[x11names,rgb]{xcolor}\n"
"\\usepackage{tikz}\n"
"\\usetikzlibrary{snakes}\n"
"\\usetikzlibrary{arrows}\n"
"\\usetikzlibrary{shapes}\n"
"\\usetikzlibrary{backgrounds}\n"
"\\usepackage{amsmath}\n"
"\\oddsidemargin -10mm\n"
"\\evensidemargin -10mm\n"
"\\topmargin 5mm\n"
"\\headheight 0cm\n"
"\\headsep 0cm\n"
"\\textheight 247mm\n"
"\\textwidth 160mm\n"
"\\begin{document}\n"
"\\pagestyle{empty}\n"
"\\vspace*{\\fill}\n"
"\\begin{center}\n"
DOC_TEMPLATE_ESCAPE_STRING "\n"
"\\end{center}\n" "\\vspace*{\\fill}\n" "\\end{document}\n";
char standard_us_doc_template_pst_latex[] =
"\\documentclass[letterpaper,12pt]{article}\n"
"\\usepackage{amsmath}\n"
"\\usepackage{pstricks}\n"
"\\usepackage{pstricks-add}\n"
"\\oddsidemargin 0in\n"
"\\evensidemargin 0in\n"
"\\topmargin 0in\n"
"\\headheight 0in\n"
"\\headsep 0in\n"
"\\textheight 9in\n"
"\\textwidth 6.5in\n"
"\\begin{document}\n"
"\\pagestyle{empty}\n"
"\\vspace*{\\fill}\n"
"\\begin{center}\n"
DOC_TEMPLATE_ESCAPE_STRING "\n"
"\\end{center}\n" "\\vspace*{\\fill}\n" "\\end{document}\n";
char standard_euro_doc_template_pst_latex[] =
"\\documentclass[a4paper,12pt]{article}\n"
"\\usepackage{amsmath}\n"
"\\usepackage{pstricks}\n"
"\\usepackage{pstricks-add}\n"
"\\oddsidemargin -10mm\n"
"\\evensidemargin -10mm\n"
"\\topmargin 5mm\n"
"\\headheight 0cm\n"
"\\headsep 0cm\n"
"\\textheight 247mm\n"
"\\textwidth 160mm\n"
"\\begin{document}\n"
"\\pagestyle{empty}\n"
"\\vspace*{\\fill}\n"
"\\begin{center}\n"
DOC_TEMPLATE_ESCAPE_STRING "\n"
"\\end{center}\n" "\\vspace*{\\fill}\n" "\\end{document}\n";
/* ---------------------------------------------------------------------- */
char standard_us_doc_template_tikz_context[] =
"\\usemodule[tikz] \\usetikzlibrary[snakes,arrows,shapes,backgrounds]\n"
"\\setuppapersize[letter][letter]\n"
"\\setuplayout[topspace=0in,backspace=0in,header=0in,footer=0in,height=middle,width=middle]\n"
"\\setuppagenumbering[state=stop] % no page numbers\n"
"\\starttext\n"
"\\startalignment[middle]\n"
DOC_TEMPLATE_ESCAPE_STRING "\n"
"\\stopalignment\n"
"\\stoptext\n";
char standard_euro_doc_template_tikz_context[] =
"\\usemodule[tikz] \\usetikzlibrary[snakes,arrows,shapes,backgrounds]\n"
"\\setuppapersize[a4][a4]\n"
"\\setuplayout[topspace=0cm,backspace=0cm,header=0cm,footer=0cm,height=middle,width=middle]\n"
"\\setuppagenumbering[state=stop] % no page numbers\n"
"\\starttext\n"
"\\startalignment[middle]\n"
DOC_TEMPLATE_ESCAPE_STRING "\n"
"\\stopalignment\n"
"\\stoptext\n";
char standard_us_doc_template_pst_context[] =
"PSTricks does not work with ConTeXt as of 1 Feb 2008.\n";
char standard_euro_doc_template_pst_context[] =
"PSTricks does not work with ConTeXt as of 1 Feb 2008.\n";
char *standard_us_doc_template[] = {
standard_us_doc_template_pst_latex,
standard_us_doc_template_tikz_latex,
standard_us_doc_template_pst_context,
standard_us_doc_template_tikz_context,
};
char *standard_euro_doc_template[] = {
standard_euro_doc_template_pst_latex,
standard_euro_doc_template_tikz_latex,
standard_euro_doc_template_pst_context,
standard_euro_doc_template_tikz_context,
};
char *
read_file_as_string (FILE * f)
{
size_t len = 0;
int buf_size = 1024;
char *buf = safe_malloc (buf_size + 1);
for (;;)
{
len += fread (buf + len, 1, buf_size - len, f);
if (feof (f) || ferror (f))
{
buf[len] = '\0';
return buf;
}
buf_size *= 2;
buf = safe_realloc (buf, buf_size + 1);
}
}
char *
doc_template_from_file (char *file_name, int output_language)
{
FILE *f;
char *r;
if (file_name == NULL)
return NULL;
if (file_name == standard_us_doc_template_file_name_flag)
return safe_strdup (standard_us_doc_template[output_language]);
if (file_name == standard_euro_doc_template_file_name_flag)
return safe_strdup (standard_euro_doc_template[output_language]);
f = fopen (file_name, "r");
if (!f)
{
err (no_line, "can't open document template '%s%' for input\n",
file_name);
return safe_strdup (standard_us_doc_template_pst_latex);
}
r = read_file_as_string (f);
fclose (f);
return r;
}
void
emit_preamble_pst_latex (FILE * f, BOX_3D * ext, GLOBAL_ENV * env)
{
char buf1[16], buf2[16], buf3[16], buf4[16];
if (global_env_is_set_p (env, GE_OPTS))
{
fprintf (f, "\\psset{");
emit_opts_raw (f, env->opts, global_env->output_language);
fprintf (f, "}\n");
}
if (global_env_is_set_p (env, GE_FRAME))
{
if (env->frame_opts)
fprintf (f, "\\psframebox[%s]{", env->frame_opts);
else
fprintf (f, "\\psframebox[framesep=0pt]{");
}
fprintf (f, "\\begin{pspicture%s}",
global_env_is_set_p (env, GE_EXTENT) ? "*" : "");
if (global_env_is_set_p (env, GE_BASELINE))
fprintf (f, "[%s]", flt_str (buf1, env->baseline));
fprintf (f,
"(%s,%s)(%s,%s)\n",
flt_str (buf1, ext->min[X]),
flt_str (buf2, ext->min[Y]),
flt_str (buf3, ext->max[X]), flt_str (buf4, ext->max[Y]));
if (cmp_with_global_pst_version(env, STRINGIFY(PST_LINEJOIN_VERSION), no_line) < 0)
{
// old way to set linejoin
fprintf (f, "\\pstVerb{1 setlinejoin}\n");
}
else
{
fprintf (f,
"%% If your PSTricks is earlier than Version "
STRINGIFY(PST_LINEJOIN_VERSION) ", it will fail here.\n"
"%% Use sketch -V option for backward compatibility.\n"
"\\psset{linejoin=1}\n");
}
}
void
emit_preamble_tkz_latex (FILE * f, BOX_3D * ext, GLOBAL_ENV * env)
{
char buf1[16], buf2[16], buf3[16], buf4[16];
int picture_opts_p = 0;
if (global_env_is_set_p (env, GE_FRAME))
{
if (env->frame_opts)
warn (no_line, "frame options [%s] ignored (TikZ)", env->frame_opts);
else
{
fprintf (f, "{\\fboxsep=0pt\\fbox{");
warn (no_line,
"remove frame around TikZ/PGF pictures for debugging");
}
}
fprintf (f, "\\begin{tikzpicture}[join=round");
if (global_env_is_set_p (env, GE_OPTS))
{
fprintf (f, ",");
emit_opts_raw (f, env->opts, global_env->output_language);
}
if (global_env_is_set_p (env, GE_BASELINE))
{
fprintf (f, ",");
fprintf (f, "baseline=%s", flt_str (buf1, env->baseline));
}
fprintf (f, "]\n");
if (global_env_is_set_p (env, GE_EXTENT))
{
flt_str (buf1, ext->min[X]);
flt_str (buf2, ext->min[Y]);
flt_str (buf3, ext->max[X]);
flt_str (buf4, ext->max[Y]);
fprintf (f,
"\\useasboundingbox(%s,%s) rectangle (%s,%s);\n"
"\\clip(%s,%s) rectangle (%s,%s);\n",
buf1, buf2, buf3, buf4, buf1, buf2, buf3, buf4);
}
}
// -----------------------------------------------------------------
void
emit_preamble_pst_context (FILE * f, BOX_3D * ext, GLOBAL_ENV * env)
{
char buf1[16], buf2[16], buf3[16], buf4[16];
if (global_env_is_set_p (env, GE_OPTS))
{
fprintf (f, "\\psset{");
emit_opts_raw (f, env->opts, global_env->output_language);
fprintf (f, "}\n");
}
fprintf (f,
"%% ConTeXt does not yet support PSTricks.\n"
"%% This is a guess at what the syntax might be.\n");
if (global_env_is_set_p (env, GE_FRAME))
{
if (env->frame_opts)
fprintf (f, "\\psframebox[%s]{", env->frame_opts);
else
fprintf (f, "\\psframebox[framesep=0pt]{");
}
fprintf (f, "\\startpspicture%s",
global_env_is_set_p (env, GE_EXTENT) ? "*" : "");
if (global_env_is_set_p (env, GE_BASELINE))
fprintf (f, "[%s]", flt_str (buf1, env->baseline));
fprintf (f,
"(%s,%s)(%s,%s)\n",
flt_str (buf1, ext->min[X]),
flt_str (buf2, ext->min[Y]),
flt_str (buf3, ext->max[X]), flt_str (buf4, ext->max[Y]));
fprintf (f, "\\pstVerb{1 setlinejoin}\n");
}
void
emit_preamble_tkz_context (FILE * f, BOX_3D * ext, GLOBAL_ENV * env)
{
char buf1[16], buf2[16], buf3[16], buf4[16];
int picture_opts_p = 0;
if (global_env_is_set_p (env, GE_FRAME))
{
if (env->frame_opts)
warn (no_line, "frame options [%s] ignored (TikZ)", env->frame_opts);
else
{
fprintf (f, "{\\fboxsep=0pt\\fbox{");
warn (no_line,
"remove frame around TikZ/PGF pictures for debugging");
}
}
fprintf (f, "\\starttikzpicture[join=round");
if (global_env_is_set_p (env, GE_OPTS))
{
fprintf (f, ",");
emit_opts_raw (f, env->opts, global_env->output_language);
}
if (global_env_is_set_p (env, GE_BASELINE))
{
fprintf (f, ",");
fprintf (f, "baseline=%s", flt_str (buf1, env->baseline));
}
fprintf (f, "]\n");
if (global_env_is_set_p (env, GE_EXTENT))
{
flt_str (buf1, ext->min[X]);
flt_str (buf2, ext->min[Y]);
flt_str (buf3, ext->max[X]);
flt_str (buf4, ext->max[Y]);
fprintf (f,
"\\useasboundingbox(%s,%s) rectangle (%s,%s);\n"
"\\clip(%s,%s) rectangle (%s,%s);\n",
buf1, buf2, buf3, buf4, buf1, buf2, buf3, buf4);
}
}
typedef void (*EMIT_PREAMBLE_FUNC) (FILE * f, BOX_3D * ext, GLOBAL_ENV * env);
EMIT_PREAMBLE_FUNC emit_preamble_tbl[] = {
emit_preamble_pst_latex,
emit_preamble_tkz_latex,
emit_preamble_pst_context,
emit_preamble_tkz_context,
};
void
emit_postamble_pst_latex (FILE * f, GLOBAL_ENV * env)
{
fprintf (f, "\\end{pspicture%s}",
global_env_is_set_p (env, GE_EXTENT) ? "*" : "");
if (global_env_is_set_p (env, GE_FRAME))
fprintf (f, "}");
}
void
emit_postamble_tkz_latex (FILE * f, GLOBAL_ENV * env)
{
fprintf (f, "\\end{tikzpicture}");
if (global_env_is_set_p (env, GE_FRAME))
fprintf (f, "}}");
}
void
emit_postamble_pst_context (FILE * f, GLOBAL_ENV * env)
{
fprintf (f, "\\stoppspicture%s}",
global_env_is_set_p (env, GE_EXTENT) ? "*" : "");
if (global_env_is_set_p (env, GE_FRAME))
fprintf (f, "}");
}
void
emit_postamble_tkz_context (FILE * f, GLOBAL_ENV * env)
{
fprintf (f, "\\stoptikzpicture");
if (global_env_is_set_p (env, GE_FRAME))
fprintf (f, "}}");
}
typedef void (*EMIT_POSTAMBLE_FUNC) (FILE * f, GLOBAL_ENV * env);
EMIT_POSTAMBLE_FUNC emit_postamble_tbl[] = {
emit_postamble_pst_latex,
emit_postamble_tkz_latex,
emit_postamble_pst_context,
emit_postamble_tkz_context,
};
void
emit (FILE * f, OBJECT * obj, GLOBAL_ENV * env, char *doc_template_file_name)
{
BOX_3D ext[1];
int n_obj;
OBJECT *p;
char buf1[16], buf2[16], buf3[16], buf4[16];
char *escape, *doc_template;
doc_template =
doc_template_from_file (doc_template_file_name, env->output_language);
get_extent (obj, ext, &n_obj);
if (n_obj == 0)
err (no_line, "no objects to write");
else
{
remark (no_line, "scene bb=(%s,%s)(%s,%s)",
flt_str (buf1, ext->min[X]),
flt_str (buf2, ext->min[Y]),
flt_str (buf3, ext->max[X]), flt_str (buf4, ext->max[Y]));
if (get_transformed_global_env_extent (ext, env))
{
remark (no_line, "actual bb=(%s,%s)(%s,%s)",
flt_str (buf1, ext->min[X]),
flt_str (buf2, ext->min[Y]),
flt_str (buf3, ext->max[X]), flt_str (buf4, ext->max[Y]));
}
remark (no_line, "writing %d objects", n_obj);
fprintf (f,
"%% Sketch output, version " VER_STRING "\n"
"%% Output language: %s\n",
output_language_str[env->output_language]);
escape = NULL;
if (doc_template)
{
escape = strstr (doc_template, DOC_TEMPLATE_ESCAPE_STRING);
if (escape)
fprintf (f, "%.*s", escape - doc_template, doc_template);
else
warn (no_line,
"document template with no escape '%s' has been ignored",
DOC_TEMPLATE_ESCAPE_STRING);
}
(*emit_preamble_tbl[env->output_language]) (f, ext, env);
for (p = obj; p; p = p->sibling)
{
if (emit_tbl_tbl[global_env->output_language][p->tag] == NULL)
die (no_line, "emit: bad tag %d", p->tag);
if (xy_overlap_p (p, ext))
(*emit_tbl_tbl[global_env->output_language][p->tag]) (f, p);
}
(*emit_postamble_tbl[env->output_language]) (f, env);
if (escape)
{
escape += DOC_TEMPLATE_ESCAPE_STRING_LEN;
fprintf (f, "%s", escape);
if (strstr (escape, DOC_TEMPLATE_ESCAPE_STRING))
warn (no_line,
"more than one escape in document template; all but first ignored");
}
fprintf (f, "%% End sketch output\n");
}
}