pdf_prim.c

changeset 59
e8821eb2fb08
child 60
ffb5b1e54eb2
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/pdf_prim.c	Thu Feb 20 12:16:00 2003 +0000
     1.3 @@ -0,0 +1,525 @@
     1.4 +#include <stdio.h>
     1.5 +#include <stdlib.h>
     1.6 +#include <string.h>
     1.7 +
     1.8 +#include "libpdf.h"
     1.9 +#include "libpdf_util.h"
    1.10 +#include "libpdf_prim.h"
    1.11 +#include "libpdf_private.h"
    1.12 +
    1.13 +
    1.14 +struct pdf_array_elem
    1.15 +{
    1.16 +  struct pdf_array_elem *next;
    1.17 +  struct pdf_obj        *val;
    1.18 +};
    1.19 +
    1.20 +
    1.21 +struct pdf_array
    1.22 +{
    1.23 +  struct pdf_array_elem *first;
    1.24 +  struct pdf_array_elem *last;
    1.25 +};
    1.26 +
    1.27 +
    1.28 +struct pdf_dict_entry
    1.29 +{
    1.30 +  struct pdf_dict_entry *next;
    1.31 +  char                  *key;
    1.32 +  struct pdf_obj        *val;
    1.33 +};
    1.34 +
    1.35 +
    1.36 +struct pdf_dict
    1.37 +{
    1.38 +  struct pdf_dict_entry *first;
    1.39 +};
    1.40 +
    1.41 +
    1.42 +struct pdf_stream
    1.43 +{
    1.44 +  struct pdf_obj *stream_dict;
    1.45 +  struct pdf_obj *length;
    1.46 +  pdf_stream_write_callback callback;
    1.47 +  void *app_data;  /* arg to pass to callback */
    1.48 +  struct pdf_obj *filters;  /* name or array of names */
    1.49 +  struct pdf_obj *decode_parms;
    1.50 +};
    1.51 +
    1.52 +
    1.53 +struct pdf_obj
    1.54 +{
    1.55 +  /* these fields only apply to indirectly referenced objects */
    1.56 +  struct pdf_obj      *prev;
    1.57 +  struct pdf_obj      *next;
    1.58 +  unsigned long       obj_num;
    1.59 +  unsigned long       obj_gen;
    1.60 +  long int            file_offset;
    1.61 +
    1.62 +  /* these fields apply to all objects */
    1.63 +  unsigned long       ref_count;
    1.64 +  pdf_obj_type        type;
    1.65 +  union {
    1.66 +    int               bool;
    1.67 +    char              *name;
    1.68 +    char              *string;
    1.69 +    unsigned long     integer;
    1.70 +    double            real;
    1.71 +    struct pdf_obj    *ind_ref;
    1.72 +    struct pdf_dict   dict;
    1.73 +    struct pdf_array  array;
    1.74 +    struct pdf_stream stream;
    1.75 +  } val;
    1.76 +};
    1.77 +
    1.78 +
    1.79 +struct pdf_obj *ref (struct pdf_obj *obj)
    1.80 +{
    1.81 +  obj->ref_count++;
    1.82 +  return (obj);
    1.83 +}
    1.84 +
    1.85 +
    1.86 +void unref (struct pdf_obj *obj)
    1.87 +{
    1.88 +  if ((--obj->ref_count) == 0)
    1.89 +    {
    1.90 +      /* $$$ free the object */
    1.91 +    }
    1.92 +}
    1.93 +
    1.94 +
    1.95 +struct pdf_obj *pdf_deref_ind_obj (struct pdf_obj *ind_obj)
    1.96 +{
    1.97 +  pdf_assert (ind_obj->type == PT_IND_REF);
    1.98 +  return (ind_obj->val.ind_ref);
    1.99 +}
   1.100 +
   1.101 +
   1.102 +void pdf_set_dict_entry (struct pdf_obj *dict_obj, char *key, struct pdf_obj *val)
   1.103 +{
   1.104 +  struct pdf_dict_entry *entry;
   1.105 +
   1.106 +  if (dict_obj->type == PT_IND_REF)
   1.107 +    dict_obj = pdf_deref_ind_obj (dict_obj);
   1.108 +
   1.109 +  pdf_assert (dict_obj->type == PT_DICTIONARY);
   1.110 +
   1.111 +  /* replacing existing entry? */
   1.112 +  for (entry = dict_obj->val.dict.first; entry; entry = entry->next)
   1.113 +    if (strcmp (entry->key, key) == 0)
   1.114 +      {
   1.115 +	unref (entry->val);
   1.116 +	entry->val = ref (val);
   1.117 +	return;
   1.118 +      }
   1.119 +
   1.120 +  /* new entry */
   1.121 +  entry = pdf_calloc (sizeof (struct pdf_dict_entry));
   1.122 +
   1.123 +  entry->next = dict_obj->val.dict.first;
   1.124 +  dict_obj->val.dict.first = entry;
   1.125 +
   1.126 +  entry->key = pdf_strdup (key);
   1.127 +  entry->val = ref (val);
   1.128 +}
   1.129 +
   1.130 +
   1.131 +struct pdf_obj *pdf_get_dict_entry (struct pdf_obj *dict_obj, char *key)
   1.132 +{
   1.133 +  struct pdf_dict_entry *entry;
   1.134 +
   1.135 +  if (dict_obj->type == PT_IND_REF)
   1.136 +    dict_obj = pdf_deref_ind_obj (dict_obj);
   1.137 +
   1.138 +  pdf_assert (dict_obj->type == PT_DICTIONARY);
   1.139 +
   1.140 +  for (entry = dict_obj->val.dict.first; entry; entry = entry->next)
   1.141 +    if (strcmp (entry->key, key) == 0)
   1.142 +      return (entry->val);
   1.143 +
   1.144 +  return (NULL);
   1.145 +}
   1.146 +
   1.147 +
   1.148 +void pdf_add_array_elem (struct pdf_obj *array_obj, struct pdf_obj *val)
   1.149 +{
   1.150 +  struct pdf_array_elem *elem = pdf_calloc (sizeof (struct pdf_array_elem));
   1.151 +
   1.152 +  if (array_obj->type == PT_IND_REF)
   1.153 +    array_obj = pdf_deref_ind_obj (array_obj);
   1.154 +
   1.155 +  pdf_assert (array_obj->type == PT_ARRAY);
   1.156 +
   1.157 +  elem->val = ref (val);
   1.158 +
   1.159 +  if (! array_obj->val.array.first)
   1.160 +    array_obj->val.array.first = elem;
   1.161 +  else
   1.162 +    array_obj->val.array.last->next = elem;
   1.163 +
   1.164 +  array_obj->val.array.last = elem;
   1.165 +}
   1.166 +
   1.167 +
   1.168 +struct pdf_obj *pdf_new_obj (pdf_obj_type type)
   1.169 +{
   1.170 +  struct pdf_obj *obj = pdf_calloc (sizeof (struct pdf_obj));
   1.171 +  obj->type = type;
   1.172 +  return (obj);
   1.173 +}
   1.174 +
   1.175 +
   1.176 +struct pdf_obj *pdf_new_bool (int bool)
   1.177 +{
   1.178 +  struct pdf_obj *obj = pdf_new_obj (PT_BOOL);
   1.179 +  obj->val.bool = bool;
   1.180 +  return (obj);
   1.181 +}
   1.182 +
   1.183 +
   1.184 +struct pdf_obj *pdf_new_name (char *name)
   1.185 +{
   1.186 +  struct pdf_obj *obj = pdf_new_obj (PT_NAME);
   1.187 +  obj->val.name = pdf_strdup (name);
   1.188 +  return (obj);
   1.189 +}
   1.190 +
   1.191 +
   1.192 +struct pdf_obj *pdf_new_string (char *str)
   1.193 +{
   1.194 +  struct pdf_obj *obj = pdf_new_obj (PT_STRING);
   1.195 +  obj->val.string = pdf_strdup (str);
   1.196 +  return (obj);
   1.197 +}
   1.198 +
   1.199 +
   1.200 +struct pdf_obj *pdf_new_integer (unsigned long val)
   1.201 +{
   1.202 +  struct pdf_obj *obj = pdf_new_obj (PT_INTEGER);
   1.203 +  obj->val.integer = val;
   1.204 +  return (obj);
   1.205 +}
   1.206 +
   1.207 +
   1.208 +struct pdf_obj *pdf_new_real (double val)
   1.209 +{
   1.210 +  struct pdf_obj *obj = pdf_new_obj (PT_REAL);
   1.211 +  obj->val.real = val;
   1.212 +  return (obj);
   1.213 +}
   1.214 +
   1.215 +
   1.216 +struct pdf_obj *pdf_new_stream (pdf_file_handle pdf_file,
   1.217 +				struct pdf_obj *stream_dict,
   1.218 +				pdf_stream_write_callback callback,
   1.219 +				void *app_data)
   1.220 +{
   1.221 +  struct pdf_obj *obj = pdf_new_obj (PT_STREAM);
   1.222 +
   1.223 +  obj->val.stream.stream_dict = stream_dict;
   1.224 +  obj->val.stream.length = pdf_new_ind_ref (pdf_file, pdf_new_integer (0));
   1.225 +  pdf_set_dict_entry (obj->val.stream.stream_dict, "Length", obj->val.stream.length);
   1.226 +
   1.227 +  obj->val.stream.callback = callback;
   1.228 +  obj->val.stream.app_data = app_data;
   1.229 +  return (obj);
   1.230 +}
   1.231 +
   1.232 +
   1.233 +/* $$$ currently limited to one filter per stream */
   1.234 +void pdf_stream_add_filter (struct pdf_obj *stream,
   1.235 +			    char *filter_name,
   1.236 +			    struct pdf_obj *decode_parms)
   1.237 +{
   1.238 +  if (stream->type == PT_IND_REF)
   1.239 +    stream = pdf_deref_ind_obj (stream);
   1.240 +
   1.241 +  pdf_assert (stream->type == PT_STREAM);
   1.242 +
   1.243 +  pdf_set_dict_entry (stream->val.stream.stream_dict, "Filter", pdf_new_name (filter_name));
   1.244 +  if (decode_parms)
   1.245 +    pdf_set_dict_entry (stream->val.stream.stream_dict, "DecodeParms", decode_parms);
   1.246 +}
   1.247 +
   1.248 +
   1.249 +struct pdf_obj *pdf_new_ind_ref (pdf_file_handle pdf_file, struct pdf_obj *obj)
   1.250 +{
   1.251 +  struct pdf_obj *ind_obj;
   1.252 +
   1.253 +  pdf_assert (obj->type != PT_IND_REF);
   1.254 +
   1.255 +  ind_obj = pdf_new_obj (PT_IND_REF);
   1.256 +
   1.257 +  ind_obj->type = PT_IND_REF;
   1.258 +  ind_obj->val.ind_ref = obj;
   1.259 +
   1.260 +  /* is there already an indirect reference to this object? */
   1.261 +  if (! obj->obj_num)
   1.262 +    {
   1.263 +      /* no, assign object number/generation and add to linked list */
   1.264 +      if (! pdf_file->first_ind_obj)
   1.265 +	{
   1.266 +	  obj->obj_num = 1;
   1.267 +	  pdf_file->first_ind_obj = pdf_file->last_ind_obj = obj;
   1.268 +	}
   1.269 +      else
   1.270 +	{
   1.271 +	  obj->obj_num = pdf_file->last_ind_obj->obj_num + 1;
   1.272 +	  pdf_file->last_ind_obj->next = obj;
   1.273 +	  obj->prev = pdf_file->last_ind_obj;
   1.274 +	  pdf_file->last_ind_obj = obj;
   1.275 +	}
   1.276 +    }
   1.277 +
   1.278 +  return (ind_obj);
   1.279 +}
   1.280 +
   1.281 +
   1.282 +unsigned long pdf_get_integer (struct pdf_obj *obj)
   1.283 +{
   1.284 +  if (obj->type == PT_IND_REF)
   1.285 +    obj = pdf_deref_ind_obj (obj);
   1.286 +
   1.287 +  pdf_assert (obj->type == PT_INTEGER);
   1.288 +
   1.289 +  return (obj->val.integer);
   1.290 +}
   1.291 +
   1.292 +void pdf_set_integer (struct pdf_obj *obj, unsigned long val)
   1.293 +{
   1.294 +  if (obj->type == PT_IND_REF)
   1.295 +    obj = pdf_deref_ind_obj (obj);
   1.296 +
   1.297 +  pdf_assert (obj->type == PT_INTEGER);
   1.298 +
   1.299 +  obj->val.integer = val;
   1.300 +}
   1.301 +
   1.302 +
   1.303 +double pdf_get_real (struct pdf_obj *obj)
   1.304 +{
   1.305 +  if (obj->type == PT_IND_REF)
   1.306 +    obj = pdf_deref_ind_obj (obj);
   1.307 +
   1.308 +  pdf_assert (obj->type == PT_REAL);
   1.309 +
   1.310 +  return (obj->val.real);
   1.311 +}
   1.312 +
   1.313 +void pdf_set_real (struct pdf_obj *obj, double val)
   1.314 +{
   1.315 +  if (obj->type == PT_IND_REF)
   1.316 +    obj = pdf_deref_ind_obj (obj);
   1.317 +
   1.318 +  pdf_assert (obj->type == PT_REAL);
   1.319 +
   1.320 +  obj->val.real = val;
   1.321 +}
   1.322 +
   1.323 +
   1.324 +static int name_char_needs_quoting (char c)
   1.325 +{
   1.326 +  return ((c < '!')  || (c > '~')  || (c == '/') || (c == '\\') ||
   1.327 +	  (c == '(') || (c == ')') || (c == '<') || (c == '>')  ||
   1.328 +	  (c == '[') || (c == ']') || (c == '{') || (c == '}')  ||
   1.329 +	  (c == '%'));
   1.330 +}
   1.331 +
   1.332 +
   1.333 +void pdf_write_name (pdf_file_handle pdf_file, char *s)
   1.334 +{
   1.335 +  fprintf (pdf_file->f, "/");
   1.336 +  while (*s)
   1.337 +    if (name_char_needs_quoting (*s))
   1.338 +      fprintf (pdf_file->f, "#%02x", 0xff & *(s++));
   1.339 +    else
   1.340 +      fprintf (pdf_file->f, "%c", *(s++));
   1.341 +  fprintf (pdf_file->f, " ");
   1.342 +}
   1.343 +
   1.344 +
   1.345 +static int string_char_needs_quoting (char c)
   1.346 +{
   1.347 +  return ((c < ' ')  || (c > '~')  || (c == '\\') ||
   1.348 +	  (c == '(') || (c == ')'));
   1.349 +}
   1.350 +
   1.351 +
   1.352 +void pdf_write_string (pdf_file_handle pdf_file, char *s)
   1.353 +{
   1.354 +  fprintf (pdf_file->f, "(");
   1.355 +  while (*s)
   1.356 +    if (string_char_needs_quoting (*s))
   1.357 +      fprintf (pdf_file->f, "\\%03o", 0xff & *(s++));
   1.358 +    else
   1.359 +      fprintf (pdf_file->f, "%c", *(s++));
   1.360 +  fprintf (pdf_file->f, ") ");
   1.361 +}
   1.362 +
   1.363 +
   1.364 +void pdf_write_real (pdf_file_handle pdf_file, double num)
   1.365 +{
   1.366 +  /* $$$ not actually good enough, precision needs to be variable,
   1.367 +     and no exponent is allowed */
   1.368 +  fprintf (pdf_file->f, "%0f ", num);
   1.369 +}
   1.370 +
   1.371 +
   1.372 +void pdf_write_ind_ref (pdf_file_handle pdf_file, struct pdf_obj *ind_obj)
   1.373 +{
   1.374 +  struct pdf_obj *obj = pdf_deref_ind_obj (ind_obj);
   1.375 +  fprintf (pdf_file->f, "%ld %ld R ", obj->obj_num, obj->obj_gen);
   1.376 +}
   1.377 +
   1.378 +
   1.379 +void pdf_write_array (pdf_file_handle pdf_file, struct pdf_obj *array_obj)
   1.380 +{
   1.381 +  struct pdf_array_elem *elem;
   1.382 +
   1.383 +  pdf_assert (array_obj->type == PT_ARRAY);
   1.384 +
   1.385 +  fprintf (pdf_file->f, "[ ");
   1.386 +  for (elem = array_obj->val.array.first; elem; elem = elem->next)
   1.387 +    {
   1.388 +      pdf_write_obj (pdf_file, elem->val);
   1.389 +      fprintf (pdf_file->f, " ");
   1.390 +    }
   1.391 +  fprintf (pdf_file->f, "] ");
   1.392 +}
   1.393 +
   1.394 +
   1.395 +void pdf_write_dict (pdf_file_handle pdf_file, struct pdf_obj *dict_obj)
   1.396 +{
   1.397 +  struct pdf_dict_entry *entry;
   1.398 +
   1.399 +  pdf_assert (dict_obj->type == PT_DICTIONARY);
   1.400 +
   1.401 +  fprintf (pdf_file->f, "<<\r\n");
   1.402 +  for (entry = dict_obj->val.dict.first; entry; entry = entry->next)
   1.403 +    {
   1.404 +      pdf_write_name (pdf_file, entry->key);
   1.405 +      fprintf (pdf_file->f, " ");
   1.406 +      pdf_write_obj (pdf_file, entry->val);
   1.407 +      fprintf (pdf_file->f, "\r\n");
   1.408 +    }
   1.409 +  fprintf (pdf_file->f, ">>\r\n");
   1.410 +}
   1.411 +
   1.412 +
   1.413 +void pdf_stream_write_data (pdf_file_handle pdf_file,
   1.414 +			    struct pdf_obj *stream,
   1.415 +			    char *data,
   1.416 +			    unsigned long len)
   1.417 +{
   1.418 +  while (len)
   1.419 +    {
   1.420 +      unsigned long l2 = fwrite (data, 1, len, pdf_file->f);
   1.421 +      data += l2;
   1.422 +      len -= l2;
   1.423 +      if (ferror (pdf_file->f))
   1.424 +	pdf_fatal ("error writing stream data\n");
   1.425 +    }
   1.426 +}
   1.427 +
   1.428 +
   1.429 +void pdf_write_stream (pdf_file_handle pdf_file, struct pdf_obj *stream)
   1.430 +{
   1.431 +  unsigned long begin_pos, end_pos;
   1.432 +
   1.433 +  pdf_assert (stream->type == PT_STREAM);
   1.434 +
   1.435 +  pdf_write_dict (pdf_file, stream->val.stream.stream_dict);
   1.436 +  fprintf (pdf_file->f, "stream\r\n");
   1.437 +  begin_pos = ftell (pdf_file->f);
   1.438 +  stream->val.stream.callback (pdf_file,
   1.439 +			       stream,
   1.440 +			       stream->val.stream.app_data);
   1.441 +  end_pos = ftell (pdf_file->f);
   1.442 +  fprintf (pdf_file->f, "\r\nendstream\r\n");
   1.443 +
   1.444 +  pdf_set_integer (stream->val.stream.length, end_pos - begin_pos);
   1.445 +}
   1.446 +
   1.447 +
   1.448 +void pdf_write_obj (pdf_file_handle pdf_file, struct pdf_obj *obj)
   1.449 +{
   1.450 +  switch (obj->type)
   1.451 +    {
   1.452 +    case PT_NULL:
   1.453 +      fprintf (pdf_file->f, "null ");
   1.454 +      break;
   1.455 +    case PT_BOOL:
   1.456 +      if (obj->val.bool)
   1.457 +	fprintf (pdf_file->f, "true ");
   1.458 +      else
   1.459 +	fprintf (pdf_file->f, "false ");
   1.460 +      break;
   1.461 +    case PT_NAME:
   1.462 +      pdf_write_name (pdf_file, obj->val.name);
   1.463 +      break;
   1.464 +    case PT_STRING:
   1.465 +      pdf_write_string (pdf_file, obj->val.string);
   1.466 +      break;
   1.467 +    case PT_INTEGER:
   1.468 +      fprintf (pdf_file->f, "%ld ", obj->val.integer);
   1.469 +      break;
   1.470 +    case PT_REAL:
   1.471 +      pdf_write_real (pdf_file, obj->val.real);
   1.472 +      break;
   1.473 +    case PT_IND_REF:
   1.474 +      pdf_write_ind_ref (pdf_file, obj);
   1.475 +      break;
   1.476 +    case PT_DICTIONARY:
   1.477 +      pdf_write_dict (pdf_file, obj);
   1.478 +      break;
   1.479 +    case PT_ARRAY:
   1.480 +      pdf_write_array (pdf_file, obj);
   1.481 +      break;
   1.482 +    case PT_STREAM:
   1.483 +      pdf_write_stream (pdf_file, obj);
   1.484 +      break;
   1.485 +    default:
   1.486 +      pdf_fatal ("bad object type\n");
   1.487 +    }
   1.488 +}
   1.489 +
   1.490 +
   1.491 +void pdf_write_ind_obj (pdf_file_handle pdf_file, struct pdf_obj *ind_obj)
   1.492 +{
   1.493 +  struct pdf_obj *obj;
   1.494 +
   1.495 +  if (ind_obj->type == PT_IND_REF)
   1.496 +    obj = pdf_deref_ind_obj (ind_obj);
   1.497 +  else
   1.498 +    obj = ind_obj;
   1.499 +
   1.500 +  obj->file_offset = ftell (pdf_file->f);
   1.501 +  fprintf (pdf_file->f, "%ld %ld obj\r\n", obj->obj_num, obj->obj_gen);
   1.502 +  pdf_write_obj (pdf_file, obj);
   1.503 +  fprintf (pdf_file->f, "endobj\r\n");
   1.504 +}
   1.505 +
   1.506 +
   1.507 +void pdf_write_all_ind_obj (pdf_file_handle pdf_file)
   1.508 +{
   1.509 +  struct pdf_obj *ind_obj;
   1.510 +  for (ind_obj = pdf_file->first_ind_obj; ind_obj; ind_obj = ind_obj->next)
   1.511 +    if (! ind_obj->file_offset)
   1.512 +      pdf_write_ind_obj (pdf_file, ind_obj);
   1.513 +}
   1.514 +
   1.515 +
   1.516 +unsigned long pdf_write_xref (pdf_file_handle pdf_file)
   1.517 +{
   1.518 +  struct pdf_obj *ind_obj;
   1.519 +  pdf_file->xref_offset = ftell (pdf_file->f);
   1.520 +  fprintf (pdf_file->f, "xref\r\n");
   1.521 +  fprintf (pdf_file->f, "0 %ld\r\n", pdf_file->last_ind_obj->obj_num + 1);
   1.522 +  fprintf (pdf_file->f, "0000000000 65535 f\r\n");
   1.523 +  for (ind_obj = pdf_file->first_ind_obj; ind_obj; ind_obj = ind_obj->next)
   1.524 +    fprintf (pdf_file->f, "%010ld 00000 n\r\n", ind_obj->file_offset);
   1.525 +  return (pdf_file->last_ind_obj->obj_num + 1);
   1.526 +}
   1.527 +
   1.528 +