%% options copyright owner = Dirk Krause copyright year = 2012-2014 license = bsd # splint comment character = @ %% header #include "dk3fig.h" #include "dk3figrd.h" #include "dk3figpr.h" /** To draw a quadrant of a circle we do not use 1/3 of the derivative, we use another factor instead. */ #define FIG2LAT_CIRCLE_QUADRANT_BEZIER 0.5522847498307932 /** Conversion configuration. */ typedef struct { double nts; /**< Normal text size (scale factor or -1.0). */ int ntf; /**< Normal text font (0=similar, 1=fig). */ } fig2lat_config_t; /** How to handle a text. */ typedef struct { double fontSize; /**< Font size in pt. */ unsigned long fontNumber; /**< Unique font number for LaTeX output. */ int psFontNo; /**< PS font number. */ } f2l_text_handling_t; /** Image structure. */ typedef struct { dk3_ufi_t ufi; /**< Unique file identifier image. */ char const *filename; /**< Image file name. */ dk3_pdf_xobject_t *xo; /**< PDF X object. */ dk3_bif_coord_t width; /**< Image width. */ dk3_bif_coord_t height; /**< Image height. */ double xres; /**< X resolution. */ double yres; /**< Y resolution. */ unsigned long imageNumber; /**< Unique number of image. */ unsigned long lineno; /**< Line number of object. */ } f2l_image_t; /** Fig2lat job structure. */ typedef struct { /* @DRIVER@ You can add driver-specific configuration compomenents here. For alignment reasons the order of elements is in decreasing type size. Remember to re-compile all modules after changing data types. Initialize the new components in fig2lat_job_init(), release allocated resources in fig2lat_job_end(). */ dk3_graphics_state_t gs; /**< Graphics state. */ dk3_ct_2d_t ct2d; /**< Coordinates transformation data. */ dk3_fig_drawing_t *drw; /**< Fig drawing to convert. */ dk3_app_t *app; /**< Application structure. */ dk3_option_set_t *opt; /**< Options and cmd arguments. */ FILE *of1; /**< Output file 1. */ FILE *of2; /**< Output file 2. */ dkChar const *on1; /**< Output file name 1. */ dkChar const *on2; /**< Output file name 2. */ dkChar const * const *msg; /**< Localized text messages. */ dkChar const * const *figmsg; /**< Localized messages dk3fig mod. */ double width; /**< Width (typically in bp). */ double height; /**< Height (typically in bp). */ double w2; /**< Secondary width information. */ double h2; /**< Secondary height information. */ double nts; /**< Normal text size. */ double tts; /**< TeX text size. */ double arcspp; /**< Spline points per arc. */ double splspp; /**< Spline points per interval. */ double xsprec; /**< Iteration precision. */ double lwbp; /**< Line width in bp. */ long lwidth; /**< Width in bp. */ long lheight; /**< Height in bp. */ size_t xssbs; /**< X-spline segement Bezier segs. */ size_t minspp; /**< Minimum spline points. */ size_t qbs; /**< Quadrant Bezier segments. */ size_t codi; /**< Color digits. */ int ntf; /**< Text font (0=similar, 1=fig). */ int dr; /**< Driver, see @ref fig2latdrivers. */ int exval; /**< Exit, see @ref fig2latexitcodes. */ int mm; /**< Flag: Make mode. */ int cmd; /**< Command to execute. */ int cosp; /**< Flag: Spline compatibility. */ int coah; /**< Flag: Arrowhead compatibility. */ int xsah; /**< Flag: X-spline arrowhead. */ int debug; /**< Flag: Write debug information. */ int showpage; /**< Flag: Write showpage operator. */ int pslevel; /**< PS language level. */ int dsc; /**< Flag: Write PS DSC. */ int srctype; /**< Source file type. */ int smash; /**< Flag: Use smash instruction. */ int mbox; /**< Flag: Use mbox instruction. */ int resfont; /**< Flag: Use reset@font. */ int css; /**< Flag: CSS-styled SVG. */ int fragment; /**< Flag: Write SVG fragment only. */ int svgfontbase; /**< SVG font base: 0=none, 1=local, 2=web. */ int group; /**< Flag: Group SVG object with ahs. */ int miterlim; /**< Flag: Write miterlimit. */ int cols; /**< Flag: Compatible line styles. */ int otherfonts; /**< Flag: Use additional fonts SVG. */ int bbts; /**< Flag: Use text size for bb. */ int stu8; /**< Flag: Special text uses UTF-8. */ int cofop; /**< Flag: Compat fill open paths. */ int lwauto; /**< Flag: Automatic line width. */ } f2l_job_t; /** @defgroup fig2latexitcodes Exit codes for fig2lat. */ /**@{*/ /** Program succeeded. */ #define FIG2LAT_EXIT_SUCCESS 0 /** Error occured, no detailed description. */ #define FIG2LAT_EXIT_ERROR_UNKNOWN 1 /** Error, no file found to convert. */ #define FIG2LAT_EXIT_ERROR_NO_SUCH_FILE 2 /** File name is too long for processing. */ #define FIG2LAT_EXIT_FILENAME_TOO_LONG 3 /** Resources problem or system error. */ #define FIG2LAT_EXIT_ERROR_SYSTEM 4 /** Mathematical error. */ #define FIG2LAT_EXIT_ERROR_MATH 5 /** Syntax error in input file. */ #define FIG2LAT_EXIT_ERROR_SYNTAX 6 /**@}*/ /** @defgroup fig2latdrivers Fig2lat output drivers. */ /**@{*/ /** Produce PGF output file (default driver). */ #define FIG2LAT_DRIVER_PGF 0 /** Produce full *.tex file, image as embedded PGF. */ #define FIG2LAT_DRIVER_TEX_FULL_PGF 1 /** Produce full *.tex file, image as included external PDF. */ #define FIG2LAT_DRIVER_TEX_FULL_PDF 2 /** Produce *.tex file including an external EPS. */ #define FIG2LAT_DRIVER_EPS_WITH_TEX 3 /** Produce *.tex file including an external PDF. */ #define FIG2LAT_DRIVER_PDF_WITH_TEX 4 /** Produce standalone EPS file. */ #define FIG2LAT_DRIVER_EPS_STANDALONE 5 /** Produce standalone SVG file. */ #define FIG2LAT_DRIVER_SVG_STANDALONE 6 /* @DRIVER@ Add new driver numbers above this comment. The driver numbers must correspond to the order of driver names in f2lopt.ctr in the f2lopt_language_names array. */ /**@}*/ #include "f2lopt.h" #include "f2lto.h" %% module /** Test flag to show Beziervalues. */ #define FIG2LAT_TEST_BEZIER 0 #include "dk3all.h" #include "dk3bezcu.h" #include "fig2lat.h" #include "f2lud.h" #include "f2lsvg.h" #include "dk3figto.h" #include "dkt-version.h" $!trace-include /** Keywords used by the module, not localized. */ static dkChar const * const f2l_kw[] = { $!string-table macro=dkT # # 0: Application group name. # dkt-3 # # 1: String table file name. # fig2lat.str # # 2: Help file name. # fig2lat.txt # # 3: Source file name suffix. # .fig # # 4: Output suffix. # .pgf # # 5: Ouptut suffix. # .svg # # 6: Output suffix. # .eps # # 7: Output suffix. # .tex # # 8: Output suffix. # .pdf # # 9: File end for PDF file. # -i.pdf $!end }; /** Version number. */ static dkChar const *fig2lat_version[] = { dkT("fig2lat "), DKT_VERSION }; /** License conditions. */ static dkChar const * const fig2lat_license[] = { $!text macro=dkT Copyright (c) 2013, Dirk Krause All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the copyright holder(s) nor the names of contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. $!end }; /** Default help text, shown if no help file is found. */ static dkChar const * const fig2lat_help_text[] = { $!text macro=dkT,file=fig2lat.txt NAME fig2lat - Fig to LaTeX conversion SYNOPSIS fig2lat [-l ] [] [-o =]* DESCRIPTION The program converts *.fig drawings created using XFig or jFig to other graphics formats, mainly for use with the LaTeX typesetting system. OPTIONS -l chooses a driver, one from the following: Drivers to use a Fig file with latex or pdflatex: pdf.tex produces a *.tex/*.pdf file pair. Include the *.tex file in your LaTeX source, the *.tex file includes the *.pdf file. eps.tex produces a *.tex/*.eps file pair. Include the *.tex file in your LaTeX source, the *.tex file includes the *.eps file. pgf produces a *.pgf file for inclusion in a *.tex source. Drivers to produce a standalone image: tex.pdf produces a full *.tex file for use with pdflatex. A file file-i.pdf is produced additionally, this file is included by the *.tex file. tex produces a full *.tex file for use with pdflatex. The image is embedded as a PGF image. eps produces a standalone EPS file. svg produces a standalone SVG file. -m activates make-mode when running on a directory to convert files only if no destination files up to date are found. -o key=value sets key/value options, may be used multiple times. The following keys can be used: General options: lw= Change the line width base. The default for Fig files is 1/80 inch. Width can be one of the following: bp PS points pt Width in pt in Width in inch cm Width in cm mm Width in mm lighten Use 1/160 inch auto 0.4pt * textsize / 10 Text handling options tf= Text font selection for normal (non-special) text. Either ``similar'' or ``fig''. ts= Font size factor for normal (non-special) text. Either the keyword ``none'' or a scale factor (typically in the range 0.95 ... 1.0). bbts[=] Use text size in bounding box calculaion. smash[=] Use \smash instruction in LaTeX output. mbox[=] Use \mbos instruction in LaTeX output. reset@font Use \reset@font instruction in LaTeX output. ste[=] Encoding used when writing special text to output file. Either ``plain'' or ``utf-8''. X-Spline and Bezier-Spline options xsss[=] Number of Bezier-Spline subsegments per X-spline segment. Default: 8, minimum: 2. Arrowhead options: ahas= Number of X-spline segments for a 90 degree angle on arcs. Default: 4. ahss= Number of arrowhead X-spline segments for each original X-spline segment for arrowheads on splines. Default: 4. ahms= Minimum number of X-spline segments for each arrowhead edge. Default: 4 ahip= Arrowhead iteration precision. Default: 0.04. Output options: codi=[] Number of digits used for colors. Default: 3. Compatibilty options (compatibility to fig2dev): cosp[=] Compatible splines. coah[=] Compatible arrowheads. xsah[=] X-spline arrowheads (not together with coah). cols[=] Compatible line styles. cofop[=] Compatible fill for open paths. Source type (mutually exclusive): xfig File produced by XFig jfig File produced by jFig. winfig File produced by WinFIG. Driver specific options (tex, tex.pdf): dcts[=] Font size in pt used in the document class. Driver specific options (eps, eps.tex): showpage[=] Use showpage operator. level= PS level, 2 or 3. Driver specific options (svg) css[=] Use CSS styling. group[=] Group arrowhead objects with the object they are attached to. fragment[=] Write an SVG fragment instead of a complete file. fontbase[=] Specify source for GhostScript fonts converted to TTF, "none", "local", or "web". replacementfonts=[] enables/disables use of replacement font families. miterlimit[=] Enables or disables use of "stroke-miterlimit" style setting. RETURN VALUE The program returns exit status code 0 on success, any other value indicates an error. FILES Fig2lat uses the following files from the current working directory: f2lfonts.tex is used in the preamble of tex and tex.pdf output, the file contents replaces the font selection packages. f2lother.tex is used in the preamble of tex and tex.pdf output, the file contents replaces the packages for "other setup". NOTES There are differences between the output from fig2lat and the output from fig2dev. This is intended, otherwise we would not need an additional program. * Interpolated X-splines: Fig2lat uses the correct formula q=-0.5*s as described in [BS1995]. * Arrowheads: All arrowhead types introduced in xfig 3.2.5 are supported. On splines and arcs we have curved arrowheads. The same line width as for the original line is used for the arrowhead too, the arrowhead linewidth specified in the Fig file is ignored. * Text: When using LaTeX/pdfLaTeX related drivers (tex, tex.pdf, pdf.tex, eps.tex, pgf) all text is handled by LaTeX/pdfLaTeX. The tf setting controls whether to use PS fonts or similar feature LaTeX fonts for normal text. For special text no font selection instructions are written at all, so document default fonts are used. * Fill patterns: Fill patterns are vector graphics in fig2lat. * Splines: X-splines are converted to a sequence of Bezier splines instead of polylines. KNOWN ISSUES * The curved arrowheads on arcs and splines are implemented as sequences of short - sometimes very short - Bezier curves. At least for my arrowtest.fig test file I saw malformed arrowheads in some renderers although the same graphics were shown fine in other renderers. You can avoid the problem by using the coah option (compatible arrowheads). For Batik 1.7 Squiggle you can also attempt the miterlimit=no option. AUTHOR Dipl.-Ing. Dirk Krause HISTORY This program replaces the fig2vect program seen in previous versions of dktools. COPYRIGHT AND LICENSE Run fig2lat --license-terms to see the license conditions. SEE ALSO [BS1995] Carole Blanc, Christophe Schlick: X-Splines: A Spline Model Designed for the End-User Proceedings of the SIGGRAPH 1995 [fig2lat] http://dktools.sourceforge.net $!end }; /** Default texts, used if localized texts are not found. */ static dkChar const * const f2l_default_messages[] = { $!string-table macro=dkT,file=fig2lat.str # # 0 1: Invalid PS level preference value! # Invalid PS level preference value " "! # # 2 Input file name too long! # Input file name too long:\n # # 3 Failed to read input file! # Failed to read input file! # # 4 Failed to process input file! # Failed to process input file! # # 5 No regular file, no directory! # Not a regular file, not a directory! # # 6 Command line options processing failed! # Command line options processing failed! # # 7 + 8 Unknown driver! # Unknown driver " "! # # 9 + 10 Invalid tf value! # Invalid value " " for tf, use "fig" or "similar"! # # 11 + 12 Invalid font size! # Invalid font size specification " "! # # 13 + 14 Invalid size specification! # Invalid size specification " " (overflow)! # # 15 + 16 + 17 + 18 Value below minimum, maximum! # Value " " is below the required minimum " "! " is above the required maximum " # # 19 + 20 Option requires positive argument! # Option " " requires a positive argument! # # 21 + 22 Option requires a numeric argument! # Option " " requires a numeric argument! # # 23 Source type already set! # Source type already set! # # 24 + 25 Option requires an unsigned numeric argument! # Option " " requires an unsigned numeric argument! # # 26 Can not handle negative font size! # Can not handle negative font size! # # 27 + 28 Illegal option/key name! # Illegal option/key name " "! # # 29 + 30 Option too long! # Option too long:\n" " # # 31 Option argument too long! # Option argument too long:\n" # # 32 Option -l requires an argument! # Option -l requires an argument! # # 33 PS level must be 2 or 3! # PS level must be 2 or 3! # # 34 Minimum of arrowhead segments corrected to 2! # Minimum of arrowhead segments corrected to 2! # # 35 Mathematical problem (coordinates transformation)! # Mathematical problem (coordinates transformation)! # # 36 Mathematical problem (spline calculation)! # Mathematical problem (spline calculation)! # # 37 Arrowhead too long for spline! # Arrowhead too long for spline! # # 38 Mathematical problem (path construction)! # Mathematical problem (path construction)! # # 39 Arrowheads in summary too long for spline! # Arrowheads in summary too long for spline! # # 40 Failed to calculate graphics state transformation! # Failed to calculate graphics state transformation! # # 41 Failed to add instructions to PDF graphics! # Failed to add instructions to PDF graphics! # # 42 Skipping special text! # Skipping special text! # # 43 Unknown object type! # Unknown object type! # # 44 # Special text found! # # 45 46 # Not a line width: " "! $!end }; /** PS level preference name. */ static dkChar const fig2lat_pref_psl[] = { dkT("/print/ps/level") }; #if 0 /** Initialize a configuration structure. @param conf Structure to initialize. */ static void fig2lat_config_init(fig2lat_config_t *conf) { $? "+ fig2lat_config_init %s", TR_PTR(conf) dk3mem_res((void *)conf, sizeof(fig2lat_config_t)); conf->nts = 1.0; conf->ntf = 0; $? "- fig2lat_config_init" } #endif /** Set PS level from preferences. @param job Job structure. */ static void fig2lat_set_ps_level_from_preferences(f2l_job_t *job) { dkChar buf[256]; /* Buffer to retrieve preference value. */ int i; /* Conversion result. */ if(dk3app_get_pref(job->app,fig2lat_pref_psl,buf,DK3_SIZEOF(buf,dkChar))) { #if VERSION_BEFORE_20140716 if(dk3sf_sscanf3(buf, dkT("%d"), &i)) #else if (0 != dk3ma_i_from_string(&i, buf, NULL)) #endif { if((i >= 2) && (i <= 3)) { job->pslevel = i; } else { /* Warning: Preference value is not a number! */ dk3app_log_3(job->app, DK3_LL_WARNING, job->msg, 0, 1, buf); } } else { /* Warning: Preference value is not a number! */ dk3app_log_3(job->app, DK3_LL_WARNING, job->msg, 0, 1, buf); } } } /** Initialize job structure. @param job Job structure to initialize. @param app Application structure for diagnostics. @param msg Localized message texts. @param figmsg Localized message texts for the dk3fig module. @return 1 on success, 0 on error. */ static int fig2lat_job_init( f2l_job_t *job, dk3_app_t *app, dkChar const * const *msg, dkChar const * const *figmsg ) { int back = 0; $? "+ fig2lat_job_init %s", TR_PTR(job) dk3mem_res((void*)job, sizeof(f2l_job_t)); dk3fig_gs_init(&(job->gs)); job->app = app; job->msg = msg; job->figmsg = figmsg; job->opt = NULL; job->drw = NULL; job->on1 = NULL; job->on2 = NULL; job->of1 = NULL; job->of2 = NULL; job->nts = 1.0; job->tts = 12.0; #if 0 fig2lat_config_init(&(job->confProgram)); fig2lat_config_init(&(job->confFile)); fig2lat_config_init(&(job->confObject)); #endif job->dr = FIG2LAT_DRIVER_PDF_WITH_TEX; job->exval = FIG2LAT_EXIT_ERROR_UNKNOWN; job->mm = 0; job->cmd = 0; job->cosp = 0; job->coah = 0; job->xsah = 0; job->xssbs = 8; job->qbs = 4; job->debug = 0; job->showpage = 0; job->pslevel = 3; job->dsc = 0; job->arcspp = 8.0; job->splspp = 8.0; job->minspp = 4; job->xsprec = 0.04; /* originally 1200 / 25400 */ job->srctype = DK3_FIG_SRCTYPE_UNKNOWN; job->smash = 1; job->mbox = 0; job->resfont = 1; job->codi = 3; job->css = 1; job->fragment = 0; job->svgfontbase = 0; job->group = 0; job->miterlim = 1; job->cols = 0; job->otherfonts = 1; job->bbts = 0; job->stu8 = 0; job->cofop = 1; job->lwbp = 0.9; /* 1/80 inch */ job->lwauto = 0; if(dk3app_get_encoding(app) == DK3_ENCODING_UTF8) { job->stu8 = 1; } fig2lat_set_ps_level_from_preferences(job); (job->ct2d).mx = (job->ct2d).my = (job->ct2d).nx = (job->ct2d).ny = 0.0; job->width = job->height = job->w2 = job->h2 = 0.0; job->lwidth = job->lheight = 0L; /* @DRIVER@ If you added components to the f2l_job_t structure, initialize them to default values here. */ back = 1; $? "- fig2lat_job_init %d", back return back; } /** Clean up job structure. @param job Job structure to clean up. */ static void fig2lat_job_end(f2l_job_t *job) { $? "+ fig2lat_job_end %s", TR_PTR(job) /* @DRIVER@ If you added components to the f2l_job_t structure make sure to release resources here. */ if(job->opt) { dk3opt_close(job->opt); job->opt = NULL; } $? "- fig2lat_job_end" } /** Show version information. @param job Job structure. */ static void fig2lat_show_version(f2l_job_t *job) { dk3sf_initialize_stdout(); dk3sf_fputs(fig2lat_version[0], stdout); dk3sf_fputs(fig2lat_version[1], stdout); dk3sf_fputc(dkT('\n'), stdout); } /** Show license terms. @param job Job structure. */ static void fig2lat_show_license(f2l_job_t *job) { dkChar const * const *ptr; /* Pointer to traverse all text lines. */ ptr = fig2lat_license; dk3sf_initialize_stdout(); while(*ptr) { dk3sf_fputs(*(ptr++), stdout); dk3sf_fputc(dkT('\n'), stdout); } } /** Show help text. @param job Job structure. */ static void fig2lat_show_help(f2l_job_t *job) { dk3app_help(job->app, f2l_kw[2], fig2lat_help_text); } /** Check whether a file has a fig suffix. @param fn File name. @return 1 for fig files, 0 for other or no suffix. */ static int fig2lat_is_fig_file(dkChar const *fn) { dkChar const *sp; /* Suffix found in file name. */ int back = 0; sp = dk3str_get_suffix(fn); if(sp) { if(0 == dk3str_cmp(sp, f2l_kw[3])) { back = 1; } } return back; } /** Create output file name. @param job Job structure. @param ifn Input file name. @param osf Output suffix. @return Valid pointer to new output name on success, NULL on error. */ static dkChar const * fig2lat_set_one_output_name( f2l_job_t *job, dkChar const *ifn, dkChar const *osf ) { dkChar buf[DK3_MAX_PATH]; /* Buffer for modification. */ dkChar *sp; /* File name suffix in buffer. */ dkChar const *back = NULL; $? "+ fig2lat_set_one_output_name \"%s\" \"%s\"", TR_STR(ifn), TR_STR(osf) if(dk3str_len(ifn) < DK3_SIZEOF(buf,dkChar)) { dk3str_cpy_not_overlapped(buf, ifn); sp = dk3str_get_suffix(buf); if(sp) { *sp = dkT('\0'); } if((dk3str_len(buf) + dk3str_len(osf)) < DK3_SIZEOF(buf,dkChar)) { dk3str_cat(buf, osf); back = dk3str_dup_app(buf, job->app); if(!(back)) { job->exval = FIG2LAT_EXIT_ERROR_SYSTEM; $? ". exitcode" } } else { $? "! too long" /* ERROR: Input file name too long! */ job->exval = FIG2LAT_EXIT_FILENAME_TOO_LONG; $? ". exitcode" dk3app_log_2(job->app, DK3_LL_ERROR, job->msg, 2, buf); } } else { $? "! ifn too long" /* ERROR: Input file name too long! */ job->exval = FIG2LAT_EXIT_FILENAME_TOO_LONG; $? ". exitcode" dk3app_log_2(job->app, DK3_LL_ERROR, job->msg, 2, ifn); } $? "- fig2lat_set_one_output_name %s", TR_STR(back) return back; } /** Set output file name(s) depending on input name and output driver. @param job Job structure. @param ifn Input file name. @return 1 on success, 0 on error. */ static int fig2lat_set_output_names(f2l_job_t *job, dkChar const *ifn) { int back = 0; $? "+ fig2lat_set_output_names \"%s\"", TR_STR(ifn) switch(job->dr) { case FIG2LAT_DRIVER_TEX_FULL_PGF: { job->on1 = fig2lat_set_one_output_name(job, ifn, f2l_kw[7]); if(job->on1) { back = 1; } } break; case FIG2LAT_DRIVER_TEX_FULL_PDF: { job->on1 = fig2lat_set_one_output_name(job, ifn, f2l_kw[7]); job->on2 = fig2lat_set_one_output_name(job, ifn, f2l_kw[9]); if((job->on1) && (job->on2)) { back = 1; } } break; case FIG2LAT_DRIVER_EPS_WITH_TEX: { job->on1 = fig2lat_set_one_output_name(job, ifn, f2l_kw[7]); job->on2 = fig2lat_set_one_output_name(job, ifn, f2l_kw[6]); if((job->on1) && (job->on2)) { back = 1; } } break; case FIG2LAT_DRIVER_PDF_WITH_TEX: { job->on1 = fig2lat_set_one_output_name(job, ifn, f2l_kw[7]); job->on2 = fig2lat_set_one_output_name(job, ifn, f2l_kw[8]); if((job->on1) && (job->on2)) { back = 1; } } break; case FIG2LAT_DRIVER_EPS_STANDALONE: { job->on1 = fig2lat_set_one_output_name(job, ifn, f2l_kw[6]); if(job->on1) { back = 1; } } break; case FIG2LAT_DRIVER_SVG_STANDALONE: { job->on1 = fig2lat_set_one_output_name(job, ifn, f2l_kw[5]); if(job->on1) { back = 1; } } break; default: { /* PGF */ job->on1 = fig2lat_set_one_output_name(job, ifn, f2l_kw[4]); if(job->on1) { back = 1; } } break; } $? ". on1 = \"%s\"", TR_STR(job->on1) $? ". on2 = \"%s\"", TR_STR(job->on2) if(!(back)) { f2l_tool_set_exit_status(job, FIG2LAT_EXIT_ERROR_UNKNOWN); $? ". exitcode" } $? "- fig2lat_set_output_names %d \"%s\"", back, TR_STR(ifn) return back; } /** Run for one file. @param job Job structure. @param fn File name (no wildcards contained). */ static void fig2lat_run_for_file(f2l_job_t *job, dkChar const *fn) { dkChar const *oldsrcname; /* Saved source file name. */ #if DK3_HAVE_SETLOCALE && defined(LC_NUMERIC) && DK3_HAVE_LOCALE_H char *oldlocale; /* Old locale setting. */ #endif unsigned long oldsrcline; /* Saved source file line number. */ $? "+ fig2lat_run_for_file \"%s\"", TR_STR(fn) oldsrcname = dk3app_get_source_file(job->app); oldsrcline = dk3app_get_source_line(job->app); dk3app_set_source_file(job->app, fn); dk3app_set_source_line(job->app, 0UL); #if 0 /* Copy program configuration to file configuration. */ dk3mem_cpy( (void *)(&(job->confFile)), (void *)(&(job->confProgram)), sizeof(fig2lat_config_t) ); #endif job->on1 = NULL; job->on2 = NULL; if(fig2lat_set_output_names(job, fn)) { job->drw = dk3fig_drawing_new(job->app, job->figmsg); if(job->drw) { dk3fig_set_options_bsegs(job->drw, job->xssbs, job->qbs); dk3fig_set_options_lwbp(job->drw, job->lwbp); dk3fig_set_options_coah(job->drw, job->coah); dk3fig_set_options_xsah(job->drw, job->xsah); dk3fig_set_options_cosp(job->drw, job->cosp); dk3fig_set_options_bbts(job->drw, job->bbts); dk3fig_set_options_srctype(job->drw, job->srctype); dk3fig_set_options_arrows(job->drw,job->arcspp,job->splspp,job->minspp); dk3fig_set_options_iteration_precision(job->drw, job->xsprec); dk3fig_set_options_codi(job->drw, job->codi); dk3fig_set_options_cofop(job->drw, job->cofop); if(dk3fig_read_file_name(job->drw, fn)) { $? ". drawing read" if(dk3fig_prepare(job->drw)) { $? ". drawing prepared" #if DK3_HAVE_SETLOCALE && defined(LC_NUMERIC) && DK3_HAVE_LOCALE_H oldlocale = setlocale(LC_NUMERIC, "C"); #endif dk3fig_gs_init(&(job->gs)); switch(job->dr) { case FIG2LAT_DRIVER_SVG_STANDALONE: { f2lsvg_output(job); } break; /* @DRIVER@ Add a case branch to call your output driver here. If any error occurs in the driver function, set job->exval to indicate the error. */ default: { f2lud_output(job); } break; } #if DK3_HAVE_SETLOCALE && defined(LC_NUMERIC) && DK3_HAVE_LOCALE_H setlocale(LC_NUMERIC, oldlocale); #endif } else { $? "! failed to prepare drw" /* ERROR while preparing drawing! */ f2l_tool_set_exit_status(job, FIG2LAT_EXIT_ERROR_UNKNOWN); $? ". exitcode" dk3app_log_1(job->app, DK3_LL_ERROR, job->msg, 4); } } else { $? "! failed to read drawing" /* ERROR while reading input file! */ f2l_tool_set_exit_status(job, FIG2LAT_EXIT_ERROR_UNKNOWN); $? ". exitcode" dk3app_log_1(job->app, DK3_LL_ERROR, job->msg, 3); } dk3fig_drawing_delete(job->drw); job->drw = NULL; } else { f2l_tool_set_exit_status(job, FIG2LAT_EXIT_ERROR_SYSTEM); $? ". exitcode" } } dk3_release(job->on1); dk3_release(job->on2); dk3app_set_source_file(job->app, oldsrcname); dk3app_set_source_line(job->app, oldsrcline); $? "- fig2lat_run_for_file" } /** Check whether there is an up-to-date output file for the given file name and output suffix. @param job Job structure. @param fn Source file name. @param sn Suffix for output file name. @return 1 if output file is available, 0 otherwise. */ static int fig2lat_up_to_date_output(f2l_job_t *job, dkChar const *fn, dkChar const *sn) { dk3_stat_t stbs; /* Source file information. */ dk3_stat_t stbd; /* Destination file information. */ dkChar fnb[DK3_MAX_PATH]; /* File name buffer. */ dkChar *sp; /* File name suffix in buffer. */ int back = 0; $? "+ fig2lat_up_to_date_output \"%s\" \"%s\"", TR_STR(fn), TR_STR(sn) if(dk3str_len(fn) < DK3_SIZEOF(fnb,dkChar)) { dk3str_cpy_not_overlapped(fnb, fn); sp = dk3str_get_suffix(fnb); if(sp) { *sp = dkT('\0'); if((dk3str_len(fnb) + dk3str_len(sn)) < DK3_SIZEOF(fnb,dkChar)) { dk3str_cpy_not_overlapped(sp, sn); if(dk3sf_stat_app(&stbs, fn, job->app)) { if(dk3sf_stat_app(&stbd, fnb, NULL)) { if(stbd.mod > stbs.mod) { back = 1; $? ". output file ok" } } } } } } $? "- fig2lat_up_to_date_output %d", back return back; } /** Check whether we need to process a fig file found in a directory. @param job Job structure. @param fn File name to check. @return 1 to process the file, 0 to skip processing. */ static int fig2lat_must_process_file(f2l_job_t *job, dkChar const *fn) { int back = 1; $? "+ fig2lat_must_process_file \"%s\"", TR_STR(fn) if(job->mm) { switch(job->dr) { case FIG2LAT_DRIVER_TEX_FULL_PGF: { if(fig2lat_up_to_date_output(job, fn, f2l_kw[7])) { back = 0; } } break; case FIG2LAT_DRIVER_TEX_FULL_PDF: { if(fig2lat_up_to_date_output(job, fn, f2l_kw[7])) { if(fig2lat_up_to_date_output(job, fn, f2l_kw[9])) { back = 0; } } } break; case FIG2LAT_DRIVER_EPS_WITH_TEX: { if(fig2lat_up_to_date_output(job, fn, f2l_kw[6])) { if(fig2lat_up_to_date_output(job, fn, f2l_kw[7])) { back = 0; } } } break; case FIG2LAT_DRIVER_PDF_WITH_TEX: { if(fig2lat_up_to_date_output(job, fn, f2l_kw[7])) { if(fig2lat_up_to_date_output(job, fn, f2l_kw[8])) { back = 0; } } } break; case FIG2LAT_DRIVER_EPS_STANDALONE: { if(fig2lat_up_to_date_output(job, fn, f2l_kw[6])) { back = 0; } } break; case FIG2LAT_DRIVER_SVG_STANDALONE: { if(fig2lat_up_to_date_output(job, fn, f2l_kw[5])) { back = 0; } } break; default: { /* PGF */ if(fig2lat_up_to_date_output(job, fn, f2l_kw[4])) { back = 0; } } break; } } $? "- fig2lat_must_process_file %d", back return back; } /** Process a directory. @param job Job structure. @param fn Directory name. */ static void fig2lat_run_for_directory(f2l_job_t *job, dkChar const *fn) { dk3_dir_t *dir; /* Directory traversal. */ dkChar const *filename; /* Found file name. */ $? "+ fig2lat_run_for_directory \"%s\"", TR_STR(fn) dir = dk3dir_open_app(fn, job->app); if(dir) { while(dk3dir_get_next_file(dir)) { filename = dk3dir_get_fullname(dir); if(filename) { if(fig2lat_is_fig_file(filename)) { if(fig2lat_must_process_file(job, filename)) { fig2lat_run_for_file(job, filename); } } } else { $? "! bug" } } dk3dir_close(dir); } else { f2l_tool_set_exit_status(job, FIG2LAT_EXIT_ERROR_SYSTEM); $? ". exitcode" } $? "- fig2lat_run_for_directory" } /** Run for file or directory name directly (no wildcards). @param job Job structure. @param fn File name (wildcards already resolved). */ static void fig2lat_run_for_file_directly(f2l_job_t *job, dkChar const *fn) { dk3_stat_t stb; /* File information. */ $? "+ fig2lat_run_for_file_directly \"%s\"", TR_STR(fn) if(dk3sf_stat_app(&stb, fn, job->app)) { switch((stb.ft) & (~(DK3_FT_SYMLINK))) { case DK3_FT_REGULAR: { fig2lat_run_for_file(job, fn); } break; case DK3_FT_DIRECTORY: { fig2lat_run_for_directory(job, fn); } break; default: { /* ERROR: Neither directory nor regular file! */ job->exval = FIG2LAT_EXIT_ERROR_NO_SUCH_FILE; $? ". exitcode" dk3app_log_1(job->app, DK3_LL_ERROR, job->msg, 5); } break; } } else { $? "! stat failed" job->exval = FIG2LAT_EXIT_ERROR_NO_SUCH_FILE; $? ". exitcode" } $? "- fig2lat_run_for_file_directly" } /** Run for file or directory name (may contain wildcards). @param job Job structure. @param fn File name (may contain wildcards). */ static void fig2lat_run_for_name(f2l_job_t *job, dkChar const *fn) { dkChar fnb[DK3_MAX_PATH]; /* Name buffer for modification. */ dkChar const *filename; /* File name found. */ dk3_dir_t *dir; /* Expander for wildcards. */ int found; /* Flag: Source file found. */ $? "+ fig2lat_run_for_name \"%s\"", TR_STR(fn) if(dk3str_len(fn) < DK3_SIZEOF(fnb,dkChar)) { dk3str_cpy_not_overlapped(fnb, fn); dk3str_correct_filename(fnb); if(dk3sf_must_expand(fnb)) { dir = dk3dir_fne_open_app(fnb, job->app); if(dir) { found = 0; while(dk3dir_get_next_file(dir)) { filename = dk3dir_get_fullname(dir); if(filename) { if(fig2lat_is_fig_file(filename)) { $? ". fig file" found = 1; fig2lat_run_for_file(job, filename); } else { $? ". other file" } } else { $? "! bug, no filename" } } if(0 == found) { job->exval = FIG2LAT_EXIT_ERROR_NO_SUCH_FILE; $? ". exitcode" } dk3dir_close(dir); } else { $? "! fne_open" job->exval = FIG2LAT_EXIT_ERROR_SYSTEM; $? ". exitcode" } } else { fig2lat_run_for_file_directly(job, fnb); } } else { $? "! too long" /* ERROR: File name too long! */ job->exval = FIG2LAT_EXIT_FILENAME_TOO_LONG; $? ". exitcode" dk3app_log_2(job->app, DK3_LL_ERROR, job->msg, 2, fn); } $? "- fig2lat_run_for_name" } /** Run for current directory. @param job Job structure. */ static void fig2lat_run_for_current_directory(f2l_job_t *job) { dkChar cwdbu[DK3_MAX_PATH]; /* Buffer for current working directory. */ $? "+ fig2lat_run_for_current_directory" if(dk3sf_getcwd_app(cwdbu, DK3_SIZEOF(cwdbu,dkChar), job->app)) { fig2lat_run_for_file_directly(job, cwdbu); } else { $? "! getcwd" f2l_tool_set_exit_status(job, FIG2LAT_EXIT_ERROR_SYSTEM); $? ". exitcode" } $? "- fig2lat_run_for_current_directory" } /** Run conversion as indicated by command line arguments. @param job Job structure. */ static void fig2lat_run(f2l_job_t *job) { dkChar const *fn; /* File name. */ int argc; /* Number of command line arguments. */ int i; /* Current command line argument processed. */ $? "+ fig2lat_run" if((argc = dk3opt_get_num_args(job->opt)) > 0) { $? ". have arguments" job->exval = FIG2LAT_EXIT_SUCCESS; $? ". exitcode-success" for(i = 0; i < argc; i++) { fn = dk3opt_get_arg(job->opt, i); if(fn) { fig2lat_run_for_name(job, fn); } else { job->exval = FIG2LAT_EXIT_ERROR_NO_SUCH_FILE; $? ". exitcode" } } } else { $? ". no arguments" fig2lat_run_for_current_directory(job); } $? "- fig2lat_run" } #if TRACE_DEBUG #if FIG2LAT_TEST_BEZIER static void fig2lat_test_bezier(void) { double t; size_t i; t = 0.0; for(i = 0; i <= 10; i++) { printf("%lg\t%lg\n",t,dk3bezier_value(0.0,1.0,9.0,10.0,t,NULL)); t += 0.1; } } #endif #endif /** Entry point. @param argc Number of command line arguments. @param argv Command line arguments array. @return 0 on success, any other value indicates an error. */ DK3_MAIN { f2l_job_t job; /* Job structure. */ dk3_app_t *app = NULL; /* Application structure. */ dkChar const * const *msg = NULL; /* Localized texts. */ dkChar const * const *figmsg = NULL; /* dk3fig module localized texts. */ int exval; /* Exit status code. */ int testcmd; /* Mask for help/version/license. */ $!trace-init fig2lat.deb $? "+ main" exval = FIG2LAT_EXIT_ERROR_UNKNOWN; app = dk3app_open_command( argc, (dkChar const * const *)argv, f2l_kw[0] ); if(app) { $? ". app" msg = dk3app_messages( app, f2l_kw[1], (dkChar const **)f2l_default_messages ); figmsg = dk3fig_get_localized_messages(app); #if TRACE_DEBUG #if FIG2LAT_TEST_BEZIER fig2lat_test_bezier(); #endif #endif if(fig2lat_job_init(&job,app,msg,figmsg)) { $? ". init" if(f2lopt_process(&job)) { $? ". option processing" testcmd = DK3_APP_CMD_HELP | DK3_APP_CMD_VERSION | DK3_APP_CMD_LICENSE; if((job.cmd) & testcmd) { $? ". help version license" if(job.cmd) { $? ". show version information" fig2lat_show_version(&job); } if((job.cmd) & DK3_APP_CMD_LICENSE) { $? ". show license" fig2lat_show_license(&job); } if((job.cmd) & DK3_APP_CMD_HELP) { $? ". show help text" fig2lat_show_help(&job); } job.exval = FIG2LAT_EXIT_SUCCESS; } else { $? ". normal processing" fig2lat_run(&job); } } else { $? "! option processing" dk3app_log_1(job.app, DK3_LL_ERROR, job.msg, 6); } } else { $? "! init" } exval = job.exval; dk3app_close(app); app = NULL; } else { $? "! app" fputs("fig2lat: ERROR: Not enough memory (RAM/swap)!\n", stderr); fflush(stderr); exval = FIG2LAT_EXIT_ERROR_SYSTEM; } fig2lat_job_end(&job); $? "- main %d", exval $!trace-end fflush(stdout); exit(exval); return exval; }