%% options copyright owner = Dirk Krause copyright year = 2018-xxxx SPDX-License-Identifier: BSD-3-Clause %% module #ifndef DK4CONF_H_INCLUDED #include "dk4conf.h" #endif #ifndef DK4TYPES_H_INCLUDED #include "dk4types.h" #endif #ifndef DK4MEM_H_INCLUDED #include "dk4mem.h" #endif #ifndef DK4ENC_H_INCLUDED #include "dk4enc.h" #endif #ifndef DK4STR8_H_INCLUDED #include "dk4str8.h" #endif #ifndef DK4STRD_H_INCLUDED #include "dk4strd.h" #endif #ifndef DK4MPL_H_INCLUDED #include "dk4mpl.h" #endif #ifndef DK4PPPT_H_INCLUDED #include "dk4pppt.h" #endif #ifndef DK4APP_H_INCLUDED #include "dk4app.h" #endif #ifndef DK4UC2L_H_INCLUDED #include "dk4uc2l.h" #endif #ifndef DK4UC2LA_H_INCLUDED #include "dk4uc2la.h" #endif #ifndef DK4GRCO_H_INCLUDED #include "dk4grco.h" #endif #ifndef DK4GRCOA_H_INCLUDED #include "dk4grcoa.h" #endif #ifndef DK4PATHD_H_INCLUDED #include "dk4pathd.h" #endif #ifndef DK4REC27_H_INCLUDED #include "dk4rec27.h" #endif #ifndef DK4MAI8DSZ_H_INCLUDED #include "dk4mai8dsz.h" #endif #ifndef DK4MAI8DBL_H_INCLUDED #include "dk4mai8dbl.h" #endif #ifndef DK4VERS_H_INCLUDED #include "dk4vers.h" #endif #ifndef DK4UNUSED_H_INCLUDED #include "dk4unused.h" #endif #ifndef DK4WMAIN_H_INCLUDED #include "dk4wmain.h" #endif $!trace-include /** Named color. */ typedef struct { char const *name; /**< Color name. */ double r; /**< Red. */ double g; /**< Green. */ double b; /**< Blue. */ } named_color_t; /** Choices for current state stored in st. */ enum { STATE_ERROR = -1, /**< Error state, do nothing. */ STATE_START , /**< Expecting first input line. */ STATE_DIM , /**< Expecting dimension line. */ STATE_CONTENTS , /**< Retrieving contents */ }; /** Input line buffer for processing stdin. 2019-10-24 NOTE: The size argument in fgets() is int type. So do not make the array size larger than INT_MAX. */ static char in_buf[4096]; /** Buffer to construct color specification. */ static char cs_buf[sizeof(in_buf)]; /** Buffer for graphics output file name. */ static dkChar fn_buf[DK4_MAX_PATH]; /** Graphics configuration. */ static dk4_gra_conf_t graconf; /** Predefined colors. Must be alphabetically sorted, bsearch() is used to find colors. */ static named_color_t const named_colors[] = { { "black", 0.0, 0.0, 0.0 }, { "blue", 0.0, 0.0, 1.0 }, { "brown", 0.75, 0.5, 0.25 }, { "cyan", 0.0, 1.0, 1.0 }, { "darkgray", 0.25, 0.25, 0.25 }, { "gray", 0.5, 0.5, 0.5 }, { "green", 0.0, 1.0, 0.0 }, { "lightgray", 0.75, 0.75, 0.75 }, { "lime", 0.75, 1.0, 0.0 }, { "magenta", 1.0, 0.0, 1.0 }, { "olive", 0.5 , 0.5, 0.0 }, { "orange", 1.0 , 0.5, 0.0 }, { "pink", 1.0 , 0.75, 0.75 }, { "purple", 0.75, 0.0, 0.25 }, { "red", 1.0, 0.0, 0.0 }, { "teal", 0.00, 0.5, 0.5 }, { "violet", 0.5 , 0.0, 0.5 }, { "white", 1.0, 1.0, 1.0 }, { "yellow", 1.0, 1.0, 0.0 } }; /** Option to check for the presence of the program. */ static dkChar const plpdftex_check_presence[] = { dkT("--check-presence") }; /** Protocol version to issue as reponse to --check-presence. */ static char const plpdftex_protocol[] = { "1\n" }; /** Keywords in dkChar to create silent application. */ static dkChar const * const kw_dk[] = { $!string-table macro=dkT # # 0 Application group name # dktools # # 1 Help file name # plpdftex.txt $!end }; /** Char keywords */ static char const * const kw_c8[] = { $!string-table # # 0 # ERROR: Not enough memory!\n # # 1 2 # ERROR: Failed to setup signal handlers!\n ERROR: Failed to restore previous signal handler!\n # # 3 4 # \\color{ } # # 5 # ERROR: Failed to open LaTeX encoding data!\n # # 6 # ERROR: Failed to write graphics output to file!\n # # 7 # ERROR: Illegal file name suffix for output file!\n # # 8 # ERROR: No file name suffix found in output file name!\n # # 9 # ERROR: Output file name too long!\n # # 10 # ERROR: Illegal characters in output file name!\n # # 11 # ERROR: Failed to recode output file name to system encoding!\n # # 12 # ERROR: Failed to open graphics output!\n # # 13 # ERROR: Syntax in line %lu - font size is not a positive number!\n # # 14 # ERROR: Syntax in line %lu - font size is not a number!\n # # 15 # ERROR: Syntax in line %lu - missing value!\n # # 16 # ERROR: Syntax in line %lu - too few values!\n # # 17 # ERROR: Syntax in line %lu - not a horizontal alignment!\n # # 18 # ERROR: Syntax in line %lu - not a vertical alignment!\n # # 19 # ERROR: Syntax in line %lu - not a number!\n # # 20 # ERROR: Syntax in line %lu - not a positive number!\n # # 21 # ERROR: Syntax in line %lu - color "%s" not found!\n # # 22 # ERROR: Syntax in line %lu - opacity value "%s" out of range 0...100!\n # # 23 # ERROR: Syntax in line %lu - illegal subcommand!\n # # 24 # ERROR: Syntax in line %lu - illegal command!\n # # 25 # ERROR: Syntax in line %lu - missing subcommand!\n $!end }; /** License text. */ static dkChar const * const lic_txt[] = { $!string-table macro=dkT Copyright (c) 2018, 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 }; /** Help text. */ static dkChar const * const help_txt[] = { $!string-table macro=dkT plpdftex - Plot to PDF+TeX file pair ------------------------------------ plpdftex --check-presence plpdftex The first form is used by other programs to check the presence of the program. Plpdftex prints the protocol version number. The second form is used to create a file pair. Other options are: --help to show this help text. --manual to show the full manual. --version to show the version number. $!end }; /** Allowed file name suffixes. */ static dkChar const * const fn_suffixes[] = { $!string-table macro=dkT .tex .pgf .pdftex .pdftexs .epstex $!end }; /** First commands in line. */ static const char * const cmd1[] = { $!string-table t p $!end }; /** Text related commands. */ static const char * const cmd2t[] = { $!string-table fs font-setup package preamble text text-box $!end }; /** Graphics related commands. */ static const char * const cmd2g[] = { $!string-table gsave grestore lw buttcap miterjoin nodash stroke-color fill-color rectangle circle moveto lineto curveto closepath stroke fill clip prepare-stroke prepare-fill $!end }; /** Names for horizontal alignments */ static char const * const h_align_names[] = { $!string-table left centered right $!end }; /** Names for vertical alignments. */ static char const * const v_align_names[] = { $!string-table top centered bottom base $!end }; /** Version number string. */ static dkChar const versno[] = { DKT_VERSION_DK }; /** Application structure for file search, especially for LaTeX encoding tables. */ static dk4_app_t *app = NULL; /** LaTeX encoding structure. */ static dk4_uc2l_t *uc2l = NULL; /** Graphics output structure. */ static dk4_pppt_t *pppt = NULL; /** Current line number. */ static unsigned long lineno = 0UL; /** Size of input buffer. */ static const size_t sz_in_buf = sizeof(in_buf); /** Current processing state. */ static int st = STATE_START; /** Flag: Can continue. */ static int cc = 1; /** Flag: All pppt output ok. */ static int ppptok = 0; /** Input encoding used on standard input. */ static int inenc = #if DK4_ON_WINDOWS DK4_ENCODING_WIN1252 #else DK4_ENCODING_PLAIN #endif ; /** Exit status code. */ static int exval = EXIT_FAILURE; #ifdef SIGPIPE /** Indicator: SIGPIPE signal received. */ static DK4_VOLATILE dk4_sig_atomic_t sig_had_pipe = 0; #endif /** Indicator: SIGINT signal received. */ static DK4_VOLATILE dk4_sig_atomic_t sig_had_int = 0; /** Indicator: SIGTERM signal received. */ static DK4_VOLATILE dk4_sig_atomic_t sig_had_term = 0; /** Pass a volatile pointer to an atomic integer. This function is necessary as some compilers mis-optimize direct access to volatile variables (at least if you believe one of the coding standards). @param ptr Address of atomic integer variable. @return The unmodified pointer. */ static DK4_VOLATILE dk4_sig_atomic_t * sig_pass_pointer(DK4_VOLATILE dk4_sig_atomic_t *ptr) { return ptr; } #ifdef SIGPIPE /** Handler for SIGPIPE signal. @param signo Signal number (always SIGPIPE, ignored). */ static void sig_handler_pipe(int DK4_ARG_UNUSED(signo) ) { DK4_UNUSED_ARG(signo) *sig_pass_pointer(&sig_had_pipe) = 1; } #endif /** Handler for SIGINT signal. @param signo Signal number (always SIGINT, ignored). */ static void sig_handler_int(int DK4_ARG_UNUSED(signo) ) { DK4_UNUSED_ARG(signo) *sig_pass_pointer(&sig_had_int) = 1; } /** Handler for SIGTERM signal. @param signo Signal number (always SIGTERM, ignored). */ static void sig_handler_term(int DK4_ARG_UNUSED(signo) ) { DK4_UNUSED_ARG(signo) *sig_pass_pointer(&sig_had_term) = 1; } /** Read value from volatile atomic type. This function is necessary as some compilers mis-optimize direct access to volatile variables (at least if you believe one of the coding standards). @param ap Pointer to volatile atomic variable. @return Contents of the variable. */ static dk4_sig_atomic_t sig_read_atomic(DK4_VOLATILE dk4_sig_atomic_t *ap) { return (*ap); } /** Check whether we can continue or if a signal was received. @param check_pipe Flag: Check for occured SIGPIPE signal too. @return 1 if the program can continue, 0 if a signal was received. */ static int sig_can_continue( #ifdef SIGPIPE int check_pipe #else int DK4_ARG_UNUSED(check_pipe) #endif ) { int back = 1; #ifndef SIGPIPE DK4_UNUSED_ARG(check_pipe) #else if (0 != check_pipe) { if (0 != sig_read_atomic(&sig_had_pipe)) { back = 0; } } #endif if (0 != sig_read_atomic(&sig_had_int )) { back = 0; } if (0 != sig_read_atomic(&sig_had_term)) { back = 0; } return back; } #if DK4_HAVE_BSEARCH /** Compare two color definition structures by name. @param l Left structure. @param r Right structure. @return Comparison result. */ static int compare_named_colors(const void *l, const void *r) { const named_color_t *pl; const named_color_t *pr; pl = (const named_color_t *)l; pr = (const named_color_t *)r; return strcmp(pl->name, pr->name); } #endif /** Find color definition for a given name. @param name Color name to search for. @return Pointer to color definition on success, NULL on error (not found). */ static const named_color_t * find_named_color(const char *name) { #if DK4_HAVE_BSEARCH /* Use bsearch() function. */ named_color_t testc; const named_color_t *back = NULL; $? "+ find_named_color (bsearch)" testc.name = name; back = bsearch( &testc, named_colors, DK4_SIZEOF(named_colors,named_color_t), sizeof(named_color_t), compare_named_colors ); $? "- find_named_color %d", TR_IPTR(back) return back; #else /* No bsearch() available, walk through all the colors. */ const named_color_t *back = NULL; const named_color_t *ptr = named_colors; size_t i; $? "+ find_named_color (full search)" for (i = 0; i < DK4_SIZEOF(named_colors,named_color_t); i++) { if (0 == strcmp(ptr->name, name)) { back = ptr; i = DK4_SIZEOF(named_colors,named_color_t); } else { ptr++; i++; } } $? "- find_named_color %d", TR_IPTR(back) return back; #endif } /** Process first input line containing the file name. */ static void process_file_name(void) { dk4_er_t er; /* Error report for recoding */ dkChar *psuff; /* File name suffix */ int res; /* Recoding result */ $? "+ process_file_name \"%!8s\"", in_buf dk4error_init(&er); res = dk4recode_c8_to_dk( fn_buf, DK4_SIZEOF(fn_buf,dkChar), dk4app_get_encoding(app), in_buf, inenc, &er ); if (0 != res) { psuff = dk4path_get_suffix(fn_buf, NULL); if (NULL != psuff) { res = dk4str_array_index(fn_suffixes, psuff, 0); switch (res) { case 0 : { $? ". tex" st = STATE_DIM; graconf.purpose = DK4_GRA_PURPOSE_DOCUMENT; dk4gra_conf_setup( &graconf, NULL, 1, DK4_GRA_CONF_SCOPE_FIG2LAT, app ); } break; case 1 : { $? ". pgf" st = STATE_DIM; dk4gra_conf_setup( &graconf, NULL, 1, DK4_GRA_CONF_SCOPE_FIG2LAT, app ); } break; case 2 : { $? ". pdftex" st = STATE_DIM; *psuff = dkT('\0'); dk4str_cat_s( fn_buf, DK4_SIZEOF(fn_buf,dkChar), fn_suffixes[0], NULL ); graconf.driver = DK4_GRA_DRIVER_PDF; dk4gra_conf_setup( &graconf, NULL, 1, DK4_GRA_CONF_SCOPE_FIG2LAT, app ); } break; case 3 : { $? ". pdftexs" st = STATE_DIM; *psuff = dkT('\0'); dk4str_cat_s( fn_buf, DK4_SIZEOF(fn_buf,dkChar), fn_suffixes[0], NULL ); graconf.driver = DK4_GRA_DRIVER_PDF; graconf.purpose = DK4_GRA_PURPOSE_DOCUMENT; dk4gra_conf_setup( &graconf, NULL, 1, DK4_GRA_CONF_SCOPE_FIG2LAT, app ); } break; case 4 : { $? ". epstex" st = STATE_DIM; *psuff = dkT('\0'); dk4str_cat_s( fn_buf, DK4_SIZEOF(fn_buf,dkChar), fn_suffixes[0], NULL ); graconf.driver = DK4_GRA_DRIVER_EPS; dk4gra_conf_setup( &graconf, NULL, 1, DK4_GRA_CONF_SCOPE_FIG2LAT, app ); } break; #if 0 /* 2018-04-17 Document not possible for EPS+TeX. */ case 5 : { /* epstexs */ st = STATE_DIM; dk4str_cpy_s( fn_buf, DK4_SIZEOF(fn_buf,dkChar), fn_suffixes[0], NULL ); graconf.driver = DK4_GRA_DRIVER_EPS; graconf.purpose = DK4_GRA_PURPOSE_DOCUMENT; dk4gra_conf_setup( &graconf, NULL, 1, DK4_GRA_CONF_SCOPE_FIG2LAT, app ); } break; #endif default : { $? "! unknown suffix" st = STATE_ERROR; /* ERROR: Illegal suffix */ fputs(kw_c8[7], stderr); } break; } } else { $? "! no suffix" st = STATE_ERROR; /* ERROR: No suffix found */ fputs(kw_c8[8], stderr); } } else { $? "! recode" st = STATE_ERROR; switch (er.ec) { case DK4_E_BUFFER_TOO_SMALL : { $? "! too long" /* ERROR: File name too long */ fputs(kw_c8[9], stderr); } break; case DK4_E_SYNTAX : { $? "! unconvertable chars" /* ERROR: Unconvertable characters */ fputs(kw_c8[10], stderr); } break; default : { $? "! other" /* ERROR: Failed to recode file name */ fputs(kw_c8[11], stderr); } break; } } $? "- process_file_name, st=%d", st } /** Process second input line containing the image dimensions. */ static void process_file_dimensions(void) { dk4_er_t er; char const *ep = NULL; char *p1 = NULL; char *p2 = NULL; size_t width = 0; size_t height = 0; int ok = 0; int dr; int isdoc; int flags; int res; $? "+ process_file_dimensions" p1 = dk4str8_start(in_buf, NULL); if (NULL != p1) { p2 = dk4str8_next(p1, NULL); if (NULL != p2) { res = dk4ma_input_c8_dec_size_t(&width, p1, &ep, 1, NULL); if (0 != res) { ep = NULL; res = dk4ma_input_c8_dec_size_t(&height, p2, &ep, 1, NULL); if (0 != res) { ok = 1; } #if TRACE_DEBUG else { $? "! not a number (2)" } #endif } #if TRACE_DEBUG else { $? "! not a number (1)" } #endif } #if TRACE_DEBUG else { $? "! no second text" } #endif } #if TRACE_DEBUG else { $? "! empty text" } #endif if (0 != ok) { ok = 0; flags = dk4gra_conf_flags_document(&graconf); flags |= DK4_GRA_PAGE_FLAG_NO_BG; dr = DK4_PPPT_DR_PGF; switch (graconf.driver) { case DK4_GRA_DRIVER_EPS : { dr = DK4_PPPT_DR_EPS_TEX; flags |= DK4_GRA_DOC_FLAG_EPS; } break; case DK4_GRA_DRIVER_PDF : { dr = DK4_PPPT_DR_PDF_TEX; } break; } if (DK4_GRA_PURPOSE_DOCUMENT == graconf.purpose) { isdoc = 1; } else { isdoc = 0; } dk4error_init(&er); pppt = dk4pppt_open( fn_buf, dr, isdoc, flags, width, height, &er ); if (NULL != pppt) { ok = 1; exval = EXIT_SUCCESS; $? ". exval success" ppptok = 1; st = STATE_CONTENTS; } else { $? "! pppt" st = STATE_ERROR; /* ERROR: Failed to open graphics output structure */ fputs(kw_c8[12], stderr); } } if (0 == ok) { $? "! errors" st = STATE_ERROR; } $? "- process_file_dimensions, st=%d ppptok=%d", st, ppptok } #if 0 /** Push fill and/or stroke color downwards. */ static void push_colors(void) { if (0 != ncf) { dk4pppt_prepare_fill(pppt, &ppptok, NULL); } if (0 != ncs) { dk4pppt_prepare_stroke(pppt, &ppptok, NULL); } ncf = ncs = 0; } #endif /** Set document font size. @param vptr Text containing the font size value. */ static void set_document_font_size(char *vptr) { const char *ep = NULL; double fs = -1.0; $? "+ set_document_font_size" if (DK4_GRA_PURPOSE_DOCUMENT == graconf.purpose) { if (NULL != vptr) { if (0 != dk4ma_input_c8_double(&fs, vptr, &ep, 1, NULL)) { if (0.0 < fs) { dk4pppt_doc_font_size(pppt, fs, &ppptok, NULL); } else { $? "! not positive" ppptok = 0; /* ERROR: Syntax */ fprintf(stderr, kw_c8[13], lineno); } } else { $? "! not a number" ppptok = 0; /* ERROR: Syntax */ fprintf(stderr, kw_c8[14], lineno); } } else { $? "! mising value" ppptok = 0; /* ERROR: Syntax */ fprintf(stderr, kw_c8[15], lineno); } } $? "- set_document_font_size st=%d ppptok=%d", st, ppptok } /** Add one font setup, package setup or general preamble line. @param vptr Setup text. @param lt Line type. */ static void set_preamble_line(char *vptr, int lt) { $? "+ set_preamble_line" if (DK4_GRA_PURPOSE_DOCUMENT == graconf.purpose) { if (NULL != vptr) { dk4pppt_doc_preamble_line(pppt, vptr, lt, &ppptok, NULL); } else { $? "! missing value" ppptok = 0; /* ERROR: Syntax */ fprintf(stderr, kw_c8[15], lineno); } } else { $? ". not in document output mode" /* Ignore for non-document output */ } $? "- set_preamble_line st=%d ppptok=%d", st, ppptok } /** Split one string into several pieces. @param parts Destination pointer array, points to the pieces found. @param nparts Array size (number of pointers). @param str String to split. @return Number of pieces found. */ static size_t split_text(char **parts, size_t nparts, char *str) { size_t back = 0; size_t i; $? "+ split_text %u \"%!8s\"", (unsigned)nparts, TR_8STR(str) if ((NULL != parts) && (NULL != str) && (0 < nparts)) { for (i = 0; i < nparts; i++) { parts[i] = NULL; } parts[0] = dk4str8_start(str, NULL); if (NULL != parts[0]) { back = 1; for (i = 1; i < nparts; i++) { parts[i] = dk4str8_next(parts[i-1], NULL); if (NULL != parts[i]) { back++; } else { i = nparts; } } } } $? "- split_text %u", (unsigned)back return back; } /** Split a text string in pieces and retrieve double numbers. @param pval Address of results array. @param parts Address of text piece pointers array. @param nparts Array size (number of elements in pval and nparts). @param str String to split and extract. @return Number of piece values found. */ static size_t split_numbers(double *pval, char **parts, size_t nparts, char *str) { char const *ep = NULL; size_t back = 0; size_t i; int res; $? "+ split_numbers %u \"%!8s\"", (unsigned)nparts, TR_8STR(str) if ((NULL != pval) && (NULL != parts) && (NULL != str) && (0 < nparts)) { for (i = 0; i < nparts; i++) { pval[i] = 0.0; parts[i] = NULL; } #if TRACE_DEBUG back = split_text(parts, nparts, str); if (back != nparts) { $? "! too few pieces %u < %u", (unsigned)back, (unsigned)nparts } nparts = back; back = 0; #else nparts = split_text(parts, nparts, str); #endif for (i = 0; i < nparts; i++) { ep = NULL; res = dk4ma_input_c8_double(&(pval[i]), parts[i], &ep, 1, NULL); if (0 != res) { back++; } else { $? "! not a number \"%!8s\"", parts[i] i = nparts; } } } #if TRACE_DEBUG for (i = 0; i < back; i++) { $? ". value[%u] = %lg", (unsigned)i, pval[i] } #endif $? "- split_numbers %u", (unsigned)back return back; } /** Construct color specification LaTeX instruction in cs_buf. @param cspec Contents of the color specification. @return 1 on success, 0 on error (buffer too small). */ static int construct_color_spec(char const *cspec) { size_t szcsb = sizeof(cs_buf); int back = 0; if (0 != dk4str8_cpy_s(cs_buf, szcsb, kw_c8[3], NULL)) { if (0 != dk4str8_cat_s(cs_buf, szcsb, cspec, NULL)) { if (0 != dk4str8_cat_s(cs_buf, szcsb, kw_c8[4], NULL)) { back = 1; } } } return back; } /** Typeset text. @param vptr Line containing text label description. @param box Flag: Use white box and oval box. */ static void typeset_text(char *vptr, int box) { char *parts[6]; char const *ep; size_t nparts; double xpos; double ypos; dk4_text_align_h_t ah = DK4_TEXT_ALIGN_H_CENTERED; dk4_text_align_v_t av = DK4_TEXT_ALIGN_V_CENTERED; int ok = 0; int res = 0; int textfl = 0; int hcs = 0; $? "+ typeset_text" if (0 != box) { textfl = (DK4_GRA_TEXT_FLAG_WHITE_BOX | DK4_GRA_TEXT_FLAG_OVAL_BOX); } if (NULL != vptr) { nparts = split_text(parts, 6, vptr); if (6 == nparts) { ep = NULL; hcs = construct_color_spec(parts[4]); res = dk4ma_input_c8_double(&xpos, parts[0], &ep, 1, NULL); if (0 != res) { ep = NULL; res = dk4ma_input_c8_double(&ypos, parts[1], &ep, 1, NULL); if (0 != res) { $? ". v align %s", parts[2] res = dk4str8_array_index(v_align_names, parts[2], 0); if (-1 < res) { switch (res) { case 3 : { av = DK4_TEXT_ALIGN_V_BASELINE; } break; case 2 : { av = DK4_TEXT_ALIGN_V_BOTTOM; } break; case 1 : { av = DK4_TEXT_ALIGN_V_CENTERED; } break; default : { av = DK4_TEXT_ALIGN_V_TOP; } break; } $? ". h alignment %s", parts[3] res = dk4str8_array_index(h_align_names, parts[3], 0); if (-1 < res) { switch (res) { case 2 : { ah = DK4_TEXT_ALIGN_H_RIGHT; } break; case 1 : { ah = DK4_TEXT_ALIGN_H_CENTERED; } break; default : { ah = DK4_TEXT_ALIGN_H_LEFT; } break; } ok = 1; dk4pppt_special_text( pppt, xpos, ypos, 0.0, parts[5], ((0 != hcs) ? (cs_buf) : (NULL)), ah, av, 0, 12.0, 0, textfl, &ppptok, NULL ); } else { $? "! h alignment" /* ERROR: Syntax (h alignment) */ fprintf(stderr, kw_c8[17], lineno); } } else { $? "! v alignment" /* ERROR: Syntax (v alignment) */ fprintf(stderr, kw_c8[18], lineno); } } else { $? "! ypos" /* ERROR: Syntax (not a number) */ fprintf(stderr, kw_c8[19], lineno); } } else { $? "! xpos" /* ERROR: Syntax (not a number) */ fprintf(stderr, kw_c8[19], lineno); } } else { $? "! too few values" /* ERROR: Syntax (too few values) */ fprintf(stderr, kw_c8[16], lineno); } } else { $? "! missing value" /* ERROR: Syntax (missing value) */ fprintf(stderr, kw_c8[15], lineno); } if (0 == ok) { ppptok = 0; st = STATE_ERROR; } $? "- typeset_text st=%d ppptok=%d", st, ppptok } /** Set line width. @param vptr Line containing text size in pt. */ static void set_line_width(char *vptr) { char const *ep = NULL; char *p1; double lw = 0.9; int ok = 0; int res; $? "+ set_line_width" if (NULL != vptr) { p1 = dk4str8_start(vptr, NULL); if (NULL != p1) { res = dk4ma_input_c8_double(&lw, p1, &ep, 1, NULL); if (0 != res) { if (0.0 < lw) { ok = 1; dk4pppt_set_line_width(pppt, lw, &ppptok, NULL); #if 0 dk4pppt_prepare_stroke(pppt, &ppptok, NULL); #endif } else { $? "! not positive" /* ERROR: Syntax */ fprintf(stderr, kw_c8[20], lineno); } } else { $? "! not a number" /* ERROR: Syntax */ fprintf(stderr, kw_c8[19], lineno); } } else { $? "! empty text" /* ERROR: Syntax */ fprintf(stderr, kw_c8[15], lineno); } } else { $? "! missing value" /* ERROR: Syntax */ fprintf(stderr, kw_c8[15], lineno); } if (0 == ok) { ppptok = 0; st = STATE_ERROR; } $? "- set_line_width st=%d ppptok=%d", st, ppptok } /** Find RGB values for color string. @param pnc Destination color value. @param str String containing color information. @return 1 on success, 0 on error. */ static int find_color_value(named_color_t *pnc, char *str) { named_color_t value; named_color_t bg; named_color_t const *pcol; char const *pe = NULL; char *pc = NULL; char *pn = NULL; char *p1 = NULL; double alpha = 0.0; int back = 0; int res = 0; $? "+ find_color_value \"%!8s\"", TR_8STR(str) pc = dk4str8_start(str, NULL); if (NULL != pc) { pn = dk4str8_chr(pc, '!'); if (NULL != pn) { *(pn++) = '\0'; pn = dk4str8_start(pn, NULL); } pcol = find_named_color(pc); if (NULL != pcol) { DK4_MEMCPY(&value,pcol,sizeof(named_color_t)); back = 1; pc = pn; while (NULL != pc) { pn = NULL; bg.r = bg.g = bg.b = 1.0; p1 = dk4str8_chr(pc, '!'); if (NULL != p1) { *(p1++) = '\0'; p1 = dk4str8_start(p1, NULL); if (NULL != p1) { pn = dk4str8_chr(p1, '!'); if (NULL != pn) { *(pn++) = '\0'; pn = dk4str8_start(pn, NULL); } pcol = find_named_color(p1); if (NULL != pcol) { bg.r = pcol->r; bg.g = pcol->g; bg.b = pcol->b; } else { /* ERROR: Syntax (color not found) */ fprintf(stderr, kw_c8[21], lineno, p1); back = 0; pn = NULL; } } } if (0 != back) { pe = NULL; res = dk4ma_input_c8_double(&alpha, pc, &pe, 1, NULL); if (0 != res) { if ((0.0 <= alpha) && (100.0 >= alpha)) { alpha = alpha / 100.0; value.r = value.r * alpha + bg.r * (1.0 - alpha); value.g = value.g * alpha + bg.g * (1.0 - alpha); value.b = value.b * alpha + bg.b * (1.0 - alpha); } else { /* ERROR: Syntax (out of range) */ fprintf(stderr, kw_c8[22], lineno, pc); back = 0; pn = NULL; } } else { /* ERROR: Syntax (not a number) */ fprintf(stderr, kw_c8[19], lineno); back = 0; pn = NULL; } } pc = pn; } if (0 != back) { pnc->r = value.r; pnc->g = value.g; pnc->b = value.b; } } else { $? "! color not found" /* ERROR: Syntax (color name not found) */ fprintf(stderr, kw_c8[21], lineno, pc); } } else { $? "! empty string" /* ERROR: Syntax (empty color string) */ fprintf(stderr, kw_c8[15], lineno); } $? "- find_color_value %d", back return back; } /** Set stroke color. @param vptr Line containing color description. */ static void set_stroke_color(char *vptr) { named_color_t value; int ok = 0; $? "+ set_stroke_color" if (NULL != vptr) { if (0 != find_color_value(&value, vptr)) { ok = 1; dk4pppt_set_stroke_rgb( pppt, value.r, value.g, value.b, &ppptok, NULL ); #if 0 dk4pppt_prepare_stroke(pppt, &ppptok, NULL); #endif } } else { $? "! missing value" /* ERROR: Syntax (missing value) */ fprintf(stderr, kw_c8[15], lineno); } if (0 == ok) { ppptok = 0; st = STATE_ERROR; } $? "- set_stroke_color st=%d ppptok=%d", st, ppptok } /** Set fill color (non-stroking color). @param vptr Line containing color information. */ static void set_fill_color(char *vptr) { named_color_t value; int ok = 0; $? "+ set_fill_color" if (NULL != vptr) { if (0 != find_color_value(&value, vptr)) { ok = 1; dk4pppt_set_fill_rgb( pppt, value.r, value.g, value.b, &ppptok, NULL ); #if 0 dk4pppt_prepare_fill(pppt, &ppptok, NULL); #endif } } else { $? "! value" /* ERROR: Syntax (missing value) */ fprintf(stderr, kw_c8[15], lineno); } if (0 == ok) { ppptok = 0; st = STATE_ERROR; } $? "- set_fill_color st=%d ppptok=%d", st, ppptok } /** Create rectangle path. @param vptr Line containing rectangle data. */ static void draw_rectangle(char *vptr) { double coord[4]; char *parts[4]; size_t nparts; int ok = 0; $? "+ draw_rectangle" if (NULL != vptr) { nparts = split_numbers(coord, parts, 4, vptr); if (4 == nparts) { ok = 1; dk4pppt_rectangle( pppt, coord[0], coord[2], coord[1], coord[3], -1.0, NULL, &ppptok, NULL ); } else { /* ERROR: Syntax (too few values) */ fprintf(stderr, kw_c8[16], lineno); } } else { /* ERROR: Syntax (missing value) */ fprintf(stderr, kw_c8[15], lineno); } if (0 == ok) { ppptok = 0; st = STATE_ERROR; } $? "- draw_rectangle st=%d ppptok=%d", st, ppptok } /** Create circle path. @param vptr Line containing circle data. */ static void draw_circle(char *vptr) { double coord[3]; char *parts[3]; size_t nparts; int ok = 0; $? "+ draw_circle" if (NULL != vptr) { nparts = split_numbers(coord, parts, 3, vptr); if (3 == nparts) { ok = 1; dk4pppt_circle( pppt, coord[0], coord[1], coord[2], NULL, &ppptok, NULL ); } else { /* ERROR: Syntax (too few values) */ fprintf(stderr, kw_c8[16], lineno); } } else { /* ERROR: Syntax (missing value) */ fprintf(stderr, kw_c8[15], lineno); } if (0 == ok) { ppptok = 0; st = STATE_ERROR; } $? "- draw_circle st=%d ppptok=%d", st, ppptok } /** Perform moveto operation. @param vptr Line containing coordinates. */ static void operation_moveto(char *vptr) { double coord[2]; char *parts[2]; size_t nparts; int ok = 0; $? "+ operation_moveto" if (NULL != vptr) { nparts = split_numbers(coord, parts, 2, vptr); if (2 == nparts) { ok = 1; dk4pppt_newpath_moveto( pppt, coord[0], coord[1], NULL, &ppptok, NULL ); } else { /* ERROR: Syntax (too few values) */ fprintf(stderr, kw_c8[16], lineno); } } else { /* ERROR: Syntax (missing value) */ fprintf(stderr, kw_c8[15], lineno); } if (0 == ok) { ppptok = 0; st = STATE_ERROR; } $? "- operation_moveto st=%d ppptok=%d", st, ppptok } /** Perform lineto operation. @param vptr Line containing coordinates. */ static void operation_lineto(char *vptr) { double coord[2]; char *parts[2]; size_t nparts; int ok = 0; $? "+ operation_lineto" if (NULL != vptr) { nparts = split_numbers(coord, parts, 2, vptr); if (2 == nparts) { ok = 1; dk4pppt_lineto( pppt, coord[0], coord[1], NULL, &ppptok, NULL ); } else { /* ERROR: Syntax (too few values) */ fprintf(stderr, kw_c8[16], lineno); } } else { /* ERROR: Syntax (missing value) */ fprintf(stderr, kw_c8[15], lineno); } if (0 == ok) { ppptok = 0; st = STATE_ERROR; } $? "- operation_lineto st=%d ppptok=%d", st, ppptok } /** Perform curveto operation. @param vptr Line containing coordinates. */ static void operation_curveto(char *vptr) { double coord[6]; char *parts[6]; size_t nparts; int ok = 0; $? "+ operation_curveto" if (NULL != vptr) { nparts = split_numbers(coord, parts, 6, vptr); if (6 == nparts) { ok = 1; dk4pppt_curveto( pppt, coord[0], coord[1], coord[2], coord[3], coord[4], coord[5], NULL, &ppptok, NULL ); } else { /* ERROR: Syntax (too few values) */ fprintf(stderr, kw_c8[16], lineno); } } else { /* ERROR: Syntax (missing value) */ fprintf(stderr, kw_c8[15], lineno); } if (0 == ok) { ppptok = 0; st = STATE_ERROR; } $? "- operation_curveto st=%d ppptok=%d", st, ppptok } /** Process one contents line. */ static void process_contents_line(void) { char *p1 = NULL; char *p2 = NULL; char *p3 = NULL; $? "+ process_contents_line" p1 = dk4str8_start(in_buf, NULL); if (NULL != p1) { p2 = dk4str8_next(p1, NULL); if (NULL != p2) { $? ". cmd = %s %s", p1, p2 p3 = dk4str8_next(p2, NULL); switch ( dk4str8_array_index(cmd1, p1, 0) ) { case 0 : { switch ( dk4str8_array_index(cmd2t, p2, 0) ) { case 0 : { /* fs */ set_document_font_size(p3); } break; case 1 : { /* font-setup */ set_preamble_line(p3, DK4_GRA_PREAMBLE_FONT); } break; case 2 : { /* package */ set_preamble_line(p3, DK4_GRA_PREAMBLE_PACKAGE); } break; case 3 : { /* preamble */ set_preamble_line(p3, DK4_GRA_PREAMBLE_OTHER); } break; case 4 : { /* text */ typeset_text(p3, 0); } break; case 5 : { /* text-box */ typeset_text(p3, 1); } break; default : { $? "! illegal subcommand" ppptok = 0; /* ERROR: Syntax */ fprintf(stderr, kw_c8[23], lineno); } break; } } break; case 1 : { switch ( dk4str8_array_index(cmd2g, p2, 0) ) { case 0 : { /* gsave */ dk4pppt_gsave(pppt, &ppptok, NULL); } break; case 1 : { /* grestore */ dk4pppt_grestore(pppt, &ppptok, NULL); } break; case 2 : { /* lw */ set_line_width(p3); } break; case 3 : { /* buttcap */ dk4pppt_set_line_cap( pppt, DK4_GRA_LC_BUTTED, &ppptok, NULL ); } break; case 4 : { /* miterjoin */ dk4pppt_set_line_join( pppt, DK4_GRA_LJ_MITERED, 20.0, &ppptok, NULL ); } break; case 5 : { /* nodash */ dk4pppt_set_line_style( pppt, DK4_GRA_LS_SOLID, 0.0, &ppptok, NULL ); } break; case 6 : { /* stroke-color */ set_stroke_color(p3); } break; case 7 : { /* fill-color */ set_fill_color(p3); } break; case 8 : { /* rectangle */ draw_rectangle(p3); } break; case 9 : { /* circle */ draw_circle(p3); } break; case 10 : { /* moveto */ operation_moveto(p3); } break; case 11 : { /* lineto */ operation_lineto(p3); } break; case 12 : { /* curveto */ operation_curveto(p3); } break; case 13 : { /* closepath */ dk4pppt_closepath(pppt, &ppptok, NULL); } break; case 14 : { /* stroke */ dk4pppt_stroke(pppt, &ppptok, NULL); } break; case 15 : { /* fill */ dk4pppt_fill(pppt, &ppptok, NULL); } break; case 16 : { /* clip */ dk4pppt_clip(pppt, &ppptok, NULL); } break; case 17 : { /* prepare-stroke */ dk4pppt_prepare_stroke(pppt, &ppptok, NULL); } break; case 18 : { /* prepare-fill */ dk4pppt_prepare_fill(pppt, &ppptok, NULL); } break; default : { $? "! illegal subcommand" ppptok = 0; /* ERROR: Syntax */ fprintf(stderr, kw_c8[23], lineno); } break; } } break; default : { $? "! illegal command!" ppptok = 0; /* ERROR: Syntax */ fprintf(stderr, kw_c8[24], lineno); } break; } } else { $? "! missing subcommand" ppptok = 0; /* ERROR: Syntax */ fprintf(stderr, kw_c8[25], lineno); } } else { $? ". empty line ok" /* Empty line is ok */ } $? "- process_contents_line st=%d ppptok=%d", st, ppptok } /** Process all input lines from standard input. */ static void process_input_from_stdin(void) { dk4_er_t er; $? "+ process_input_from_stdin" while (1 == cc) { cc = 0; if (0 != sig_can_continue(1)) { /* 2019-10-24 NOTE: MSVC complains about the conversion from size_t to int in the fgets() call below. Ths sz_in_buf value is constant (4096 at this time) and fits into an int. */ if (NULL != fgets(in_buf, sz_in_buf, stdin)) { cc = 1; lineno++; dk4str8_delnl(in_buf); $? ". INPUT LINE %s", in_buf switch (st) { case STATE_ERROR : { /* Do nothing */ } break; case STATE_START : { process_file_name(); } break; case STATE_DIM : { process_file_dimensions(); } break; default : { process_contents_line(); } break; } } } else { cc = -1; } } $? ". finished processsing input lines" /* Write output and clean up. */ if (NULL != pppt) { $? ". pppt" if ((0 != ppptok) && (STATE_ERROR != st)) { $? ". pppt, st ok" dk4error_init(&er); if (0 == dk4pppt_write_and_close(pppt, &er)) { exval = EXIT_FAILURE; $? "! failed, exval failure" /* ERROR: Failed to write graphics output */ fputs(kw_c8[6], stderr); } } else { $? "! pppt or state" dk4pppt_close(pppt); exval = EXIT_FAILURE; $? ". exval failure" } } else { $? "! pppt" exval = EXIT_FAILURE; $? ". exval failure" } $? "- process_input_from_stdin st=%d ppptok=%d exval=%d", st, ppptok, exval } #if DK4_HAVE_SIGACTION /** Set signal handlers and run. */ static void plpdftex_run_with_signal_handlers(void) { #ifdef SIGPIPE struct sigaction opipe; #endif struct sigaction oint; #ifdef SIGPIPE struct sigaction npipe; #endif struct sigaction nint; struct sigaction oterm; struct sigaction nterm; int success = 0; $? "+ plpdftex_run_with_signal_handlers (sigaction)" #ifdef SIGPIPE /* Set up signal handling for SIGPIPE. */ DK4_MEMRES(&npipe, sizeof(npipe)); npipe.sa_handler = sig_handler_pipe; npipe.sa_flags = 0; if (0 != sigemptyset(&npipe.sa_mask)) { /* ERROR: Failed to set up masked signal set for SIGPIPE */ fputs(kw_c8[1], stderr); goto finished; } if (0 != sigaddset(&npipe.sa_mask, SIGPIPE)) { /* ERROR: Failed to set up masked signal set for SIGPIPE */ fputs(kw_c8[1], stderr); goto finished; } if (0 != sigaction(SIGPIPE, &npipe, &opipe)) { /* ERROR: Failed to set up signal handler for SIGPIPE */ fputs(kw_c8[1], stderr); goto finished; } #endif /* Set up signal handling for SIGINT. */ DK4_MEMRES(&nint, sizeof(nint)); nint.sa_handler = sig_handler_int; nint.sa_flags = 0; if (0 != sigemptyset(&nint.sa_mask)) { /* ERROR: Failed to set up masked signal set for SIGINT */ fputs(kw_c8[1], stderr); goto restore_old_pipe; } if (0 != sigaddset(&nint.sa_mask, SIGINT)) { /* ERROR: Failed to set up masked signal set for SIGINT */ fputs(kw_c8[1], stderr); goto restore_old_pipe; } if (0 != sigaction(SIGINT, &nint, &oint)) { /* ERROR: Failed to set up signal handler for SIGINT */ fputs(kw_c8[1], stderr); goto restore_old_pipe; } /* Set up signal handling for SIGTERM */ DK4_MEMRES(&nterm, sizeof(nterm)); nterm.sa_handler = sig_handler_term; nterm.sa_flags = 0; if (0 != sigemptyset(&nterm.sa_mask)) { /* ERROR: Failed to set up masked signal set for SIGTERM */ fputs(kw_c8[1], stderr); goto restore_old_int; } if (0 != sigaddset(&nterm.sa_mask, SIGTERM)) { /* ERROR: Failed to set up masked signal set for SIGTERM */ fputs(kw_c8[1], stderr); goto restore_old_int; } if (0 != sigaction(SIGTERM, &nterm, &oterm)) { /* ERROR: Failed to set up signal handler for SIGTERM */ fputs(kw_c8[1], stderr); goto restore_old_int; } success = 1; process_input_from_stdin(); /* Restore signal handling for SIGTERM. */ if (0 != sigaction(SIGTERM, &oterm, NULL)) { /* ERROR: Failed to restore old SIGTERM settings */ fputs(kw_c8[2], stderr); success = 0; } /* Restore signal handling for SIGINT. */ restore_old_int: if (0 != sigaction(SIGINT, &oint, NULL)) { /* ERROR: Failed to restore old SIGPIPE settings */ fputs(kw_c8[2], stderr); success = 0; } #ifdef SIGPIPE /* Restore signal handling for SIGPIPE. */ restore_old_pipe: if (0 != sigaction(SIGPIPE, &opipe, NULL)) { /* ERROR: Failed to restore old SIGPIPE settings */ fputs(kw_c8[2], stderr); success = 0; } #endif /* Set exit status code if error occured. */ finished: if (0 == success) { exval = EXIT_FAILURE; $? ". exval failure" } $? "- plpdftex_run_with_signal_handlers" } #else #if DK4_HAVE_SIGSET /** Set signal handlers and run. */ static void plpdftex_run_with_signal_handlers(plpdftex_job_t *job) { #ifdef SIGPIPE dk4_sig_handler_t *oldpipe = NULL; #endif dk4_sig_handler_t *oldint = NULL; dk4_sig_handler_t *oldterm = NULL; $? "+ plpdftex_run_with_signal_handlers (sigset)" #ifdef SIGPIPE oldpipe = sigset(SIGPIPE, sig_handler_pipe); #endif oldint = sigset(SIGINT, sig_handler_int); oldterm = sigset(SIGTERM, sig_handler_term); process_input_from_stdin(); sigset(SIGTERM, oldterm); sigset(SIGINT, oldint); #ifdef SIGPIPE sigset(SIGPIPE, oldpipe); #endif $? "- plpdftex_run_with_signal_handlers" } #else #if DK4_HAVE_SIGNAL /** Set signal handlers and run. */ static void plpdftex_run_with_signal_handlers(void) { #ifdef SIGPIPE dk4_sig_handler_t *oldpipe = NULL; #endif dk4_sig_handler_t *oldint = NULL; dk4_sig_handler_t *oldterm = NULL; $? "+ plpdftex_run_with_signal_handlers (signal)" #ifdef SIGPIPE oldpipe = signal(SIGPIPE, sig_handler_pipe); #endif oldint = signal(SIGINT, sig_handler_int); oldterm = signal(SIGTERM, sig_handler_term); process_input_from_stdin(); signal(SIGTERM, oldterm); signal(SIGINT, oldint); #ifdef SIGPIPE signal(SIGPIPE, oldpipe); #endif $? "- plpdftex_run_with_signal_handlers" } #else /** Set signal handlers and run. */ static void plpdftex_run_with_signal_handlers(void) { process_input_from_stdin(); } #endif #endif #endif /** Main function. @param argc Number of command line arguments. @param argv Command line arguments array. @return 0 on success, all other values indicate errors. */ #if DK4_CHAR_SIZE > 1 int wmain(int argc, wchar_t *argv[]) #else int main(int argc, char *argv[]) #endif { $!trace-init plpdftex.deb $? "+ main" if ((1 < argc) && (0 == dk4str_cmp(argv[1], plpdftex_check_presence))) { exval = EXIT_SUCCESS; $? ". exval successs" fputs(plpdftex_protocol, stdout); } else { app = dk4app_open_silent( argc, argv, NULL, 0, kw_dk[0], versno, kw_dk[1], help_txt, lic_txt ); if (NULL != app) { if (0 != dk4app_can_run_normally(app)) { $? ". normal" dk4gra_conf_init(&graconf); graconf.driver = DK4_GRA_DRIVER_PGF; if (DK4_ENCODING_UTF8 == dk4app_get_stdin_encoding(app)) { inenc = DK4_ENCODING_UTF8; } uc2l = dk4uc2l_open_from_app(app); if (NULL != uc2l) { $? ". with uc2l" plpdftex_run_with_signal_handlers(); dk4uc2l_close(uc2l); } else { $? "! no uc2l" /* ERROR: Failed to create LaTeX encoding structure */ fputs(kw_c8[5], stderr); } } else { $? ". help version license" dk4app_help_version_license(app); exval = EXIT_SUCCESS; $? ". exval success" } dk4app_close(app); app = NULL; } else { $? "! no app" fputs(kw_c8[0], stderr); } } $? "- main %d", exval $!trace-end exit(exval); return exval; } /* vim: set ai sw=4 ts=4 : */