Here's the current version of my token writer code for review. It's basically complete, but I'll need to read over it again and do some more testing before submitting a final version.
Anyone wanting to test it could use the new --token-writer flag (and possibly the --reader-flags and --writer-flags parameters) for utils/pdf-tokeniser to read in a file (with no streams) and write it out again. -- Michael
# Bazaar merge directive format 2 (Bazaar 0.90) # revision_id: [email protected] # target_branch: file:///home/michael/src/%2Blocal/gnupdf/trunk/ # testament_sha1: d98ca6749064824f21ca16ce1514bb1ee207ed02 # timestamp: 2009-10-25 14:07:18 -0400 # base_revision_id: jema...@termi-20091021125920-kkzb61bh82cu8gew # # Begin patch === modified file 'src/Makefile.am' --- src/Makefile.am 2009-06-27 23:19:51 +0000 +++ src/Makefile.am 2009-10-25 18:06:21 +0000 @@ -84,7 +84,8 @@ base/pdf-fp-func.h base/pdf-fp-func.c TOKEN_MODULE_SOURCES = base/pdf-token.c base/pdf-token.h \ - base/pdf-token-reader.c base/pdf-token-reader.h + base/pdf-token-reader.c base/pdf-token-reader.h \ + base/pdf-token-writer.c base/pdf-token-writer.h BASE_LAYER_SOURCES = base/pdf-base.c base/pdf-base.h \ $(ALLOC_MODULE_SOURCES) \ @@ -143,7 +144,8 @@ base/pdf-crypt.h \ base/pdf-fp-func.h \ base/pdf-token.h \ - base/pdf-token-reader.h + base/pdf-token-reader.h \ + base/pdf-token-writer.h pdf.h : $(PUBLIC_HDRS) chmod +x $(top_builddir)/src/extract-public-hdr === modified file 'src/base/pdf-stm.c' --- src/base/pdf-stm.c 2009-10-21 07:08:09 +0000 +++ src/base/pdf-stm.c 2009-10-25 18:06:21 +0000 @@ -1,4 +1,4 @@ -/* -*- mode: C -*- Time-stamp: "2009-10-21 04:48:36 mgold" +/* -*- mode: C -*- Time-stamp: "2009-10-22 08:14:33 mgold" * * File: pdf-stm.c * Date: Fri Jul 6 18:43:15 2007 @@ -206,7 +206,7 @@ pdf_status_t pdf_stm_write (pdf_stm_t stm, - pdf_char_t *buf, + const pdf_char_t *buf, pdf_size_t bytes, pdf_size_t *written_bytes) { === modified file 'src/base/pdf-stm.h' --- src/base/pdf-stm.h 2009-06-16 20:22:17 +0000 +++ src/base/pdf-stm.h 2009-10-25 18:06:21 +0000 @@ -107,7 +107,7 @@ pdf_size_t bytes, pdf_size_t *read_bytes); pdf_status_t pdf_stm_write (pdf_stm_t stm, - pdf_char_t *buf, + const pdf_char_t *buf, pdf_size_t bytes, pdf_size_t *written_bytes); pdf_status_t pdf_stm_read_char (pdf_stm_t stm, pdf_char_t *c); === added file 'src/base/pdf-token-writer.c' --- src/base/pdf-token-writer.c 1970-01-01 00:00:00 +0000 +++ src/base/pdf-token-writer.c 2009-10-25 18:06:21 +0000 @@ -0,0 +1,869 @@ +/* -*- mode: C -*- Time-stamp: "2009-10-25 06:56:00 mgold" + * + * File: pdf-token-writer.c + * Date: Wed Sep 23 04:37:51 2009 + * + * GNU PDF Library - Stream token writer + * + */ + +/* Copyright (C) 2009 Free Software Foundation, Inc. */ + +/* This program 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 of the License, or + * (at your option) any later version. + * + * This program 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 this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <config.h> + +#include <assert.h> +#include <stdlib.h> +#include <string.h> +#include <locale.h> +#include <inttypes.h> +#include <math.h> + +#include <pdf-token-writer.h> + +pdf_status_t +pdf_token_writer_new (pdf_stm_t stm, pdf_token_writer_t *writer) +{ + pdf_status_t err; + pdf_token_writer_t new_tokw; + + err = PDF_ENOMEM; + new_tokw = pdf_alloc (sizeof (*new_tokw)); + if (!new_tokw) + goto fail; + + /* determine the current locale's decimal point + * (avoid using localeconv since it may not be thread-safe) */ + new_tokw->decimal_point = NULL; + { + int len; + char decpt[16]; + + err = PDF_ERROR; + len = snprintf (decpt, sizeof (decpt), "%#.0f", 1.0); + if (len <= 0 || (pdf_size_t)len >= sizeof (decpt)) /* shouldn't happen */ + goto fail; + + err = PDF_ENOMEM; + new_tokw->decimal_point = pdf_alloc (len); + if (!new_tokw->decimal_point) + goto fail; + + /* this copies the trailing '\0' due to the starting offset */ + memcpy (new_tokw->decimal_point, &decpt[1], len); + } + + /* PDF32000 7.5.1: "lines that are not part of stream object data + * are limited to no more than 255 characters"... */ + new_tokw->max_line_length = 255; /* set to 0 to disable */ + + new_tokw->buffer = pdf_buffer_new (32768); + if (!new_tokw->buffer) + goto fail; + + new_tokw->stream = stm; + pdf_token_writer_reset (new_tokw); + + *writer = new_tokw; + return PDF_OK; + +fail: + if (new_tokw) + pdf_dealloc (new_tokw->decimal_point); + pdf_dealloc (new_tokw); + + return err; +} + +pdf_status_t +pdf_token_writer_reset (pdf_token_writer_t writer) +{ + writer->pos = 0; + writer->stage = 0; + writer->in_keyword = PDF_FALSE; + writer->line_length = 0; + return PDF_OK; +} + +pdf_status_t +pdf_token_writer_destroy (pdf_token_writer_t writer) +{ + if (!writer) return PDF_EBADDATA; + + assert (writer->buffer); + if (writer->buffer) + pdf_buffer_destroy (writer->buffer); + pdf_dealloc (writer->decimal_point); + pdf_dealloc (writer); + + return PDF_OK; +} + + +/***** Unbuffered output *****/ + +/* Write data to the stream. All output passes through this function. */ +static pdf_status_t +write_data (pdf_token_writer_t writer, const pdf_char_t *data, + pdf_size_t len, pdf_size_t *written) +{ + pdf_size_t i; + pdf_status_t rv; + + rv = pdf_stm_write (writer->stream, data, len, written); + if (rv != PDF_OK) + return rv; + + for (i = 0; i < *written; ++i) + { + pdf_char_t ch = data[i]; + ++writer->line_length; + if (pdf_is_eol_char (ch)) + writer->line_length = 0; + + writer->in_keyword = pdf_is_regular_char (ch); + } + return PDF_OK; +} + +/* Write a single character. */ +static INLINE pdf_status_t +write_char (pdf_token_writer_t writer, pdf_char_t ch) +{ + pdf_size_t written; + return write_data (writer, &ch, 1, &written); +} + +/* Write data starting at writer->pos, incrementing writer->pos as needed. */ +static INLINE pdf_status_t +write_data_using_pos (pdf_token_writer_t writer, + const pdf_char_t *data, pdf_size_t len) +{ + pdf_status_t rv; + pdf_size_t written; + if (writer->pos > len) + return PDF_EBADDATA; + + while (writer->pos < len) + { + rv = write_data (writer, data + writer->pos, len - writer->pos, + &written); + if (rv != PDF_OK) + return rv; + + writer->pos += written; + } + return PDF_OK; +} + + +/***** Buffered output, buffer management *****/ + +/* Write all buffered data to the stream. */ +static pdf_status_t +flush_buffer (pdf_token_writer_t writer) +{ + pdf_buffer_t buf = writer->buffer; + pdf_size_t len; + while ( (len = buf->wp - buf->rp) > 0 ) + { + pdf_size_t written; + pdf_status_t rv = write_data (writer, + buf->data + buf->rp, + len, &written); + if (rv != PDF_OK) + return rv; + + buf->rp += written; + } + return pdf_buffer_rewind (buf); +} + +/* Flush the buffer if there are less than 'len' bytes free. */ +static INLINE pdf_status_t +reserve_buffer_space (pdf_token_writer_t writer, pdf_size_t len) +{ + if (writer->buffer->wp + len > writer->buffer->size) + { + pdf_status_t rv = flush_buffer (writer); + if (rv != PDF_OK) + return rv; + + assert (len < writer->buffer->size); + assert (writer->buffer->wp == 0); + } + return PDF_OK; +} + +/* Write a character into the buffer; this assumes it will fit. */ +static INLINE void +write_buffered_char_nocheck (pdf_token_writer_t writer, pdf_char_t ch) +{ + writer->buffer->data[writer->buffer->wp++] = ch; + if (pdf_is_eol_char(ch)) + writer->buffered_line_length = 0; + else + ++writer->buffered_line_length; +} + +/* Write a character into the buffer. The buffer is flushed only if + * there's no room to write the character. */ +static INLINE pdf_status_t +write_buffered_char (pdf_token_writer_t writer, pdf_char_t ch) +{ + pdf_status_t rv = reserve_buffer_space (writer, 1); + if (rv == PDF_OK) + write_buffered_char_nocheck (writer, ch); + return rv; +} + + +/***** Misc. utility functions *****/ + +/* Takes a number from 0 to 15 and returns the ASCII code for the + * corresponding hexadecimal digit. */ +static INLINE pdf_char_t +hexchar (pdf_char_t ch) +{ + if (ch < 10) + return 48 + ch; /* '0'--'9' */ + else if (ch < 16) + return 65 + ch - 10; /* 'A'--'F' */ + return 255; +} + +static pdf_status_t +start_token (pdf_token_writer_t writer, pdf_bool_t need_wspace, + pdf_size_t len) +{ + pdf_bool_t add_wspace = (need_wspace && writer->in_keyword); + pdf_char_t wspace_char = 32; /*space*/ + + if (add_wspace) + ++len; + + if (writer->line_length + len > writer->max_line_length + && writer->max_line_length > 0) + { + add_wspace = PDF_TRUE; + wspace_char = 10; /* newline */ + } + + if (add_wspace) + { + pdf_status_t rv = write_char (writer, wspace_char); + if (rv != PDF_OK) return rv; + } + + return PDF_OK; +} + + +/***** Numeric tokens *****/ + +/* Encode snprintf output for PDF. 'len' is the return value of snprint. + * Re-encodes bytes 0 to 'len' of writer->buffer and resets buffer->rp/wp. */ +static pdf_bool_t +encode_buffer_number (pdf_token_writer_t writer, int len) +{ + pdf_buffer_t buf = writer->buffer; + if (len < 0 || len >= buf->size) + return PDF_FALSE; /* snprint failed, or truncated its output. */ + + buf->wp = buf->rp = 0; + while (buf->wp < len) + { + char ch = (char)buf->data[buf->rp]; + if (ch == '-') + { + ++buf->rp; + buf->data[buf->wp++] = 45; /* '-' */ + } + else if (ch >= '0' && ch <= '9') + { + ++buf->rp; + buf->data[buf->wp++] = 48 + (ch - '0'); + } + else + { + /* This should be a decimal point; check it. */ + pdf_size_t declen = strlen (writer->decimal_point); + int cmp = memcmp (buf->data + buf->rp, + writer->decimal_point, declen); + if (cmp != 0) + return PDF_FALSE; /* unexpected char */ + + buf->rp += declen; + buf->data[buf->wp++] = 46; /* '.' */ + } + } + buf->rp = 0; + return PDF_TRUE; /* success */ +} + +static INLINE pdf_status_t +write_integer_token (pdf_token_writer_t writer, pdf_token_t token) +{ + pdf_status_t rv; + switch (writer->stage) + { + case 0: + { + pdf_i32_t value = pdf_token_get_integer_value (token); + int len = snprintf ((char*)writer->buffer->data, + writer->buffer->size, "%"PRId32, value); + if (!encode_buffer_number (writer, len)) return PDF_ERROR; + } + ++writer->stage; /* fall through */ + case 1: + rv = start_token (writer, PDF_TRUE /*need_wspace*/, + writer->buffer->wp); + if (rv != PDF_OK) return rv; + ++writer->stage; /* fall through */ + case 2: + return flush_buffer (writer); + default: + return PDF_EBADDATA; + } +} + +static INLINE pdf_status_t +write_real_token (pdf_token_writer_t writer, pdf_token_t token) +{ + pdf_status_t rv; + switch (writer->stage) + { + case 0: + { + pdf_buffer_t buf = writer->buffer; + pdf_real_t value = pdf_token_get_real_value (token); + if (isnan(value) || isinf(value)) + return PDF_EBADDATA; + + /* The '#' flag forces snprintf to write a decimal point. */ + int len = snprintf ((char*)buf->data, + buf->size, "%#f", (double)value); + if (!encode_buffer_number (writer, len)) return PDF_ERROR; + + /* strip trailing zeroes */ + while (buf->wp && buf->data[buf->wp-1] == 48 /* '0' */) + --buf->wp; + } + ++writer->stage; /* fall through */ + case 1: + rv = start_token (writer, PDF_TRUE /*need_wspace*/, + writer->buffer->wp); + if (rv != PDF_OK) return rv; + ++writer->stage; /* fall through */ + case 2: + return flush_buffer (writer); + default: + return PDF_EBADDATA; + } +} + + +/***** String tokens *****/ + +static INLINE pdf_bool_t +should_escape_strchar (pdf_u32_t flags, pdf_char_t ch, + pdf_bool_t quote_parens) +{ + if (ch == 92 /* '\\' */ || ch == 13 /* CR */) + return PDF_TRUE; + if (ch == 40 /* '(' */ || ch == 41 /* ')' */) + return quote_parens; + + if (flags & PDF_TOKEN_READABLE_STRINGS) + { + if (ch == 127 || (ch < 32 && ch != 10)) + return PDF_TRUE; + } + + return PDF_FALSE; +} + +static INLINE int +str_escape_len (const pdf_char_t *data, pdf_size_t len, pdf_size_t pos) +{ + switch (data[pos]) + { + /* characters with two-character escape codes */ + case 8: + case 9: + case 10: + case 12: + case 13: + case 40: + case 41: + case 92: + return 2; + } + + if (data[pos] >= 0100) + return 4; /* escaped using a backslash and 3 octal characters */ + + if (pos+1 < len) + { + if (data[pos+1] >= 48 && data[pos+1] <= 57) /* '0'..'9' */ + return 4; /* need to write a 3-character octal number */ + } + + return (data[pos] >= 010) ? 3 : 2; +} + +static void +scan_string (pdf_token_writer_t writer, pdf_u32_t flags, + const pdf_char_t *data, pdf_size_t len, pdf_bool_t *use_hex) +{ + /* Match parentheses, and determine the portion of the string + * in which they should be quoted. */ + writer->paren_quoting_start = 0; + writer->paren_quoting_end = len; + pdf_size_t i, j; + for (i = 0; i < len; ++i) + { + if (data[i] == 40) /* '(' */ + { + for (j = writer->paren_quoting_end - 1; j > i; --j) + { + /* find a matching ')' */ + if (data[j] == 41) + { + writer->paren_quoting_end = j; + writer->paren_quoting_start = i + 1; + break; + } + } + } + } + + /* Determine the size of the escaped string. */ + pdf_size_t enc_bytes = 0; + for (i = 0; i < len; ++i) + { + pdf_bool_t quote_parens = (i >= writer->paren_quoting_start + && i < writer->paren_quoting_end); + if (should_escape_strchar (flags, data[i], quote_parens)) + enc_bytes += str_escape_len (data, len, i); + else + ++enc_bytes; + } + *use_hex = (enc_bytes > len*2); +} + +static INLINE pdf_status_t +write_string_char (pdf_token_writer_t writer, pdf_u32_t flags, + const pdf_char_t *data, pdf_char_t len, + pdf_size_t pos) +{ + assert (len > 0); + pdf_status_t rv; + const pdf_char_t *output = data + pos; + pdf_size_t outlen = 1; + pdf_char_t esc[4] = {92 /* '\\' */, 0, 0, 0}; + + pdf_char_t ch = data[pos]; + pdf_bool_t quote_parens = (pos >= writer->paren_quoting_start + && pos < writer->paren_quoting_end); + if (should_escape_strchar (flags, ch, quote_parens)) + { + /* escape the character */ + output = esc; + outlen = 2; + switch (ch) + { + case 8: esc[1] = 98; break; /* 'b' */ + case 9: esc[1] = 116; break; /* 't' */ + case 10: esc[1] = 110; break; /* 'n' */ + case 12: esc[1] = 102; break; /* 'f' */ + case 13: esc[1] = 114; break; /* 'r' */ + case 40: /* '('; fall through */ + case 41: /* ')'; fall through */ + case 92: /* '\\' */ + esc[1] = ch; + break; + default: /* use an octal escape */ + { + pdf_size_t digits; + pdf_char_t nextch = (pos+1 < len) ? data[pos+1] : 0; + if (nextch >= 48 && nextch <= 57) /* '0'..'9' */ + digits = 3; /* must use 3 octal characters */ + else if (ch > 0100) digits = 3; + else if (ch > 010) digits = 2; + else digits = 1; + + outlen = 1; + switch (digits) + { + /* fall through each case */ + case 3: esc[outlen++] = hexchar(ch / 0100); + case 2: esc[outlen++] = hexchar((ch % 0100) / 010); + case 1: esc[outlen++] = hexchar(ch % 010); + } + } + } + } + + /* If the line will be too long, split it (the length cannot be equal to + * the maximum, since this would leave no room for the backslash). */ + if (writer->max_line_length > 0 && !pdf_is_eol_char(output[0]) + && writer->buffered_line_length + outlen >= writer->max_line_length) + { + rv = reserve_buffer_space (writer, 2); + if (rv != PDF_OK) return rv; + write_buffered_char_nocheck (writer, 92); /* '\\' */ + write_buffered_char_nocheck (writer, 10); /* newline */ + assert (writer->buffered_line_length == 0); + } + + rv = reserve_buffer_space (writer, outlen); + if (rv == PDF_OK) + { + pdf_size_t i; + for (i = 0; i < outlen; ++i) + write_buffered_char_nocheck (writer, output[i]); + } + return rv; +} + +static INLINE pdf_status_t +write_string_token (pdf_token_writer_t writer, pdf_u32_t flags, + pdf_token_t token) +{ + pdf_status_t rv; + const pdf_char_t *data = pdf_token_get_string_data (token); + pdf_size_t size = pdf_token_get_string_size (token); + + switch (writer->stage) + { + case 0: + { + pdf_bool_t use_hex = (flags & PDF_TOKEN_HEX_STRINGS); + if (!use_hex) + scan_string (writer, flags, data, size, &use_hex); + + if (use_hex) + goto hexstring_start; + } + ++writer->stage; /* fall through */ + case 1: + { + /* Passing a correct length to start_token isn't important + * since we can split the string across multiple lines. */ + pdf_size_t dummy_len = PDF_MIN(20, 2 + size); + rv = start_token (writer, PDF_FALSE /*need_wspace*/, dummy_len); + if (rv != PDF_OK) return rv; + } + writer->buffered_line_length = writer->line_length; + write_buffered_char_nocheck (writer, 40 /* '(' */); + writer->pos = 0; + ++writer->stage; /* fall through */ + case 2: + while (writer->pos < size) + { + rv = write_string_char (writer, flags, data, size, writer->pos); + if (rv != PDF_OK) return rv; + + ++writer->pos; + } + ++writer->stage; /* fall through */ + case 3: + rv = write_buffered_char (writer, 41 /* ')' */); + if (rv != PDF_OK) return rv; + ++writer->stage; /* fall through */ + case 4: + return flush_buffer (writer); + + /*** hex strings ***/ +hexstring_start: + writer->stage = 101; + case 101: + { + pdf_size_t dummy_len = PDF_MIN(20, 2 + size*2); + rv = start_token (writer, PDF_FALSE /*need_wspace*/, dummy_len); + if (rv != PDF_OK) return rv; + } + writer->buffered_line_length = writer->line_length; + write_buffered_char_nocheck (writer, 60 /* '<' */); + writer->pos = 0; + ++writer->stage; /* fall through */ + case 102: + while (writer->pos < size) + { + /* If this line would be too long, start a new one. */ + if (writer->buffered_line_length + 2 > writer->max_line_length + && writer->max_line_length > 0) + { + rv = write_buffered_char (writer, 10); /* newline */ + if (rv != PDF_OK) return rv; + assert (writer->buffered_line_length == 0); + } + + pdf_char_t ch = data[writer->pos]; + rv = reserve_buffer_space (writer, 2); + if (rv != PDF_OK) return rv; + + write_buffered_char_nocheck (writer, hexchar(ch / 16)); + if (writer->pos == size-1 && (ch%16) == 0) + ; /* don't write a final 0 */ + else + write_buffered_char_nocheck (writer, hexchar(ch % 16)); + ++writer->pos; + } + ++writer->stage; /* fall through */ + case 103: + rv = write_buffered_char (writer, 62 /* '>' */); + if (rv != PDF_OK) return rv; + ++writer->stage; /* fall through */ + case 104: + return flush_buffer (writer); + default: + return PDF_EBADDATA; + } +} + + +/***** Other tokens *****/ + +static INLINE pdf_status_t +should_escape_namechar (pdf_u32_t flags, pdf_char_t ch, pdf_bool_t *escape) +{ + if (!ch) + return PDF_EBADDATA; + + *escape = !pdf_is_regular_char(ch); + if (flags & PDF_TOKEN_NO_NAME_ESCAPES) + { + if (*escape) + return PDF_EBADDATA; + } + else + { + *escape = *escape || ch == 35 /* '#' */ + || ch < 33 || ch >= 127; + } + return PDF_OK; +} + +static INLINE pdf_status_t +write_name_token (pdf_token_writer_t writer, pdf_u32_t flags, + pdf_token_t token) +{ + pdf_status_t rv; + pdf_size_t size = pdf_token_get_name_size (token); + const pdf_char_t *data = pdf_token_get_name_data (token); + switch (writer->stage) + { + case 0: + /* Validate the name; also calculate the encoded size + * and store it in ->pos temporarily. */ + writer->pos = 1 + size; + { + pdf_size_t i; + for (i = 0; i < size; ++i) + { + pdf_bool_t escape; + rv = should_escape_namechar(flags, data[i], &escape); + if (rv != PDF_OK) return rv; /* bad name */ + + if (escape) + writer->pos += 2; /* 2 hex characters */ + } + } + write_buffered_char_nocheck (writer, 47 /* '/' */); + ++writer->stage; /* fall through */ + case 1: + rv = start_token (writer, PDF_FALSE /*need_wspace*/, + writer->pos /* encoded token length */); + if (rv != PDF_OK) return rv; + + writer->pos = 0; + ++writer->stage; /* fall through */ + case 2: + while (writer->pos < size) + { + pdf_bool_t escape; + pdf_char_t ch = data[writer->pos]; + rv = should_escape_namechar(flags, ch, &escape); + if (rv != PDF_OK) return rv; /* bad name */ + + if (escape) + { + rv = reserve_buffer_space (writer, 3); + if (rv != PDF_OK) return rv; + + write_buffered_char_nocheck (writer, 35 /* '#' */); + write_buffered_char_nocheck (writer, hexchar(ch / 16)); + write_buffered_char_nocheck (writer, hexchar(ch % 16)); + } + else + { + rv = write_buffered_char (writer, ch); + if (rv != PDF_OK) return rv; + } + ++writer->pos; + } + ++writer->stage; /* fall through */ + case 3: + return flush_buffer (writer); + default: + return PDF_EBADDATA; + } +} + +static INLINE pdf_status_t +write_keyword_token (pdf_token_writer_t writer, pdf_token_t token) +{ + const pdf_char_t *data = pdf_token_get_keyword_data (token); + pdf_size_t size = pdf_token_get_keyword_size (token); + pdf_status_t rv; + switch (writer->stage) + { + case 0: + if (memchr (data, 0, size)) + return PDF_EBADDATA; /* data contains a null byte */ + ++writer->stage; /* fall through */ + case 1: + rv = start_token (writer, PDF_TRUE /*need_wspace*/, size); + if (rv != PDF_OK) return rv; + + writer->pos = 0; + ++writer->stage; /* fall through */ + case 2: + return write_data_using_pos (writer, + pdf_token_get_keyword_data (token), + size); + default: + return PDF_EBADDATA; + } +} + +static INLINE pdf_status_t +write_comment_token (pdf_token_writer_t writer, pdf_token_t token) +{ + const pdf_char_t *data = pdf_token_get_comment_data (token); + pdf_size_t size = pdf_token_get_comment_size (token); + pdf_status_t rv; + switch (writer->stage) + { + case 0: + { + /* A comment can't span multiple lines. */ + pdf_size_t i; + for (i = 0; i < size; ++i) + { + if (pdf_is_eol_char(data[i])) + return PDF_EBADDATA; + } + } + ++writer->stage; /* fall through */ + case 1: + rv = start_token (writer, PDF_FALSE /*need_wspace*/, size+1); + if (rv != PDF_OK) return rv; + ++writer->stage; /* fall through */ + case 2: + rv = write_char (writer, 37 /* '%' */); + if (rv != PDF_OK) return rv; + + writer->pos = 0; + ++writer->stage; /* fall through */ + case 3: + rv = write_data_using_pos (writer, data, size); + if (rv != PDF_OK) return rv; + ++writer->stage; /* fall through */ + case 4: + return write_char (writer, 10 /* '\n' */); + default: + return PDF_EBADDATA; + } +} + +static INLINE pdf_status_t +write_valueless_token (pdf_token_writer_t writer, + pdf_char_t ch, pdf_size_t len) +{ + pdf_char_t buf[2] = {ch,ch}; + pdf_status_t rv; + assert (len == 1 || len == 2); + + switch (writer->stage) + { + case 0: + rv = start_token (writer, PDF_FALSE /*need_wspace*/, len); + if (rv != PDF_OK) return rv; + + writer->pos = 0; + ++writer->stage; /* fall through */ + case 1: + return write_data_using_pos (writer, buf, len); + default: + return PDF_EBADDATA; + } +} + + +/***** Token dispatching *****/ + +static INLINE pdf_status_t +write_token_dispatch (pdf_token_writer_t writer, pdf_u32_t flags, + pdf_token_t token) +{ + switch (pdf_token_get_type (token)) + { + case PDF_TOKEN_INTEGER: + return write_integer_token (writer, token); + case PDF_TOKEN_REAL: + return write_real_token (writer, token); + case PDF_TOKEN_STRING: + return write_string_token (writer, flags, token); + case PDF_TOKEN_NAME: + return write_name_token (writer, flags, token); + case PDF_TOKEN_KEYWORD: + return write_keyword_token (writer, token); + case PDF_TOKEN_COMMENT: + return write_comment_token (writer, token); + case PDF_TOKEN_DICT_START: + return write_valueless_token (writer, 60 /* '<' */, 2); + case PDF_TOKEN_DICT_END: + return write_valueless_token (writer, 62 /* '>' */, 2); + case PDF_TOKEN_ARRAY_START: + return write_valueless_token (writer, 91 /* '[' */, 1); + case PDF_TOKEN_ARRAY_END: + return write_valueless_token (writer, 93 /* ']' */, 1); + case PDF_TOKEN_PROC_START: + return write_valueless_token (writer, 123 /* '{' */, 1); + case PDF_TOKEN_PROC_END: + return write_valueless_token (writer, 125 /* '}' */, 1); + default: + assert (0); + return PDF_ERROR; + } +} + +pdf_status_t +pdf_token_write (pdf_token_writer_t writer, pdf_u32_t flags, pdf_token_t token) +{ + pdf_status_t rv = write_token_dispatch (writer, flags, token); + if (rv == PDF_OK) + { + pdf_buffer_rewind (writer->buffer); + writer->stage = 0; + } + return rv; +} + +/* End of pdf-token-writer.c */ === added file 'src/base/pdf-token-writer.h' --- src/base/pdf-token-writer.h 1970-01-01 00:00:00 +0000 +++ src/base/pdf-token-writer.h 2009-10-25 18:06:21 +0000 @@ -0,0 +1,66 @@ +/* -*- mode: C -*- Time-stamp: "2009-10-25 02:43:11 mgold" + * + * File: pdf-token-writer.h + * Date: Wed Sep 23 04:30:26 2009 + * + * GNU PDF Library - Stream token writer + * + */ + +/* Copyright (C) 2009 Free Software Foundation, Inc. */ + +/* This program 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 of the License, or + * (at your option) any later version. + * + * This program 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 this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef PDF_TOKEN_WRITER_H +#define PDF_TOKEN_WRITER_H + +#include <config.h> + +#include <pdf-types.h> +#include <pdf-stm.h> +#include <pdf-token.h> +#include <pdf-token-reader.h> + +/* BEGIN PUBLIC */ +/* pdf-token-writer.h */ + +struct pdf_token_writer_s; /* opaque type */ +typedef struct pdf_token_writer_s *pdf_token_writer_t; + +pdf_status_t pdf_token_writer_new (pdf_stm_t stm, pdf_token_writer_t *writer); +pdf_status_t pdf_token_writer_destroy (pdf_token_writer_t writer); +pdf_status_t pdf_token_writer_reset (pdf_token_writer_t writer); +pdf_status_t pdf_token_write (pdf_token_writer_t writer, pdf_u32_t flags, + pdf_token_t token); + +/* END PUBLIC */ + +/* Internal state */ +struct pdf_token_writer_s { + pdf_stm_t stream; /* stream to read bytes from */ + char *decimal_point; + + pdf_bool_t in_keyword; + pdf_size_t line_length, buffered_line_length, max_line_length; + + int stage; + pdf_size_t pos; + pdf_size_t paren_quoting_start, paren_quoting_end; + pdf_buffer_t buffer; +}; + +#endif + +/* End of pdf-token-writer.h */ === modified file 'utils/pdf-tokeniser.c' --- utils/pdf-tokeniser.c 2009-10-21 12:59:20 +0000 +++ utils/pdf-tokeniser.c 2009-10-25 18:06:21 +0000 @@ -1,4 +1,4 @@ -/* -*- mode: C -*- Time-stamp: "09/10/21 14:57:10 jemarch" +/* -*- mode: C -*- Time-stamp: "2009-10-23 06:12:29 mgold" * * File: pdf-tokeniser.c * Date: Wed May 20 05:25:40 2009 @@ -43,6 +43,9 @@ {"help", no_argument, NULL, HELP_ARG}, {"usage", no_argument, NULL, USAGE_ARG}, {"version", no_argument, NULL, VERSION_ARG}, + {"token-writer", no_argument, NULL, TOKW_ARG}, + {"reader-flags", required_argument, NULL, READER_FLAGS_ARG}, + {"writer-flags", required_argument, NULL, WRITER_FLAGS_ARG}, {NULL, 0, NULL, 0} }; @@ -59,6 +62,9 @@ --help print a help message and exit\n\ --usage print a usage message and exit\n\ --version show pdf-tokeniser version and exit\n\ + --token-writer generate output using the token writer\n\ + --reader-flags=INTEGER specify token reader flags\n\ + --writer-flags=INTEGER specify token writer flags\n\ "; char *pdf_tokeniser_help_msg = ""; @@ -183,30 +189,62 @@ }; void -print_file (FILE *file) +print_file (FILE *file, pdf_bool_t use_tokw, + pdf_u32_t reader_flags, pdf_u32_t writer_flags) { pdf_status_t rv; pdf_token_reader_t reader = NULL; + pdf_token_writer_t writer = NULL; pdf_token_t token; - pdf_stm_t stm = NULL; + pdf_stm_t stm_in = NULL; + pdf_stm_t stm_out = NULL; - rv = pdf_stm_cfile_new (file, 0, 0 /*cache_size*/, PDF_STM_READ, &stm); + rv = pdf_stm_cfile_new (file, 0, 0 /*cache_size*/, PDF_STM_READ, &stm_in); if (rv != PDF_OK) { - fprintf(stderr, "failed to create stream\n"); + fprintf(stderr, "failed to create input stream\n"); goto out; } - rv = pdf_token_reader_new(stm, &reader); + rv = pdf_token_reader_new(stm_in, &reader); if (rv != PDF_OK) { fprintf(stderr, "failed to create reader\n"); goto out; } - while (( rv = pdf_token_read(reader, 0, &token) ) == PDF_OK) - { - print_tok(token); + if (use_tokw) + { + rv = pdf_stm_cfile_new (stdout, 0, 0 /*cache_size*/, + PDF_STM_WRITE, &stm_out); + if (rv != PDF_OK) + { + fprintf(stderr, "failed to create output stream\n"); + goto out; + } + + rv = pdf_token_writer_new(stm_out, &writer); + if (rv != PDF_OK) + { + fprintf(stderr, "failed to create writer\n"); + goto out; + } + } + + while (( rv = pdf_token_read(reader, reader_flags, &token) ) == PDF_OK) + { + if (use_tokw) + { + rv = pdf_token_write(writer, writer_flags, token); + if (rv != PDF_OK) + { + fprintf(stderr, "pdf_token_write error %d\n", rv); + goto out; + } + } + else + print_tok(token); + pdf_token_destroy(token); } @@ -218,14 +256,18 @@ fprintf(stderr, "done\n"); out: + if (writer) pdf_token_writer_destroy(writer); + if (stm_out) pdf_stm_destroy(stm_out); if (reader) pdf_token_reader_destroy(reader); - if (stm) pdf_stm_destroy(stm); + if (stm_in) pdf_stm_destroy(stm_in); } int main (int argc, char **argv) { char c; + pdf_bool_t use_tokw = PDF_FALSE; + pdf_u32_t reader_flags = 0, writer_flags = 0; /* set_program_name (argv[0]); */ @@ -256,6 +298,21 @@ exit (0); break; } + case TOKW_ARG: + { + use_tokw = 1; + break; + } + case READER_FLAGS_ARG: + { + reader_flags = atoi(optarg); + break; + } + case WRITER_FLAGS_ARG: + { + writer_flags = atoi(optarg); + break; + } default: { break; @@ -264,6 +321,6 @@ } setlocale(LC_ALL, ""); - print_file(stdin); + print_file(stdin, use_tokw, reader_flags, writer_flags); return 0; } === modified file 'utils/pdf-tokeniser.h' --- utils/pdf-tokeniser.h 2009-06-28 09:20:53 +0000 +++ utils/pdf-tokeniser.h 2009-10-25 18:06:21 +0000 @@ -1,4 +1,4 @@ -/* -*- mode: C -*- Time-stamp: "09/06/24 20:56:43 jemarch" +/* -*- mode: C -*- Time-stamp: "2009-10-23 02:09:23 mgold" * * File: pdf-tokeniser.h * Date: Wed Jun 24 20:54:49 2009 @@ -35,7 +35,10 @@ { HELP_ARG, USAGE_ARG, - VERSION_ARG + VERSION_ARG, + TOKW_ARG, + READER_FLAGS_ARG, + WRITER_FLAGS_ARG }; #endif /* pdf-tokeniser.h */ # Begin bundle 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
signature.asc
Description: Digital signature
