pdf_prim.c

Fri, 21 Feb 2003 09:12:05 +0000

author
eric
date
Fri, 21 Feb 2003 09:12:05 +0000
changeset 65
5acb5b549729
parent 62
9bd354b83e16
child 66
6e0551b59dba
permissions
-rw-r--r--

*** empty log message ***

eric@62 1 /*
eric@62 2 * t2p: Create a PDF file from the contents of one or more TIFF
eric@62 3 * bilevel image files. The images in the resulting PDF file
eric@62 4 * will be compressed using ITU-T T.6 (G4) fax encoding.
eric@62 5 *
eric@62 6 * PDF routines
eric@62 7 * $Id: pdf_prim.c,v 1.3 2003/02/20 04:44:17 eric Exp $
eric@62 8 * Copyright 2001, 2002, 2003 Eric Smith <eric@brouhaha.com>
eric@62 9 *
eric@62 10 * This program is free software; you can redistribute it and/or modify
eric@62 11 * it under the terms of the GNU General Public License version 2 as
eric@62 12 * published by the Free Software Foundation. Note that permission is
eric@62 13 * not granted to redistribute this program under the terms of any
eric@62 14 * other version of the General Public License.
eric@62 15 *
eric@62 16 * This program is distributed in the hope that it will be useful,
eric@62 17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
eric@62 18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
eric@62 19 * GNU General Public License for more details.
eric@62 20 *
eric@62 21 * You should have received a copy of the GNU General Public License
eric@62 22 * along with this program; if not, write to the Free Software
eric@62 23 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111 USA
eric@62 24 */
eric@62 25
eric@62 26
eric@62 27 #include <stdbool.h>
eric@62 28 #include <stdint.h>
eric@59 29 #include <stdio.h>
eric@59 30 #include <stdlib.h>
eric@59 31 #include <string.h>
eric@59 32
eric@62 33 #include "bitblt.h"
eric@60 34 #include "pdf.h"
eric@60 35 #include "pdf_util.h"
eric@60 36 #include "pdf_prim.h"
eric@60 37 #include "pdf_private.h"
eric@59 38
eric@59 39
eric@59 40 struct pdf_array_elem
eric@59 41 {
eric@59 42 struct pdf_array_elem *next;
eric@59 43 struct pdf_obj *val;
eric@59 44 };
eric@59 45
eric@59 46
eric@59 47 struct pdf_array
eric@59 48 {
eric@59 49 struct pdf_array_elem *first;
eric@59 50 struct pdf_array_elem *last;
eric@59 51 };
eric@59 52
eric@59 53
eric@59 54 struct pdf_dict_entry
eric@59 55 {
eric@59 56 struct pdf_dict_entry *next;
eric@59 57 char *key;
eric@59 58 struct pdf_obj *val;
eric@59 59 };
eric@59 60
eric@59 61
eric@59 62 struct pdf_dict
eric@59 63 {
eric@59 64 struct pdf_dict_entry *first;
eric@59 65 };
eric@59 66
eric@59 67
eric@59 68 struct pdf_stream
eric@59 69 {
eric@59 70 struct pdf_obj *stream_dict;
eric@59 71 struct pdf_obj *length;
eric@59 72 pdf_stream_write_callback callback;
eric@59 73 void *app_data; /* arg to pass to callback */
eric@59 74 struct pdf_obj *filters; /* name or array of names */
eric@59 75 struct pdf_obj *decode_parms;
eric@59 76 };
eric@59 77
eric@59 78
eric@59 79 struct pdf_obj
eric@59 80 {
eric@59 81 /* these fields only apply to indirectly referenced objects */
eric@59 82 struct pdf_obj *prev;
eric@59 83 struct pdf_obj *next;
eric@59 84 unsigned long obj_num;
eric@59 85 unsigned long obj_gen;
eric@59 86 long int file_offset;
eric@59 87
eric@59 88 /* these fields apply to all objects */
eric@59 89 unsigned long ref_count;
eric@59 90 pdf_obj_type type;
eric@59 91 union {
eric@62 92 bool boolean;
eric@59 93 char *name;
eric@59 94 char *string;
eric@59 95 unsigned long integer;
eric@59 96 double real;
eric@59 97 struct pdf_obj *ind_ref;
eric@59 98 struct pdf_dict dict;
eric@59 99 struct pdf_array array;
eric@59 100 struct pdf_stream stream;
eric@59 101 } val;
eric@59 102 };
eric@59 103
eric@59 104
eric@59 105 struct pdf_obj *ref (struct pdf_obj *obj)
eric@59 106 {
eric@59 107 obj->ref_count++;
eric@59 108 return (obj);
eric@59 109 }
eric@59 110
eric@59 111
eric@59 112 void unref (struct pdf_obj *obj)
eric@59 113 {
eric@59 114 if ((--obj->ref_count) == 0)
eric@59 115 {
eric@59 116 /* $$$ free the object */
eric@59 117 }
eric@59 118 }
eric@59 119
eric@59 120
eric@59 121 struct pdf_obj *pdf_deref_ind_obj (struct pdf_obj *ind_obj)
eric@59 122 {
eric@59 123 pdf_assert (ind_obj->type == PT_IND_REF);
eric@59 124 return (ind_obj->val.ind_ref);
eric@59 125 }
eric@59 126
eric@59 127
eric@59 128 void pdf_set_dict_entry (struct pdf_obj *dict_obj, char *key, struct pdf_obj *val)
eric@59 129 {
eric@59 130 struct pdf_dict_entry *entry;
eric@59 131
eric@59 132 if (dict_obj->type == PT_IND_REF)
eric@59 133 dict_obj = pdf_deref_ind_obj (dict_obj);
eric@59 134
eric@59 135 pdf_assert (dict_obj->type == PT_DICTIONARY);
eric@59 136
eric@59 137 /* replacing existing entry? */
eric@59 138 for (entry = dict_obj->val.dict.first; entry; entry = entry->next)
eric@59 139 if (strcmp (entry->key, key) == 0)
eric@59 140 {
eric@59 141 unref (entry->val);
eric@59 142 entry->val = ref (val);
eric@59 143 return;
eric@59 144 }
eric@59 145
eric@59 146 /* new entry */
eric@59 147 entry = pdf_calloc (sizeof (struct pdf_dict_entry));
eric@59 148
eric@59 149 entry->next = dict_obj->val.dict.first;
eric@59 150 dict_obj->val.dict.first = entry;
eric@59 151
eric@59 152 entry->key = pdf_strdup (key);
eric@59 153 entry->val = ref (val);
eric@59 154 }
eric@59 155
eric@59 156
eric@59 157 struct pdf_obj *pdf_get_dict_entry (struct pdf_obj *dict_obj, char *key)
eric@59 158 {
eric@59 159 struct pdf_dict_entry *entry;
eric@59 160
eric@59 161 if (dict_obj->type == PT_IND_REF)
eric@59 162 dict_obj = pdf_deref_ind_obj (dict_obj);
eric@59 163
eric@59 164 pdf_assert (dict_obj->type == PT_DICTIONARY);
eric@59 165
eric@59 166 for (entry = dict_obj->val.dict.first; entry; entry = entry->next)
eric@59 167 if (strcmp (entry->key, key) == 0)
eric@59 168 return (entry->val);
eric@59 169
eric@59 170 return (NULL);
eric@59 171 }
eric@59 172
eric@59 173
eric@59 174 void pdf_add_array_elem (struct pdf_obj *array_obj, struct pdf_obj *val)
eric@59 175 {
eric@59 176 struct pdf_array_elem *elem = pdf_calloc (sizeof (struct pdf_array_elem));
eric@59 177
eric@59 178 if (array_obj->type == PT_IND_REF)
eric@59 179 array_obj = pdf_deref_ind_obj (array_obj);
eric@59 180
eric@59 181 pdf_assert (array_obj->type == PT_ARRAY);
eric@59 182
eric@59 183 elem->val = ref (val);
eric@59 184
eric@59 185 if (! array_obj->val.array.first)
eric@59 186 array_obj->val.array.first = elem;
eric@59 187 else
eric@59 188 array_obj->val.array.last->next = elem;
eric@59 189
eric@59 190 array_obj->val.array.last = elem;
eric@59 191 }
eric@59 192
eric@59 193
eric@59 194 struct pdf_obj *pdf_new_obj (pdf_obj_type type)
eric@59 195 {
eric@59 196 struct pdf_obj *obj = pdf_calloc (sizeof (struct pdf_obj));
eric@59 197 obj->type = type;
eric@59 198 return (obj);
eric@59 199 }
eric@59 200
eric@59 201
eric@62 202 struct pdf_obj *pdf_new_bool (bool val)
eric@59 203 {
eric@59 204 struct pdf_obj *obj = pdf_new_obj (PT_BOOL);
eric@62 205 obj->val.boolean = val;
eric@59 206 return (obj);
eric@59 207 }
eric@59 208
eric@59 209
eric@59 210 struct pdf_obj *pdf_new_name (char *name)
eric@59 211 {
eric@59 212 struct pdf_obj *obj = pdf_new_obj (PT_NAME);
eric@59 213 obj->val.name = pdf_strdup (name);
eric@59 214 return (obj);
eric@59 215 }
eric@59 216
eric@59 217
eric@59 218 struct pdf_obj *pdf_new_string (char *str)
eric@59 219 {
eric@59 220 struct pdf_obj *obj = pdf_new_obj (PT_STRING);
eric@59 221 obj->val.string = pdf_strdup (str);
eric@59 222 return (obj);
eric@59 223 }
eric@59 224
eric@59 225
eric@59 226 struct pdf_obj *pdf_new_integer (unsigned long val)
eric@59 227 {
eric@59 228 struct pdf_obj *obj = pdf_new_obj (PT_INTEGER);
eric@59 229 obj->val.integer = val;
eric@59 230 return (obj);
eric@59 231 }
eric@59 232
eric@59 233
eric@59 234 struct pdf_obj *pdf_new_real (double val)
eric@59 235 {
eric@59 236 struct pdf_obj *obj = pdf_new_obj (PT_REAL);
eric@59 237 obj->val.real = val;
eric@59 238 return (obj);
eric@59 239 }
eric@59 240
eric@59 241
eric@59 242 struct pdf_obj *pdf_new_stream (pdf_file_handle pdf_file,
eric@59 243 struct pdf_obj *stream_dict,
eric@59 244 pdf_stream_write_callback callback,
eric@59 245 void *app_data)
eric@59 246 {
eric@59 247 struct pdf_obj *obj = pdf_new_obj (PT_STREAM);
eric@59 248
eric@59 249 obj->val.stream.stream_dict = stream_dict;
eric@59 250 obj->val.stream.length = pdf_new_ind_ref (pdf_file, pdf_new_integer (0));
eric@59 251 pdf_set_dict_entry (obj->val.stream.stream_dict, "Length", obj->val.stream.length);
eric@59 252
eric@59 253 obj->val.stream.callback = callback;
eric@59 254 obj->val.stream.app_data = app_data;
eric@59 255 return (obj);
eric@59 256 }
eric@59 257
eric@59 258
eric@59 259 /* $$$ currently limited to one filter per stream */
eric@59 260 void pdf_stream_add_filter (struct pdf_obj *stream,
eric@59 261 char *filter_name,
eric@59 262 struct pdf_obj *decode_parms)
eric@59 263 {
eric@59 264 if (stream->type == PT_IND_REF)
eric@59 265 stream = pdf_deref_ind_obj (stream);
eric@59 266
eric@59 267 pdf_assert (stream->type == PT_STREAM);
eric@59 268
eric@59 269 pdf_set_dict_entry (stream->val.stream.stream_dict, "Filter", pdf_new_name (filter_name));
eric@59 270 if (decode_parms)
eric@59 271 pdf_set_dict_entry (stream->val.stream.stream_dict, "DecodeParms", decode_parms);
eric@59 272 }
eric@59 273
eric@59 274
eric@59 275 struct pdf_obj *pdf_new_ind_ref (pdf_file_handle pdf_file, struct pdf_obj *obj)
eric@59 276 {
eric@59 277 struct pdf_obj *ind_obj;
eric@59 278
eric@59 279 pdf_assert (obj->type != PT_IND_REF);
eric@59 280
eric@59 281 ind_obj = pdf_new_obj (PT_IND_REF);
eric@59 282
eric@59 283 ind_obj->type = PT_IND_REF;
eric@59 284 ind_obj->val.ind_ref = obj;
eric@59 285
eric@59 286 /* is there already an indirect reference to this object? */
eric@59 287 if (! obj->obj_num)
eric@59 288 {
eric@59 289 /* no, assign object number/generation and add to linked list */
eric@59 290 if (! pdf_file->first_ind_obj)
eric@59 291 {
eric@59 292 obj->obj_num = 1;
eric@59 293 pdf_file->first_ind_obj = pdf_file->last_ind_obj = obj;
eric@59 294 }
eric@59 295 else
eric@59 296 {
eric@59 297 obj->obj_num = pdf_file->last_ind_obj->obj_num + 1;
eric@59 298 pdf_file->last_ind_obj->next = obj;
eric@59 299 obj->prev = pdf_file->last_ind_obj;
eric@59 300 pdf_file->last_ind_obj = obj;
eric@59 301 }
eric@59 302 }
eric@59 303
eric@59 304 return (ind_obj);
eric@59 305 }
eric@59 306
eric@59 307
eric@59 308 unsigned long pdf_get_integer (struct pdf_obj *obj)
eric@59 309 {
eric@59 310 if (obj->type == PT_IND_REF)
eric@59 311 obj = pdf_deref_ind_obj (obj);
eric@59 312
eric@59 313 pdf_assert (obj->type == PT_INTEGER);
eric@59 314
eric@59 315 return (obj->val.integer);
eric@59 316 }
eric@59 317
eric@59 318 void pdf_set_integer (struct pdf_obj *obj, unsigned long val)
eric@59 319 {
eric@59 320 if (obj->type == PT_IND_REF)
eric@59 321 obj = pdf_deref_ind_obj (obj);
eric@59 322
eric@59 323 pdf_assert (obj->type == PT_INTEGER);
eric@59 324
eric@59 325 obj->val.integer = val;
eric@59 326 }
eric@59 327
eric@59 328
eric@59 329 double pdf_get_real (struct pdf_obj *obj)
eric@59 330 {
eric@59 331 if (obj->type == PT_IND_REF)
eric@59 332 obj = pdf_deref_ind_obj (obj);
eric@59 333
eric@59 334 pdf_assert (obj->type == PT_REAL);
eric@59 335
eric@59 336 return (obj->val.real);
eric@59 337 }
eric@59 338
eric@59 339 void pdf_set_real (struct pdf_obj *obj, double val)
eric@59 340 {
eric@59 341 if (obj->type == PT_IND_REF)
eric@59 342 obj = pdf_deref_ind_obj (obj);
eric@59 343
eric@59 344 pdf_assert (obj->type == PT_REAL);
eric@59 345
eric@59 346 obj->val.real = val;
eric@59 347 }
eric@59 348
eric@59 349
eric@59 350 static int name_char_needs_quoting (char c)
eric@59 351 {
eric@59 352 return ((c < '!') || (c > '~') || (c == '/') || (c == '\\') ||
eric@59 353 (c == '(') || (c == ')') || (c == '<') || (c == '>') ||
eric@59 354 (c == '[') || (c == ']') || (c == '{') || (c == '}') ||
eric@59 355 (c == '%'));
eric@59 356 }
eric@59 357
eric@59 358
eric@59 359 void pdf_write_name (pdf_file_handle pdf_file, char *s)
eric@59 360 {
eric@59 361 fprintf (pdf_file->f, "/");
eric@59 362 while (*s)
eric@59 363 if (name_char_needs_quoting (*s))
eric@59 364 fprintf (pdf_file->f, "#%02x", 0xff & *(s++));
eric@59 365 else
eric@59 366 fprintf (pdf_file->f, "%c", *(s++));
eric@59 367 fprintf (pdf_file->f, " ");
eric@59 368 }
eric@59 369
eric@59 370
eric@59 371 static int string_char_needs_quoting (char c)
eric@59 372 {
eric@59 373 return ((c < ' ') || (c > '~') || (c == '\\') ||
eric@59 374 (c == '(') || (c == ')'));
eric@59 375 }
eric@59 376
eric@59 377
eric@59 378 void pdf_write_string (pdf_file_handle pdf_file, char *s)
eric@59 379 {
eric@59 380 fprintf (pdf_file->f, "(");
eric@59 381 while (*s)
eric@59 382 if (string_char_needs_quoting (*s))
eric@59 383 fprintf (pdf_file->f, "\\%03o", 0xff & *(s++));
eric@59 384 else
eric@59 385 fprintf (pdf_file->f, "%c", *(s++));
eric@59 386 fprintf (pdf_file->f, ") ");
eric@59 387 }
eric@59 388
eric@59 389
eric@59 390 void pdf_write_real (pdf_file_handle pdf_file, double num)
eric@59 391 {
eric@59 392 /* $$$ not actually good enough, precision needs to be variable,
eric@59 393 and no exponent is allowed */
eric@59 394 fprintf (pdf_file->f, "%0f ", num);
eric@59 395 }
eric@59 396
eric@59 397
eric@59 398 void pdf_write_ind_ref (pdf_file_handle pdf_file, struct pdf_obj *ind_obj)
eric@59 399 {
eric@59 400 struct pdf_obj *obj = pdf_deref_ind_obj (ind_obj);
eric@59 401 fprintf (pdf_file->f, "%ld %ld R ", obj->obj_num, obj->obj_gen);
eric@59 402 }
eric@59 403
eric@59 404
eric@59 405 void pdf_write_array (pdf_file_handle pdf_file, struct pdf_obj *array_obj)
eric@59 406 {
eric@59 407 struct pdf_array_elem *elem;
eric@59 408
eric@59 409 pdf_assert (array_obj->type == PT_ARRAY);
eric@59 410
eric@59 411 fprintf (pdf_file->f, "[ ");
eric@59 412 for (elem = array_obj->val.array.first; elem; elem = elem->next)
eric@59 413 {
eric@59 414 pdf_write_obj (pdf_file, elem->val);
eric@59 415 fprintf (pdf_file->f, " ");
eric@59 416 }
eric@59 417 fprintf (pdf_file->f, "] ");
eric@59 418 }
eric@59 419
eric@59 420
eric@59 421 void pdf_write_dict (pdf_file_handle pdf_file, struct pdf_obj *dict_obj)
eric@59 422 {
eric@59 423 struct pdf_dict_entry *entry;
eric@59 424
eric@59 425 pdf_assert (dict_obj->type == PT_DICTIONARY);
eric@59 426
eric@59 427 fprintf (pdf_file->f, "<<\r\n");
eric@59 428 for (entry = dict_obj->val.dict.first; entry; entry = entry->next)
eric@59 429 {
eric@59 430 pdf_write_name (pdf_file, entry->key);
eric@59 431 fprintf (pdf_file->f, " ");
eric@59 432 pdf_write_obj (pdf_file, entry->val);
eric@59 433 fprintf (pdf_file->f, "\r\n");
eric@59 434 }
eric@59 435 fprintf (pdf_file->f, ">>\r\n");
eric@59 436 }
eric@59 437
eric@59 438
eric@59 439 void pdf_stream_write_data (pdf_file_handle pdf_file,
eric@59 440 struct pdf_obj *stream,
eric@59 441 char *data,
eric@59 442 unsigned long len)
eric@59 443 {
eric@59 444 while (len)
eric@59 445 {
eric@59 446 unsigned long l2 = fwrite (data, 1, len, pdf_file->f);
eric@59 447 data += l2;
eric@59 448 len -= l2;
eric@59 449 if (ferror (pdf_file->f))
eric@59 450 pdf_fatal ("error writing stream data\n");
eric@59 451 }
eric@59 452 }
eric@59 453
eric@59 454
eric@59 455 void pdf_write_stream (pdf_file_handle pdf_file, struct pdf_obj *stream)
eric@59 456 {
eric@59 457 unsigned long begin_pos, end_pos;
eric@59 458
eric@59 459 pdf_assert (stream->type == PT_STREAM);
eric@59 460
eric@59 461 pdf_write_dict (pdf_file, stream->val.stream.stream_dict);
eric@59 462 fprintf (pdf_file->f, "stream\r\n");
eric@59 463 begin_pos = ftell (pdf_file->f);
eric@59 464 stream->val.stream.callback (pdf_file,
eric@59 465 stream,
eric@59 466 stream->val.stream.app_data);
eric@59 467 end_pos = ftell (pdf_file->f);
eric@59 468 fprintf (pdf_file->f, "\r\nendstream\r\n");
eric@59 469
eric@59 470 pdf_set_integer (stream->val.stream.length, end_pos - begin_pos);
eric@59 471 }
eric@59 472
eric@59 473
eric@59 474 void pdf_write_obj (pdf_file_handle pdf_file, struct pdf_obj *obj)
eric@59 475 {
eric@59 476 switch (obj->type)
eric@59 477 {
eric@59 478 case PT_NULL:
eric@59 479 fprintf (pdf_file->f, "null ");
eric@59 480 break;
eric@59 481 case PT_BOOL:
eric@62 482 if (obj->val.boolean)
eric@59 483 fprintf (pdf_file->f, "true ");
eric@59 484 else
eric@59 485 fprintf (pdf_file->f, "false ");
eric@59 486 break;
eric@59 487 case PT_NAME:
eric@59 488 pdf_write_name (pdf_file, obj->val.name);
eric@59 489 break;
eric@59 490 case PT_STRING:
eric@59 491 pdf_write_string (pdf_file, obj->val.string);
eric@59 492 break;
eric@59 493 case PT_INTEGER:
eric@59 494 fprintf (pdf_file->f, "%ld ", obj->val.integer);
eric@59 495 break;
eric@59 496 case PT_REAL:
eric@59 497 pdf_write_real (pdf_file, obj->val.real);
eric@59 498 break;
eric@59 499 case PT_IND_REF:
eric@59 500 pdf_write_ind_ref (pdf_file, obj);
eric@59 501 break;
eric@59 502 case PT_DICTIONARY:
eric@59 503 pdf_write_dict (pdf_file, obj);
eric@59 504 break;
eric@59 505 case PT_ARRAY:
eric@59 506 pdf_write_array (pdf_file, obj);
eric@59 507 break;
eric@59 508 case PT_STREAM:
eric@59 509 pdf_write_stream (pdf_file, obj);
eric@59 510 break;
eric@59 511 default:
eric@59 512 pdf_fatal ("bad object type\n");
eric@59 513 }
eric@59 514 }
eric@59 515
eric@59 516
eric@59 517 void pdf_write_ind_obj (pdf_file_handle pdf_file, struct pdf_obj *ind_obj)
eric@59 518 {
eric@59 519 struct pdf_obj *obj;
eric@59 520
eric@59 521 if (ind_obj->type == PT_IND_REF)
eric@59 522 obj = pdf_deref_ind_obj (ind_obj);
eric@59 523 else
eric@59 524 obj = ind_obj;
eric@59 525
eric@59 526 obj->file_offset = ftell (pdf_file->f);
eric@59 527 fprintf (pdf_file->f, "%ld %ld obj\r\n", obj->obj_num, obj->obj_gen);
eric@59 528 pdf_write_obj (pdf_file, obj);
eric@59 529 fprintf (pdf_file->f, "endobj\r\n");
eric@59 530 }
eric@59 531
eric@59 532
eric@59 533 void pdf_write_all_ind_obj (pdf_file_handle pdf_file)
eric@59 534 {
eric@59 535 struct pdf_obj *ind_obj;
eric@59 536 for (ind_obj = pdf_file->first_ind_obj; ind_obj; ind_obj = ind_obj->next)
eric@59 537 if (! ind_obj->file_offset)
eric@59 538 pdf_write_ind_obj (pdf_file, ind_obj);
eric@59 539 }
eric@59 540
eric@59 541
eric@59 542 unsigned long pdf_write_xref (pdf_file_handle pdf_file)
eric@59 543 {
eric@59 544 struct pdf_obj *ind_obj;
eric@59 545 pdf_file->xref_offset = ftell (pdf_file->f);
eric@59 546 fprintf (pdf_file->f, "xref\r\n");
eric@59 547 fprintf (pdf_file->f, "0 %ld\r\n", pdf_file->last_ind_obj->obj_num + 1);
eric@59 548 fprintf (pdf_file->f, "0000000000 65535 f\r\n");
eric@59 549 for (ind_obj = pdf_file->first_ind_obj; ind_obj; ind_obj = ind_obj->next)
eric@59 550 fprintf (pdf_file->f, "%010ld 00000 n\r\n", ind_obj->file_offset);
eric@59 551 return (pdf_file->last_ind_obj->obj_num + 1);
eric@59 552 }
eric@59 553
eric@59 554