diff options
Diffstat (limited to 'base/pack_ps.c')
-rw-r--r-- | base/pack_ps.c | 453 |
1 files changed, 453 insertions, 0 deletions
diff --git a/base/pack_ps.c b/base/pack_ps.c new file mode 100644 index 00000000..1bd8edc1 --- /dev/null +++ b/base/pack_ps.c @@ -0,0 +1,453 @@ +/* Copyright (C) 2001-2019 Artifex Software, Inc. + All Rights Reserved. + + This software is provided AS-IS with no warranty, either express or + implied. + + This software is distributed under license and may not be copied, + modified or distributed except as expressly authorized under the terms + of the license contained in the file LICENSE in this distribution. + + Refer to licensing information at http://www.artifex.com or contact + Artifex Software, Inc., 1305 Grant Avenue - Suite 200, Novato, + CA 94945, U.S.A., +1(415)492-9861, for further information. +*/ + +/* Pack a PostScript file into a C file containing an array of strings. */ + +/* The PostScript data is packed to remove excess whitespace and comments. + * Optionally, all header comments in the PostScript file can be copied + * over as comments in the C file. + * + * Usage: pack_ps [-o outfile] [-n name] [-c] ps_file + * + * options (must precede ps_file to be processed): + * + * -o outfile Output C file name. + * -n array_name Name of the array inside the generated C file. + * -c output PostScript header comments as C comments. + * + * ps_file Name of the PostScript file to be converted. + */ + +#define OUTFILE_NAME_DEFAULT "obj/packed_ps.c" +#define ARRAY_NAME_DEFAULT "packed_ps" + +#include "stdpre.h" +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> + +#ifdef DEBUG_AUX +#define STRIP_PDFR_DEBUG_CALLS 0 /* Disabled for debug builds. */ +#else +/* Set to 1 to strip PDFR_DEBUG calls for release builds. */ +#define STRIP_PDFR_DEBUG_CALLS 0 /* Disabled for release builds. */ +#endif + +#if STRIP_PDFR_DEBUG_CALLS +/* Start and end prefixes for PDFR_DEBUG blocks. */ +#define PDFR_DEBUG_START_PREFIX "//PDFR_DEBUG" +#define PDFR_DEBUG_END_COMMENT_1 "% //PDFR_DEBUG" +#define PDFR_DEBUG_END_COMMENT_2 "%//PDFR_DEBUG" +#endif + +/* Forward references */ +static bool readline(FILE * in, char *str, int len); +static int pack_postscript_line(const char *inputline, char *outputline, char *comment, bool nopack); +static void usage(const char *outfilename, const char *arrayname); +int main(int argc, char *argv[]); + +/* Read a line from the input. */ +static bool +readline(FILE * in, char *str, int len) +{ + /* + * Unfortunately, we can't use fgets here, because the typical + * implementation only recognizes the EOL convention of the current + * platform. + */ + int i = 0, c = getc(in); + + if (c < 0) { + return false; + } + while (i < len - 1) { + if (c < 0 || c == 10) { /* ^J, Unix EOL */ + break; + } + if (c == 13) { /* ^M, Mac EOL */ + c = getc(in); + if (c != 10 && c >= 0) { /* ^M^J, PC EOL */ + ungetc(c, in); + } + break; + } + str[i++] = c; + c = getc(in); + } + str[i] = 0; + return true; +} + +/* + * Strip extraneous whitespace and comments from a line of PostScript code. + * Return a pointer to any string that remains, or NULL if the line contains + * no code. + * + * Stores the packed string in outputline, and any comment section in comment, + * as NULL-terminated strings. If there is no comment, the comment string will + * be set to an empty string. + * + * Returns the length of outputline. If the line contains no PostScript data + * other than whitespace or comments, this length will be zero. + * + * Note: the caller is responsible for allocating storage for all three strings, + * and this routine will modify both the outputline and comment parameters. + * Currently no overflow checking is performed. In practice, both outputline + * and comment will be equal to or shorter than the input line, but in the + * worst case (a line of all double-quotes or all backslashes, neither of + * which is legal PostScript) the output line could theoretically consume + * double the space of the input line. A large fixed size for all three + * strings, such as 4096, should be sufficient for practical purposes. + * + * Note: Although this routine resembles the "doit" method in mkromfs.c, it + * performs additional escaping of backslash and double-quote characters, since + * its output is intended to be embedded in a C string. + */ +static int +pack_postscript_line(const char *inputline, char *outputline, char *comment, bool nopack) +{ + const char *str = inputline; + const char *from; + char *to; + int in_string = 0; + + /* Clear the output strings. */ + outputline[0] = '\0'; + comment[0] = '\0'; + + while (!nopack && (*str == ' ' || *str == '\t')) { /* strip leading whitespace */ + ++str; + } + if (*str == 0) { /* all whitespace */ + return 0; + } + if (!strncmp(str, "%END", 4)) { /* keep these for .skipeof */ + strcpy(outputline, str); + strcpy(comment, str); + return strlen(outputline); + } + + /* + * Copy the string over, removing: + * - All comments not within string literals; + * - Whitespace adjacent to '[' ']' '{' '}'; + * - Whitespace before '/' '(' '<'; + * - Whitespace after ')' '>'. + */ + for (from = str, to = outputline; (*to = *from) != 0; ++from, ++to) { + switch (*from) { + case '%': + if (!in_string && !nopack) { + /* Store the rest of the line in the comment. */ + while (*from && (/* (*from == '%') || */ (*from == ' ') || (*from == '\t'))) { + from++; + } + strcpy(comment, from); + break; + } + continue; + case ' ': + case '\t': + if (!nopack && to > outputline && !in_string && strchr(" \t>[]{})", to[-1])) { + --to; + } + continue; + case '(': + case '<': + case '/': + case '[': + case ']': + case '{': + case '}': + if (!nopack && to > outputline && !in_string && strchr(" \t", to[-1])) { + *--to = *from; + } + if (*from == '(') { + ++in_string; + } + continue; + case ')': + --in_string; + continue; + case '\"': + /* Because we're writing to a C string, every double-quote we output needs to be escaped with a preceding backslash. */ + *to = '\\'; + *++to = *from; + continue; + case '\\': + /* Because we're writing to a C string, every backslash we output needs to be escaped with a preceding backslash. */ + *++to = '\\'; + + if (from[1] == '\\') { + *++to = '\\'; /* A double-backslash turns into four backslashes inside a C quote. */ + *++to = *++from; + } + else if (from[1] == '(' || from[1] == ')') { + *++to = *++from; /* Output as "\\(" or "\\)" */ + } + continue; + default: + continue; + } + break; + } + + /* Strip trailing whitespace from outputline. */ + while (to > outputline && (to[-1] == ' ' || to[-1] == '\t')) { + --to; + } + *to = 0; + + /* Strip trailing whitespace from the comment. */ + if (strlen(comment) > 0) { + int comment_len = strlen(comment); + char *ptr = comment + comment_len - 1; + + while ((*ptr == ' ') || (*ptr == '\t')) { + *(ptr--) = '\0'; + } + + /* If all that's left is the percent sign, skip it unless it's the only thing on the line. */ + if ((strlen (comment) == 1) && (strlen(outputline) != 0)) { + comment [0] = '\0'; + } + } + + /* Return line length, so 0 = no data found. */ + return strlen(outputline); +} + +static void +usage(const char *outfilename, const char *arrayname) +{ + printf("\n"); + printf(" Usage: pack_ps [-o outfile] [-n name] [-c] ps_file\n"); + printf("\n"); + printf(" options (must precede ps_file to be processed):\n"); + printf(" -o outfile default: %s - output file name\n", outfilename); + printf(" -n array_name default: %s - name of the array inside the generated C file.\n", arrayname); + printf(" -c output PostScript header comments as C comments.\n"); +} + +int +main(int argc, char *argv[]) +{ + const char *outfilename = OUTFILE_NAME_DEFAULT; + const char *arrayname = ARRAY_NAME_DEFAULT; + const char *infilename = NULL; + bool output_comments = false; + bool no_pack = false; + FILE *infile; + FILE *outfile; + + int atarg = 1; + int total_input_length = 0; + int total_output_length = 0; + int total_code_lines = 0; + int total_comment_header_lines = 0; +#if STRIP_PDFR_DEBUG_CALLS + int pdfr_debug_start_count = 0; + int pdfr_debug_end_count = 0; + bool skip_this_line = false; +#endif + time_t buildtime = 0; + char *env_source_date_epoch; + +#define INPUT_LINE_LENGTH_MAX 4096 + char inputline[INPUT_LINE_LENGTH_MAX]; + + /* At least an input file name must be provided. */ + if (argc < 2) { + usage(outfilename, arrayname); + exit(1); + } + + /* Process arguments denoted with dashes. */ + while (atarg < argc) { + if (argv[atarg][0] != '-') { + /* End of optional arguments */ + break; + } + switch (argv[atarg][1]) { + case 'o' : /* output file name */ + /* Skip to next argument */ + if (++atarg >= argc) { + usage(outfilename, arrayname); + exit(0); + } + outfilename = argv[atarg++]; + break; + + case 'n' : /* C array name for this block of code */ + /* Skip to next argument */ + if (++atarg >= argc) { + usage(outfilename, arrayname); + exit(0); + } + arrayname = argv[atarg++]; + break; + + case 'c' : /* Enable comments in output C file */ + /* Skip to next argument */ + output_comments = true; + atarg++; + break; + case 'd' : /* Don't string whitespace or comments */ + /* Skip to next argument */ + no_pack = true; + atarg++; + break; + } + } + + /* The final argument is the file name to be processed. */ + if (atarg >= argc) { + usage(outfilename, arrayname); + exit(-1); + } + infilename = argv[atarg]; + + printf("%s:\n", argv[0]); + printf(" Input file: %s\n", infilename); + printf(" Output file: %s\n", outfilename); + printf(" Array name: %s\n", arrayname); + + infile = fopen(infilename, "r"); + if (infile == NULL) { + printf("Unable to open input file \"%s\"\n", infilename); + exit(-1); + } + outfile = fopen(outfilename, "w"); + if (outfile == NULL) { + fclose(infile); + printf("Unable to open output file \"%s\"\n", outfilename); + exit(-1); + } + + /* Output a header comment showing the source file and build time. */ + if ((env_source_date_epoch = getenv("SOURCE_DATE_EPOCH"))) { + buildtime = strtoul(env_source_date_epoch, NULL, 10); + } + if (!buildtime) { + buildtime = time(NULL); + } + fprintf(outfile,"/* Auto-generated from PostScript file \"%s\" at time %ld */\n", infilename, buildtime); + + while (readline(infile, inputline, INPUT_LINE_LENGTH_MAX)) { + + char packedline[INPUT_LINE_LENGTH_MAX]; + char comment[INPUT_LINE_LENGTH_MAX]; + int unpackedlen = strlen(inputline); + int packedlen = pack_postscript_line(inputline, packedline, comment, no_pack); + int commentlen = strlen(comment); + +#if STRIP_PDFR_DEBUG_CALLS + skip_this_line = false; + if (!strncmp(packedline, PDFR_DEBUG_START_PREFIX, strlen(PDFR_DEBUG_START_PREFIX))) { + /* Start of PDFR_DEBUG command found. */ + pdfr_debug_start_count++; + if (pdfr_debug_start_count != pdfr_debug_end_count+1) { + printf ("ERROR: missing PDFR_DEBUG terminating comment for call %d.\n", pdfr_debug_start_count); + fclose(infile); + fclose(outfile); + exit(-1); + } + } + + /* Skip the line if we're in a PDFR_DEBUG block. By checking here before we look for the + * trailing comment, we can suppress single-line as well as multi-line PDFR_DEBUG blocks. + */ + if (pdfr_debug_start_count != pdfr_debug_end_count) { + skip_this_line = true; + } + + if ((!strncmp(comment, PDFR_DEBUG_END_COMMENT_1, strlen(PDFR_DEBUG_END_COMMENT_1))) || + (!strncmp(comment, PDFR_DEBUG_END_COMMENT_2, strlen(PDFR_DEBUG_END_COMMENT_2)))) { + + /* End of PDFR_DEBUG command found. */ + pdfr_debug_end_count++; + if (pdfr_debug_start_count != pdfr_debug_end_count) { + printf ("ERROR: extra PDFR_DEBUG terminating comment for call %d.\n", pdfr_debug_start_count+1); + fclose(infile); + fclose(outfile); + exit(-1); + } + } + + if (skip_this_line) { + continue; + } +#endif + if (packedlen > 0) { + total_code_lines++; + } + + total_input_length += unpackedlen; + total_output_length += packedlen; + + /* Output any comments at the head of the file if requested. */ + if (output_comments) { + if ((total_code_lines == 0) && (commentlen > 0)) { + total_comment_header_lines++; + + if (total_comment_header_lines == 1) { + fprintf(outfile, "/* %s\n", comment); + } + else { + fprintf(outfile, " * %s\n", comment); + } + } + else if ((total_code_lines == 1) && (total_comment_header_lines > 0) && (packedlen > 0)) { + fprintf(outfile, " */\n"); + } + } + + if (packedlen > 0) { + if (total_code_lines == 1) { + fprintf(outfile,"const char *%s [] = {\n", arrayname); + } + /* Output the line with no comment. */ + fprintf(outfile, "\"%s\\n\",\n", packedline); + } + } + if (total_code_lines > 0) { + fprintf(outfile, "0x00\n"); + fprintf(outfile, "};\n"); + } + +#if STRIP_PDFR_DEBUG_CALLS + /* Make sure no PDFR_DEBUG calls were left unmatched. */ + if (pdfr_debug_start_count != pdfr_debug_end_count) { + printf ("ERROR: missing final PDFR_DEBUG terminating comment.\n"); + fclose(infile); + fclose(outfile); + exit(-1); + } +#endif + + /* Display processing statistics. */ + printf(" Processed %d lines of PostScript data.\n", total_code_lines); + printf(" %d bytes of PostScript data packed down to %d bytes.\n", total_input_length, total_output_length); +#if STRIP_PDFR_DEBUG_CALLS + printf(" %d PDFR_DEBUG calls removed from the code.\n", pdfr_debug_start_count); +#endif + + /* Close files and exit with success. */ + fclose(infile); + fclose(outfile); + + return 0; +} |