home *** CD-ROM | disk | FTP | other *** search
/ AmigActive 2 / AACD 2.iso / AACD / Magazine / UsingPDF / GhostScript / source / gs5.10 / gdevpdf.c < prev    next >
Encoding:
C/C++ Source or Header  |  1997-11-02  |  37.6 KB  |  1,338 lines

  1. /* Copyright (C) 1996, 1997 Aladdin Enterprises.  All rights reserved.
  2.   
  3.   This file is part of Aladdin Ghostscript.
  4.   
  5.   Aladdin Ghostscript is distributed with NO WARRANTY OF ANY KIND.  No author
  6.   or distributor accepts any responsibility for the consequences of using it,
  7.   or for whether it serves any particular purpose or works at all, unless he
  8.   or she says so in writing.  Refer to the Aladdin Ghostscript Free Public
  9.   License (the "License") for full details.
  10.   
  11.   Every copy of Aladdin Ghostscript must include a copy of the License,
  12.   normally in a plain ASCII text file named PUBLIC.  The License grants you
  13.   the right to copy, modify and redistribute Aladdin Ghostscript, but only
  14.   under certain conditions described in the License.  Among other things, the
  15.   License requires that the copyright notice and this notice be preserved on
  16.   all copies.
  17. */
  18.  
  19. /* gdevpdf.c */
  20. /* PDF-writing driver */
  21. #include "math_.h"
  22. #include "memory_.h"
  23. #include "string_.h"
  24. #include "time_.h"
  25. #include "gx.h"
  26. #include "gp.h"
  27. #include "gscdefs.h"
  28. #include "gserrors.h"
  29. #include "gxdevice.h"
  30. #include "gxfixed.h"
  31. #include "gxistate.h"
  32. #include "gxpaint.h"
  33. #include "gzpath.h"
  34. #include "gzcpath.h"
  35. #include "gdevpdfx.h"
  36. #include "scanchar.h"
  37. #include "strimpl.h"        /* for short-sighted compilers */
  38. #include "scfx.h"        /* s_CFE_template is default */
  39. #include "sstring.h"
  40. #include "szlibx.h"
  41.  
  42. /*
  43.  ****** DISABLE GC because we still allocate all temporary data
  44.  ****** on the C heap.
  45.  */
  46. #define DISABLE_GC
  47.  
  48. /* Optionally substitute other filters for FlateEncode for debugging. */
  49. #if 1
  50. #  define compression_filter_name "FlateDecode"
  51. #  define compression_filter_template s_zlibE_template
  52. #  define compression_filter_state stream_zlib_state
  53. #else
  54. #  include "slzwx.h"
  55. #  define compression_filter_name "LZWDecode"
  56. #  define compression_filter_template s_LZWE_template
  57. #  define compression_filter_state stream_LZW_state
  58. #endif
  59.  
  60. /* Define the size of the internal stream buffer. */
  61. /* (This is not a limitation, it only affects performance.) */
  62. #define sbuf_size 512
  63.  
  64. /*
  65.  * Define the offset that indicates that a file position is in the
  66.  * resource file (rfile) rather than the main (contents) file.
  67.  * Must be a power of 2, and larger than the largest possible output file.
  68.  */
  69. #define rfile_base_position min_long
  70.  
  71. /* GC descriptors */
  72. private_st_device_pdfwrite();
  73. private_st_pdf_resource();
  74. private_st_pdf_font();
  75. private_st_pdf_char_proc();
  76.  
  77. /* GC procedures */
  78. #define pdev ((gx_device_pdf *)vptr)
  79. private ENUM_PTRS_BEGIN(device_pdfwrite_enum_ptrs) {
  80. #ifdef DISABLE_GC        /* **************** */
  81.     return 0;
  82. #else                /* **************** */
  83.     index -= gx_device_pdf_num_ptrs + gx_device_pdf_num_strings;
  84.     if ( index < num_resource_types * num_resource_chains )
  85.       ENUM_RETURN(pdev->resources[index / num_resource_chains].chains[index % num_resource_chains);
  86.     index -= num_resource_types;
  87.     if ( index < pdev->outline_depth )
  88.       ENUM_RETURN_STRING_PTR(gx_device_pdf, outline_levels[index].first.action_string);
  89.     index -= pdev->outline_depth;
  90.     if ( index < pdev->outline_depth )
  91.       ENUM_RETURN_STRING_PTR(gx_device_pdf, outline_levels[index].last.action_string);
  92.     index -= pdev->outline_depth;
  93.     ENUM_PREFIX(st_device_psdf, 0);
  94. #endif                /* **************** */
  95.     }
  96. #ifndef DISABLE_GC        /* **************** */
  97. #define e1(i,elt) ENUM_PTR(i, gx_device_pdf, elt);
  98.     gx_device_pdf_do_ptrs(e1)
  99. #undef e1
  100. #define e1(i,elt) ENUM_STRING_PTR(i + gx_device_pdf_num_ptrs, gx_device_pdf, elt);
  101.     gx_device_pdf_do_strings(e1)
  102. #undef e1
  103. #endif                /* **************** */
  104. ENUM_PTRS_END
  105. private RELOC_PTRS_BEGIN(device_pdfwrite_reloc_ptrs) {
  106. #ifndef DISABLE_GC        /* **************** */
  107.     RELOC_PREFIX(st_device_psdf);
  108. #define r1(i,elt) RELOC_PTR(gx_device_pdf,elt);
  109.     gx_device_pdf_do_ptrs(r1)
  110. #undef r1
  111. #define r1(i,elt) RELOC_STRING_PTR(gx_device_pdf,elt);
  112.     gx_device_pdf_do_strings(r1)
  113. #undef r1
  114.     { int i, j;
  115.       for ( i = 0; i < num_resource_types; ++i )
  116.         for ( j = 0; j < num_resource_chains; ++j )
  117.           RELOC_PTR(gx_device_pdf, resources[i].chains[j]);
  118.       for ( i = 0; i < pdev->outline_depth; ++i ) {
  119.         RELOC_STRING_PTR(gx_device_pdf, outline_levels[i].first.action_string);
  120.         RELOC_STRING_PTR(gx_device_pdf, outline_levels[i].last.action_string);
  121.       }
  122.     }
  123. #endif                /* **************** */
  124. } RELOC_PTRS_END
  125. #undef pdev
  126. /* Even though device_pdfwrite_finalize is the same as gx_device_finalize, */
  127. /* we need to implement it separately because st_composite_final */
  128. /* declares all 3 procedures as private. */
  129. private void
  130. device_pdfwrite_finalize(void *vpdev)
  131. {    gx_device_finalize(vpdev);
  132. }
  133.  
  134. /* Device procedures */
  135. private dev_proc_open_device(pdf_open);
  136. private dev_proc_output_page(pdf_output_page);
  137. private dev_proc_close_device(pdf_close);
  138. extern dev_proc_fill_rectangle(gdev_pdf_fill_rectangle); /* in gdevpdfd.c */
  139. extern dev_proc_copy_mono(gdev_pdf_copy_mono);        /* in gdevpdfi.c */
  140. extern dev_proc_copy_color(gdev_pdf_copy_color);    /* in gdevpdfi.c */
  141. extern dev_proc_get_params(gdev_pdf_get_params);    /* in gdevpdfp.c */
  142. extern dev_proc_put_params(gdev_pdf_put_params);    /* in gdevpdfp.c */
  143. extern dev_proc_fill_path(gdev_pdf_fill_path);        /* in gdevpdfd.c */
  144. extern dev_proc_stroke_path(gdev_pdf_stroke_path);    /* in gdevpdfd.c */
  145. extern dev_proc_fill_mask(gdev_pdf_fill_mask);        /* in gdevpdfi.c */
  146. extern dev_proc_begin_image(gdev_pdf_begin_image);    /* in gdevpdfi.c */
  147. extern dev_proc_image_data(gdev_pdf_image_data);    /* in gdevpdfi.c */
  148. extern dev_proc_end_image(gdev_pdf_end_image);        /* in gdevpdfi.c */
  149.  
  150. #ifndef X_DPI
  151. #  define X_DPI 720
  152. #endif
  153. #ifndef Y_DPI
  154. #  define Y_DPI 720
  155. #endif
  156.  
  157. gx_device_pdf far_data gs_pdfwrite_device =
  158. {    std_device_color_stype_body(gx_device_pdf, 0, "pdfwrite",
  159.       &st_device_pdfwrite,
  160.       85*X_DPI/10, 110*Y_DPI/10, X_DPI, Y_DPI, 24, 255, 255),
  161.     {    pdf_open,
  162.         gx_upright_get_initial_matrix,
  163.         NULL,            /* sync_output */
  164.         pdf_output_page,
  165.         pdf_close,
  166.         gx_default_rgb_map_rgb_color,
  167.         gx_default_rgb_map_color_rgb,
  168.         gdev_pdf_fill_rectangle,
  169.         NULL,            /* tile_rectangle */
  170.         gdev_pdf_copy_mono,
  171.         gdev_pdf_copy_color,
  172.         NULL,            /* draw_line */
  173.         NULL,            /* get_bits */
  174.         gdev_pdf_get_params,
  175.         gdev_pdf_put_params,
  176.         NULL,            /* map_cmyk_color */
  177.         NULL,            /* get_xfont_procs */
  178.         NULL,            /* get_xfont_device */
  179.         NULL,            /* map_rgb_alpha_color */
  180.         gx_page_device_get_page_device,
  181.         NULL,            /* get_alpha_bits */
  182.         NULL,            /* copy_alpha */
  183.         NULL,            /* get_band */
  184.         NULL,            /* copy_rop */
  185.         gdev_pdf_fill_path,
  186.         gdev_pdf_stroke_path,
  187.         gdev_pdf_fill_mask,
  188.         NULL,            /* fill_trapezoid */
  189.         NULL,            /* fill_parallelogram */
  190.         NULL,            /* fill_triangle */
  191.         NULL,            /* draw_thin_line */
  192.         gdev_pdf_begin_image,
  193.         gdev_pdf_image_data,
  194.         gdev_pdf_end_image
  195.     },
  196.     psdf_initial_values(0 /*false*/),    /* (!ASCII85EncodePages) */
  197.     1.2,        /* CompatibilityLevel */
  198.     0/*false*/,    /* DoThumbnails */
  199.     1/*true*/,    /* ReAssignCharacters */
  200.     1/*true*/,    /* ReEncodeCharacters */
  201.     1,        /* FirstObjectNumber */
  202.     0/*false*/,    /* binary_ok */
  203.     pdf_compress_none,    /* compression */
  204.      { 0 },        /* tfname */
  205.     0,        /* tfile */
  206.      { 0 },        /* rfname */
  207.     0,        /* rfile */
  208.     0,        /* rstrm */
  209.     0,        /* rstrmbuf */
  210.     0,        /* rsave_strm */
  211.     0,        /* open_font */
  212.     0,        /* embedded_encoding_id */
  213.     0,        /* next_id */
  214.     0,        /* root_id */
  215.     0,        /* info_id */
  216.     0,        /* pages_id */
  217.     0,        /* outlines_id */
  218.     0,        /* next_page */
  219.     0,        /* contents_id */
  220.     pdf_in_none,    /* context */
  221.     0,        /* contents_length_id */
  222.     0,        /* contents_pos */
  223.     NoMarks,    /* procsets */
  224.     -1,        /* flatness */
  225.      { gx_line_params_initial },    /* line_params */
  226.      { pdf_text_state_default },    /* text */
  227.      { 0 },        /* space_char_ids */
  228.     0,        /* page_ids */
  229.     0,        /* num_page_ids */
  230.     0,        /* pages_referenced */
  231.      { { { 0 } } },    /* resources */
  232.     0,        /* annots */
  233.     0,        /* last_resource */
  234.      { 0, 0 },    /* catalog_string */
  235.      { 0, 0 },    /* pages_string */
  236.      { 0, 0 },    /* page_string */
  237.      { { { 0 } } },    /* outline_levels */
  238.     0,        /* outline_depth */
  239.     0,        /* closed_outline_depth */
  240.     0,        /* outlines_open */
  241.     0,        /* articles */
  242.     0        /* named_dests */
  243. };
  244.  
  245. /* ---------------- Utilities ---------------- */
  246.  
  247. /* ------ Document ------ */
  248.  
  249. /* Initialize the IDs allocated at startup. */
  250. void
  251. pdf_initialize_ids(gx_device_pdf *pdev)
  252. {    pdev->next_id = pdev->FirstObjectNumber;
  253.     pdev->root_id = pdf_obj_ref(pdev);
  254.     pdev->pages_id = pdf_obj_ref(pdev);
  255. }
  256.  
  257. /* Open the document if necessary. */
  258. void
  259. pdf_open_document(gx_device_pdf *pdev)
  260. {    if ( !is_in_document(pdev) && pdf_stell(pdev) == 0 )
  261.       { stream *s = pdev->strm;
  262.  
  263.         pprintd1(s, "%%PDF-1.%d\n",
  264.              (pdev->CompatibilityLevel >= 1.2 ? 2 : 1));
  265.         pdev->binary_ok = !pdev->params.ASCII85EncodePages;
  266.         if ( pdev->binary_ok )
  267.           pputs(s, "%\307\354\217\242\n");
  268.       }
  269.     /*
  270.      * Determine the compression method.  Currently this does nothing.
  271.      * It also isn't clear whether the compression method can now be
  272.      * changed in the course of the document.
  273.      *
  274.      * The following algorithm is per an update to TN # 5151 by
  275.      * Adobe Developer Support.
  276.      */
  277.     if ( !pdev->params.CompressPages )
  278.       pdev->compression = pdf_compress_none;
  279.     else if ( pdev->CompatibilityLevel < 1.2 )
  280.       pdev->compression = pdf_compress_LZW;
  281.     else if ( pdev->params.UseFlateCompression )
  282.       pdev->compression = pdf_compress_Flate;
  283.     else
  284.       pdev->compression = pdf_compress_LZW;
  285. }
  286.  
  287. /* ------ Objects ------ */
  288.  
  289. /* Allocate an object ID. */
  290. private long
  291. pdf_next_id(gx_device_pdf *pdev)
  292. {    return (pdev->next_id)++;
  293. }
  294.  
  295. /* Return the current position in the output. */
  296. /* Note that this may be in either the main file or the resource file. */
  297. long
  298. pdf_stell(gx_device_pdf *pdev)
  299. {    stream *s = pdev->strm;
  300.     long pos = stell(s);
  301.  
  302.     if ( s == pdev->rstrm )
  303.       pos += rfile_base_position;
  304.     return pos;
  305. }
  306.  
  307. /* Allocate an ID for a future object. */
  308. long
  309. pdf_obj_ref(gx_device_pdf *pdev)
  310. {    long id = pdf_next_id(pdev);
  311.     long pos = pdf_stell(pdev);
  312.  
  313.     fwrite(&pos, sizeof(pos), 1, pdev->tfile);
  314.     return id;
  315. }
  316.  
  317. /* Begin an object, optionally allocating an ID. */
  318. long
  319. pdf_open_obj(gx_device_pdf *pdev, long id)
  320. {    stream *s = pdev->strm;
  321.  
  322.     if ( id <= 0 )
  323.       { id = pdf_obj_ref(pdev);
  324.       }
  325.     else
  326.       { long pos = pdf_stell(pdev);
  327.         FILE *tfile = pdev->tfile;
  328.         long tpos = ftell(tfile);
  329.  
  330.         fseek(tfile, (id - pdev->FirstObjectNumber) * sizeof(pos),
  331.           SEEK_SET);
  332.         fwrite(&pos, sizeof(pos), 1, tfile);
  333.         fseek(tfile, tpos, SEEK_SET);
  334.       }
  335.     pprintld1(s, "%ld 0 obj\n", id);
  336.     return id;
  337. }
  338.  
  339. /* End an object. */
  340. int
  341. pdf_end_obj(gx_device_pdf *pdev)
  342. {    pputs(pdev->strm, "endobj\n");
  343.     return 0;
  344. }
  345.  
  346. /* ------ Graphics ------ */
  347.  
  348. /* Reset the graphics state parameters to initial values. */
  349. void
  350. pdf_reset_graphics(gx_device_pdf *pdev)
  351. {    color_set_pure(&pdev->fill_color, 0);    /* black */
  352.     color_set_pure(&pdev->stroke_color, 0);    /* ditto */
  353.     pdev->flatness = -1;
  354.     { static const gx_line_params lp_initial = { gx_line_params_initial };
  355.       pdev->line_params = lp_initial;
  356.     }
  357. }
  358.  
  359. /* Set the fill or stroke color. */
  360. int
  361. pdf_set_color(gx_device_pdf *pdev, gx_color_index color,
  362.   gx_drawing_color *pdcolor, const char *rgs)
  363. {    if ( gx_dc_pure_color(pdcolor) != color )
  364.       { int code;
  365.  
  366.         /*
  367.          * In principle, we can set colors in either stream or text
  368.          * context.  However, since we currently enclose all text
  369.          * strings inside a gsave/grestore, this causes us to lose
  370.          * track of the color when we leave text context.  Therefore,
  371.          * we require stream context for setting colors.
  372.          */
  373. #if 0
  374.         switch ( pdev->context )
  375.           {
  376.           case pdf_in_stream:
  377.           case pdf_in_text:
  378.         break;
  379.           case pdf_in_none:
  380.         code = pdf_open_page(pdev, pdf_in_stream);
  381.         goto open;
  382.           case pdf_in_string:
  383.         code = pdf_open_page(pdev, pdf_in_text);
  384. open:        if ( code < 0 )
  385.           return code;
  386.           }
  387. #else
  388.         code = pdf_open_page(pdev, pdf_in_stream);
  389.         if ( code < 0 )
  390.           return code;
  391. #endif
  392.         color_set_pure(pdcolor, color);
  393.         psdf_set_color((gx_device_vector *)pdev, pdcolor, rgs);
  394.       }
  395.     return 0;
  396. }
  397.  
  398. /* Write matrix values. */
  399. void
  400. pdf_put_matrix(gx_device_pdf *pdev, const char *before,
  401.   const gs_matrix *pmat, const char *after)
  402. {    stream *s = pdev->strm;
  403.  
  404.     if ( before )
  405.       pputs(s, before);
  406.     pprintg6(s, "%g %g %g %g %g %g ",
  407.          pmat->xx, pmat->xy, pmat->yx, pmat->yy, pmat->tx, pmat->ty);
  408.     if ( after )
  409.       pputs(s, after);
  410. }
  411.  
  412. /*
  413.  * Write a string in its shortest form ( () or <> ).  Note that
  414.  * this form is different depending on whether binary data are allowed.
  415.  * We wish PDF supported ASCII85 strings ( <~ ~> ), but it doesn't.
  416.  */
  417. void
  418. pdf_put_string(gx_device_pdf *pdev, const byte *str, uint size)
  419. {    uint added = 0;
  420.     uint i;
  421.     const stream_template *template;
  422.     stream *s = pdev->strm;
  423.  
  424.     if ( pdev->binary_ok )
  425.       { /* Only need to escape (, ), \, CR, EOL. */
  426.         pputc(s, '(');
  427.         for ( i = 0; i < size; ++i )
  428.           { byte ch = str[i];
  429.  
  430.             switch ( ch )
  431.           {
  432.           case char_CR:
  433.             pputs(s, "\\r");
  434.             continue;
  435.           case char_EOL:
  436.             pputs(s, "\\n");
  437.             continue;
  438.           case '(': case ')': case '\\':
  439.             pputc(s, '\\');
  440.           }
  441.         pputc(s, ch);
  442.           }
  443.         pputc(s, ')');
  444.         return;
  445.       }
  446.  
  447.     for ( i = 0; i < size; ++i )
  448.       { byte ch = str[i];
  449.  
  450.         if ( ch == 0 || ch >= 127 )
  451.           added += 3;
  452.         else if ( strchr("()\\\n\r\t\b\f", ch) != 0 )
  453.           ++added;
  454.         else if ( ch < 32 )
  455.           added += 3;
  456.       }
  457.  
  458.     if ( added < size )
  459.       { /* More efficient to represent as PostScript string. */
  460.         template = &s_PSSE_template;
  461.         pputc(s, '(');
  462.       }
  463.     else
  464.       { /* More efficient to represent as hex string. */
  465.         template = &s_AXE_template;
  466.         pputc(s, '<');
  467.       }
  468.  
  469.     { byte buf[100];        /* size is arbitrary */
  470.       stream_cursor_read r;
  471.       stream_cursor_write w;
  472.       int status;
  473.  
  474.       r.ptr = str - 1;
  475.       r.limit = r.ptr + size;
  476.       w.limit = buf + sizeof(buf) - 1;
  477.       do
  478.         { w.ptr = buf - 1;
  479.           status =
  480.         (*template->process)(NULL, &r, &w, true);
  481.           pwrite(s, buf, (uint)(w.ptr + 1 - buf));
  482.         }
  483.       while ( status == 1 );
  484.     }
  485. }
  486.  
  487. /* ------ Page contents ------ */
  488.  
  489. private const stream_procs filter_write_procs =
  490. {    s_std_noavailable, s_std_noseek, s_std_write_reset,
  491.     s_std_write_flush, s_filter_close
  492. };
  493.  
  494. /* Handle transitions between contexts. */
  495. private int
  496.   none_to_stream(P1(gx_device_pdf *)),
  497.   stream_to_text(P1(gx_device_pdf *)),
  498.   string_to_text(P1(gx_device_pdf *)),
  499.   text_to_stream(P1(gx_device_pdf *)),
  500.   stream_to_none(P1(gx_device_pdf *));
  501. private int (*context_procs[4][4])(P1(gx_device_pdf *)) = {
  502.   {0, none_to_stream, none_to_stream, none_to_stream},
  503.   {stream_to_none, 0, stream_to_text, stream_to_text},
  504.   {text_to_stream, text_to_stream, 0, 0},
  505.   {string_to_text, string_to_text, string_to_text, 0}
  506. };
  507. /* Enter stream context. */
  508. private int
  509. none_to_stream(gx_device_pdf *pdev)
  510. {    stream *s;
  511.  
  512.     if ( pdev->contents_id != 0 )
  513.       return_error(gs_error_Fatal); /* only 1 contents per page */
  514.     pdev->contents_id = pdf_begin_obj(pdev);
  515.     pdev->contents_length_id = pdf_obj_ref(pdev);
  516.     s = pdev->strm;
  517.     pprintld1(s, "<</Length %ld 0 R", pdev->contents_length_id);
  518.     if ( pdev->compression == pdf_compress_Flate )
  519.       pprints1(s, "/Filter /%s", compression_filter_name);
  520.     pputs(s, ">>\nstream\n");
  521.     pdev->contents_pos = pdf_stell(pdev);
  522.     if ( pdev->compression == pdf_compress_Flate )
  523.       { /* Set up the Flate filter. */
  524.         const stream_template *template = &compression_filter_template;
  525.         stream *es = s_alloc(pdev->pdf_memory, "PDF compression stream");
  526.         byte *buf = gs_alloc_bytes(pdev->pdf_memory, sbuf_size,
  527.                        "PDF compression buffer");
  528.         compression_filter_state *st =
  529.           gs_alloc_struct(pdev->pdf_memory, compression_filter_state,
  530.                   template->stype, "PDF compression state");
  531.  
  532.         if ( es == 0 || st == 0 || buf == 0 )
  533.           return_error(gs_error_VMerror);
  534.         s_std_init(es, buf, sbuf_size, &filter_write_procs,
  535.                s_mode_write);
  536.         st->memory = pdev->pdf_memory;
  537.         st->template = template;
  538.         es->state = (stream_state *)st;
  539.         es->procs.process = template->process;
  540.         es->strm = s;
  541.         (*template->set_defaults)((stream_state *)st);
  542.         (*template->init)((stream_state *)st);
  543.         pdev->strm = s = es;
  544.       }
  545.     /* Scale the coordinate system. */
  546.     pprintg2(s, "%g 0 0 %g 0 0 cm\n",
  547.          72.0 / pdev->HWResolution[0], 72.0 / pdev->HWResolution[1]);
  548.     /* Do a level of gsave for the clipping path. */
  549.     pputs(s, "q\n");
  550.     return pdf_in_stream;
  551. }
  552. /* Enter text context from stream context. */
  553. private int
  554. stream_to_text(gx_device_pdf *pdev)
  555. {    /*
  556.      * Bizarrely enough, Acrobat Reader cares how the final font size is
  557.      * obtained -- the CTM (cm), text matrix (Tm), and font size (Tf)
  558.      * are *not* all equivalent.  In particular, it seems to use the
  559.      * product of the text matrix and font size to decide how to
  560.      * anti-alias characters.  Therefore, we have to temporarily patch
  561.      * the CTM so that the scale factors are unity.  What a nuisance!
  562.      */
  563.     pprintg2(pdev->strm, "q %g 0 0 %g 0 0 cm BT\n",
  564.          pdev->HWResolution[0] / 72.0, pdev->HWResolution[1] / 72.0);
  565.     pdev->procsets |= Text;
  566.     gs_make_identity(&pdev->text.matrix);
  567.     pdev->text.line_start.x = pdev->text.line_start.y = 0;
  568.     pdev->text.buffer_count = 0;
  569.     return pdf_in_text;
  570. }
  571. /* Exit string context to text context. */
  572. private int
  573. string_to_text(gx_device_pdf *pdev)
  574. {    pdf_put_string(pdev, pdev->text.buffer, pdev->text.buffer_count);
  575.     pputs(pdev->strm, "Tj\n");
  576.     pdev->text.buffer_count = 0;
  577.     return pdf_in_text;
  578. }
  579. /* Exit text context to stream context. */
  580. private int
  581. text_to_stream(gx_device_pdf *pdev)
  582. {    pputs(pdev->strm, "ET Q\n");
  583.     pdev->text.font = 0;    /* because of Q */
  584.     return pdf_in_stream;
  585. }
  586. /* Exit stream context. */
  587. private int
  588. stream_to_none(gx_device_pdf *pdev)
  589. {    stream *s = pdev->strm;
  590.     long length;
  591.  
  592.     if ( pdev->compression == pdf_compress_Flate )
  593.       { /* Terminate the Flate filter. */
  594.         stream *fs = s->strm;
  595.  
  596.         sclose(s);
  597.         gs_free_object(pdev->pdf_memory, s->cbuf, "zlib buffer");
  598.         gs_free_object(pdev->pdf_memory, s, "zlib stream");
  599.         pdev->strm = s = fs;
  600.       }
  601.     length = pdf_stell(pdev) - pdev->contents_pos;
  602.     pputs(s, "endstream\n");
  603.     pdf_end_obj(pdev);
  604.     pdf_open_obj(pdev, pdev->contents_length_id);
  605.     pprintld1(s, "%ld\n", length);
  606.     pdf_end_obj(pdev);
  607.     return pdf_in_none;
  608. }
  609.  
  610. /* Begin a page contents part. */
  611. int
  612. pdf_open_contents(gx_device_pdf *pdev, pdf_context context)
  613. {    int (*proc)(P1(gx_device_pdf *));
  614.  
  615.     while ( (proc = context_procs[pdev->context][context]) != 0 )
  616.       { int code = (*proc)(pdev);
  617.         if ( code < 0 )
  618.           return code;
  619.         pdev->context = (pdf_context)code;
  620.       }
  621.     pdev->context = context;
  622.     return 0;
  623. }
  624.  
  625. /* Close the current contents part if we are in one. */
  626. int
  627. pdf_close_contents(gx_device_pdf *pdev, bool last)
  628. {    if ( pdev->context == pdf_in_none )
  629.       return 0;
  630.     if ( last )
  631.       { /* Exit from the clipping path gsave. */
  632.         pdf_open_contents(pdev, pdf_in_stream);
  633.         pputs(pdev->strm, "Q\n");
  634.         pdev->text.font = 0;
  635.       }
  636.     return pdf_open_contents(pdev, pdf_in_none);
  637. }
  638.  
  639. /* ------ Resources et al ------ */
  640.  
  641. /* Define the hash function for gs_ids. */
  642. #define gs_id_hash(rid) ((rid) + ((rid) / num_resource_chains))
  643.  
  644. /* Define the names of the resource types. */
  645. private const char *resource_names[] =
  646.  { pdf_resource_type_names };
  647.  
  648. /* Define the allocator descriptors for the resource types. */
  649. private const gs_memory_struct_type_t *resource_structs[] =
  650.  { pdf_resource_type_structs };
  651.  
  652. /* Find a resource of a given type by gs_id. */
  653. pdf_resource *
  654. pdf_find_resource_by_gs_id(gx_device_pdf *pdev, pdf_resource_type type,
  655.   gs_id rid)
  656. {    pdf_resource **pchain =
  657.       &pdev->resources[type].chains[gs_id_hash(rid) % num_resource_chains];
  658.     pdf_resource **pprev = pchain;
  659.     pdf_resource *pres;
  660.  
  661.     for ( ;    (pres = *pprev) != 0; pprev = &pres->next )
  662.       if ( pres->rid == rid ) {
  663.         if ( pprev != pchain ) {
  664.           *pprev = pres->next;
  665.           pres->next = *pchain;
  666.           *pchain = pres;
  667.         }
  668.         return pres;
  669.       }
  670.     return 0;
  671. }
  672.  
  673. /* Begin an object logically separate from the contents. */
  674. long
  675. pdf_open_separate(gx_device_pdf *pdev, long id)
  676. {    pdf_open_document(pdev);
  677.     pdev->rsave_strm = pdev->strm;
  678.     pdev->strm = pdev->rstrm;
  679.     return pdf_open_obj(pdev, id);
  680. }
  681.  
  682. /* Begin an aside (resource, annotation, ...). */
  683. private int
  684. pdf_alloc_aside(gx_device_pdf *pdev, pdf_resource **plist,
  685.   const gs_memory_struct_type_t *pst, pdf_resource **ppres)
  686. {    pdf_resource *pres;
  687.  
  688.     if ( pst == NULL )
  689.       pst = &st_pdf_resource;
  690.     pres =
  691.       gs_alloc_struct(pdev->pdf_memory, pdf_resource, pst, "begin_aside");
  692.     if ( pres == 0 )
  693.       return_error(gs_error_VMerror);
  694.     pres->next = *plist;
  695.     *plist = pres;
  696.     pres->prev = pdev->last_resource;
  697.     pdev->last_resource = pres;
  698.     *ppres = pres;
  699.     return 0;
  700. }
  701. int
  702. pdf_begin_aside(gx_device_pdf *pdev, pdf_resource **plist,
  703.   const gs_memory_struct_type_t *pst, pdf_resource **ppres)
  704. {    long id = pdf_begin_separate(pdev);
  705.     int code;
  706.  
  707.     if ( id < 0 )
  708.       return id;
  709.     code = pdf_alloc_aside(pdev, plist, pst, ppres);
  710.     if ( code < 0 )
  711.       return code;
  712.     (*ppres)->id = id;
  713.     return 0;
  714. }
  715.  
  716. /* Begin a resource of a given type. */
  717. int
  718. pdf_begin_resource(gx_device_pdf *pdev, pdf_resource_type type, gs_id rid,
  719.   pdf_resource **ppres)
  720. {    int code = pdf_begin_aside(pdev,
  721.       &pdev->resources[type].chains[gs_id_hash(rid) % num_resource_chains],
  722.       resource_structs[type], ppres);
  723.  
  724.     if ( code < 0 )
  725.       return code;
  726.     if ( resource_names[type] != 0 )
  727.       { stream *s = pdev->strm;
  728.  
  729.         pprints1(s, "<< /Type /%s", resource_names[type]);
  730.         pprintld1(s, " /Name /R%ld", (*ppres)->id);
  731.       }
  732.     return code;
  733. }
  734.  
  735. /* Allocate a resource, but don't open the stream. */
  736. int
  737. pdf_alloc_resource(gx_device_pdf *pdev, pdf_resource_type type, gs_id rid,
  738.   pdf_resource **ppres)
  739. {    int code = pdf_alloc_aside(pdev,
  740.       &pdev->resources[type].chains[gs_id_hash(rid) % num_resource_chains],
  741.       resource_structs[type], ppres);
  742.  
  743.     if ( code < 0 )
  744.       return code;
  745.     (*ppres)->id = pdf_obj_ref(pdev);
  746.     return 0;
  747. }
  748.  
  749. /* End an aside or other separate object. */
  750. int
  751. pdf_end_aside(gx_device_pdf *pdev)
  752. {    int code = pdf_end_obj(pdev);
  753.  
  754.     pdev->strm = pdev->rsave_strm;
  755.     return code;
  756. }
  757. /* End a resource. */
  758. int
  759. pdf_end_resource(gx_device_pdf *pdev)
  760. {    return pdf_end_aside(pdev);
  761. }
  762.  
  763. /* ------ Pages ------ */
  764.  
  765. /* Reset the state of the current page. */
  766. void
  767. pdf_reset_page(gx_device_pdf *pdev, bool first_page)
  768. {    pdev->contents_id = 0;
  769.     pdf_reset_graphics(pdev);
  770.     pdev->procsets = NoMarks;
  771.     { int i, j;
  772.       for ( i = 0; i < num_resource_types; ++i )
  773.         if ( first_page ||
  774.          !(i == resourceFont || resource_names[i] == 0)
  775.            )
  776.           for ( j = 0; j < num_resource_chains; ++j )
  777.         pdev->resources[i].chains[j] = 0;
  778.     }
  779.     pdev->page_string.data = 0;
  780.     { static const pdf_text_state text_default =
  781.        { pdf_text_state_default };
  782.       pdev->text = text_default;
  783.     }
  784. }
  785.  
  786. /* Get or assign the ID for a page. */
  787. /* Returns 0 if the page number is out of range. */
  788. long
  789. pdf_page_id(gx_device_pdf *pdev, int page_num)
  790. {    long page_id;
  791.  
  792.     if ( page_num >= pdev->num_page_ids )
  793.       { /* Grow the page_ids array. */
  794.         uint new_num_ids =
  795.           max(page_num + 10, pdev->num_page_ids << 1);
  796.         /* resize_object for a byte array takes a new object size */
  797.         /* in bytes.  This is a quirk of the API that we probably */
  798.         /* won't ever be able to fix.... */
  799.         long *new_ids = gs_resize_object(pdev->pdf_memory, pdev->page_ids,
  800.                          new_num_ids * sizeof(long),
  801.                          "pdf_page_id(resize page_ids)");
  802.  
  803.         if ( new_ids == 0 )
  804.           return 0;
  805.         pdev->page_ids = new_ids;
  806.         pdev->num_page_ids = new_num_ids;
  807.       }
  808.     if ( page_num < 1 )
  809.       return 0;
  810.     while ( page_num > pdev->pages_referenced )
  811.       pdev->page_ids[pdev->pages_referenced++] = 0;
  812.     if ( (page_id = pdev->page_ids[page_num - 1]) == 0 )
  813.       pdev->page_ids[page_num - 1] = page_id = pdf_obj_ref(pdev);
  814.     return page_id;
  815. }
  816.  
  817. /* Write saved page- or document-level information. */
  818. int
  819. pdf_write_saved_string(gx_device_pdf *pdev, gs_string *pstr)
  820. {    if ( pstr->data != 0 )
  821.       { pwrite(pdev->strm, pstr->data, pstr->size);
  822.         gs_free_string(pdev->pdf_memory, pstr->data, pstr->size,
  823.                "pdf_write_saved_string");
  824.         pstr->data = 0;
  825.       }
  826.     return 0;
  827. }
  828.  
  829. /* Open a page for writing. */
  830. int
  831. pdf_open_page(gx_device_pdf *pdev, pdf_context context)
  832. {    if ( !is_in_page(pdev) )
  833.       { if ( pdf_page_id(pdev, pdev->next_page + 1) == 0 )
  834.           return_error(gs_error_VMerror);
  835.         pdf_open_document(pdev);
  836.       }
  837.     /* Note that context may be pdf_in_none here. */
  838.     return pdf_open_contents(pdev, context);
  839. }
  840.  
  841. /* Close the current page. */
  842. private int
  843. pdf_close_page(gx_device_pdf *pdev)
  844. {    stream *s;
  845.     int page_num = ++(pdev->next_page);
  846.     long page_id;
  847.  
  848.     /* If the very first page is blank, we need to open the document */
  849.     /* before doing anything else. */
  850.     pdf_open_document(pdev);
  851.     pdf_close_contents(pdev, true);
  852.     page_id = pdf_page_id(pdev, page_num);
  853.     pdf_open_obj(pdev, page_id);
  854.     s = pdev->strm;
  855.     pprintd2(s, "<<\n/Type /Page\n/MediaBox [0 0 %d %d]\n",
  856.          (int)(pdev->MediaSize[0]), (int)(pdev->MediaSize[1]));
  857.     pprintld1(s, "/Parent %ld 0 R\n", pdev->pages_id);
  858.     pputs(s, "/Resources << /ProcSet [/PDF");
  859.     if ( pdev->procsets & ImageB )
  860.       pputs(s, " /ImageB");
  861.     if ( pdev->procsets & ImageC )
  862.       pputs(s, " /ImageC");
  863.     if ( pdev->procsets & ImageI )
  864.       pputs(s, " /ImageI");
  865.     if ( pdev->procsets & Text )
  866.       pputs(s, " /Text");
  867.     pputs(s, "]\n");
  868.     { int i;
  869.  
  870.       for ( i = 0; i < num_resource_types; ++i )
  871.        if ( !(i == resourceFont || resource_names[i] == 0) )
  872.         { bool first = true;
  873.           int j;
  874.           const pdf_resource *pres;
  875.  
  876.           for ( j = 0; j < num_resource_chains; ++j ) {
  877.         for ( pres = pdev->resources[i].chains[j];
  878.               pres != 0; pres = pres->next
  879.             ) {
  880.           if ( first )
  881.             pprints1(s, "/%s<<", resource_names[i]), first = false;
  882.           pprintld2(s, "/R%ld\n%ld 0 R", pres->id, pres->id);
  883.         }
  884.         pdev->resources[i].chains[j] = 0;
  885.           }
  886.           if ( !first )
  887.         pputs(s, ">>\n");
  888.         }
  889.     }
  890.     /* Put out references to just those fonts used on this page. */
  891.     { bool first = true;
  892.       int j;
  893.       pdf_font *font;
  894.  
  895.       for ( j = 0; j < num_resource_chains; ++j )
  896.         for ( font = (pdf_font *)pdev->resources[resourceFont].chains[j];
  897.           font != 0; font = font->next
  898.         )
  899.           if ( font->used_on_page ) {
  900.         if ( first )
  901.           pputs(s, "/Font <<\n"), first = false;
  902.             if ( font->frname[0] )
  903.           pprints1(s, "/%s", font->frname);
  904.         else
  905.           pprintld1(s, "/R%ld", font->id);
  906.         pprintld1(s, " %ld 0 R\n", font->id);
  907.         font->used_on_page = false;
  908.           }
  909.       if ( !first )
  910.         pputs(s, ">>\n");
  911.     }
  912.     pputs(s, ">>\n");
  913.     if ( pdev->contents_id == 0 )
  914.       pputs(s, "/Contents []\n");
  915.     else
  916.       pprintld1(s, "/Contents %ld 0 R\n", pdev->contents_id);
  917.     pdf_write_saved_string(pdev, &pdev->page_string);
  918.     { const pdf_resource *pres = pdev->annots;
  919.       bool any = false;
  920.       for ( ; pres != 0; pres = pres->next )
  921.         if ( pres->rid == page_num - 1 )
  922.           { if ( !any )
  923.           { pputs(s, "/Annots [\n");
  924.             any = true;
  925.           }
  926.         pprintld1(s, "%ld 0 R\n", pres->id);
  927.           }
  928.       if ( any )
  929.         pputs(s, "]\n");
  930.     }
  931.     pputs(s, ">>\n");
  932.     pdf_end_obj(pdev);
  933.     pdf_reset_page(pdev, false);
  934.     return 0;
  935. }
  936.  
  937. /* Write the default entries of the Info dictionary. */
  938. int
  939. pdf_write_default_info(gx_device_pdf *pdev)
  940. {    stream *s = pdev->strm;
  941.     /* Reading the time without using time_t is a challenge.... */
  942.     long t[2];    /* time_t can't be longer than 2 longs. */
  943.     struct tm ltime;
  944.     char buf[20];
  945.  
  946.     time((void *)t);
  947.     ltime = *localtime((void *)t);
  948.     sprintf(buf, "%04d%02d%02d%02d%02d%02d",
  949.         ltime.tm_year + 1900, ltime.tm_mon + 1, ltime.tm_mday,
  950.         ltime.tm_hour, ltime.tm_min, ltime.tm_sec);
  951.     pprints1(s, "/CreationDate (D:%s)\n", buf);
  952.     sprintf(buf, "%1.2f", gs_revision / 100.0);
  953.     pprints2(s, "/Producer (%s %s)\n", gs_product, buf);
  954.     return 0;
  955. }
  956.  
  957. /* ---------------- Device open/close ---------------- */
  958.  
  959. /* Close and remove temporary files. */
  960. private void
  961. pdf_close_files(gx_device_pdf *pdev)
  962. {    gs_free_object(pdev->pdf_memory, pdev->rstrmbuf,
  963.                "pdf_close_files(rstrmbuf)");
  964.     pdev->rstrmbuf = 0;
  965.     gs_free_object(pdev->pdf_memory, pdev->rstrm,
  966.                "pdf_close_files(rstrm)");
  967.     pdev->rstrm = 0;
  968.     if ( pdev->rfile != 0 )
  969.       { fclose(pdev->rfile);
  970.         pdev->rfile = 0;
  971.         unlink(pdev->rfname);
  972.       }
  973.     if ( pdev->tfile != 0 )
  974.       { fclose(pdev->tfile);
  975.         pdev->tfile = 0;
  976.         unlink(pdev->tfname);
  977.       }
  978. }
  979.  
  980. /* Open the device. */
  981. private int
  982. pdf_open(gx_device *dev)
  983. {    gx_device_pdf *pdev = (gx_device_pdf *)dev;
  984.     char fmode[4];
  985.     int code;
  986.  
  987.     pdev->pdf_memory = &gs_memory_default;        /* as good as any */
  988.     strcpy(fmode, "w+");
  989.     strcat(fmode, gp_fmode_binary_suffix);
  990.     pdev->tfile =
  991.       gp_open_scratch_file(gp_scratch_file_name_prefix,
  992.                    pdev->tfname, fmode);
  993.     if ( pdev->tfile == 0 )
  994.       return_error(gs_error_invalidfileaccess);
  995.     pdev->rfile =
  996.       gp_open_scratch_file(gp_scratch_file_name_prefix,
  997.                    pdev->rfname, fmode);
  998.     if ( pdev->rfile == 0 )
  999.       { code = gs_note_error(gs_error_invalidfileaccess);
  1000.         goto fail;
  1001.       }
  1002.     pdev->rstrm = s_alloc(pdev->pdf_memory, "pdf_open(rstrm)");
  1003.     pdev->rstrmbuf = gs_alloc_bytes(pdev->pdf_memory, sbuf_size,
  1004.                     "pdf_open(rstrmbuf)");
  1005.     if ( pdev->rstrm == 0 || pdev->rstrmbuf == 0 )
  1006.       { code = gs_note_error(gs_error_VMerror);
  1007.         goto fail;
  1008.       }
  1009.     swrite_file(pdev->rstrm, pdev->rfile, pdev->rstrmbuf, sbuf_size);
  1010.     code = gdev_vector_open_file((gx_device_vector *)pdev, sbuf_size);
  1011.     if ( code < 0 )
  1012.       goto fail;
  1013.     gdev_vector_init((gx_device_vector *)pdev);
  1014.     /* Set in_page so the vector routines won't try to call */
  1015.     /* any vector implementation procedures. */
  1016.     pdev->in_page = true;
  1017.     pdf_initialize_ids(pdev);
  1018.     pdev->outlines_id = 0;
  1019.     pdev->next_page = 0;
  1020.     memset(pdev->space_char_ids, 0, sizeof(pdev->space_char_ids));
  1021.     pdev->page_ids = (void *)
  1022.       gs_alloc_byte_array(pdev->pdf_memory, initial_num_page_ids,
  1023.                   sizeof(*pdev->page_ids), "pdf_open(page_ids)");
  1024.     if ( pdev->page_ids == 0 )
  1025.       { code = gs_error_VMerror;
  1026.         goto fail;
  1027.       }
  1028.     pdev->num_page_ids = initial_num_page_ids;
  1029.     pdev->pages_referenced = 0;
  1030.     pdev->catalog_string.data = 0;
  1031.     pdev->pages_string.data = 0;
  1032.     pdev->outline_levels[0].first.id = 0;
  1033.     pdev->outline_levels[0].left = max_int;
  1034.     pdev->outline_depth = 0;
  1035.     pdev->closed_outline_depth = 0;
  1036.     pdev->outlines_open = 0;
  1037.     pdev->articles = 0;
  1038.     pdev->named_dests = 0;
  1039.     pdf_reset_page(pdev, true);
  1040.  
  1041.     return 0;
  1042. fail:
  1043.     pdf_close_files(pdev);
  1044.     return code;
  1045. }
  1046.  
  1047. /* Wrap up ("output") a page. */
  1048. private int
  1049. pdf_output_page(gx_device *dev, int num_copies, int flush)
  1050. {    gx_device_pdf *pdev = (gx_device_pdf *)dev;
  1051.     return pdf_close_page(pdev);
  1052. }
  1053.  
  1054. /* Write out the CharProcs for an embedded font. */
  1055. /* We thought that Acrobat 2.x required this to be an indirect object, */
  1056. /* but we were wrong. */
  1057. private int
  1058. pdf_write_char_procs(gx_device_pdf *pdev, const pdf_font *pef,
  1059.   gs_int_rect *pbbox, int widths[256])
  1060. {    stream *s = pdev->strm;
  1061.     const pdf_char_proc *pcp;
  1062.     int w;
  1063.  
  1064.     pputs(s, "<<");
  1065.     /* Write real characters. */
  1066.     for ( pcp = pef->char_procs; pcp; pcp = pcp->char_next ) {
  1067.       pbbox->p.y = min(pbbox->p.y, pcp->y_offset);
  1068.       pbbox->q.x = max(pbbox->q.x, pcp->width);
  1069.       pbbox->q.y = max(pbbox->q.y, pcp->height + pcp->y_offset);
  1070.       widths[pcp->char_code] = pcp->x_width;
  1071.       pprintld2(s, "/a%ld\n%ld 0 R", (long)pcp->char_code, pcp->id);
  1072.     }
  1073.     /* Write space characters. */
  1074.     for ( w = 0; w < countof(pef->spaces); ++w ) {
  1075.       byte ch = pef->spaces[w];
  1076.       if ( ch ) {
  1077.         pprintld2(s, "/a%ld\n%ld 0 R", (long)ch,
  1078.               pdev->space_char_ids[w]);
  1079.         widths[ch] = w + x_space_min;
  1080.       }
  1081.     }
  1082.     pputs(s, ">>");
  1083.     return 0;
  1084. }
  1085.  
  1086. /* Write out the Widths for an embedded font similarly. */
  1087. private int
  1088. pdf_write_widths(gx_device_pdf *pdev, const pdf_font *pef, int widths[256])
  1089. {    stream *s = pdev->strm;
  1090.     int i;
  1091.  
  1092.     pputs(s, "[");
  1093.     for ( i = 0; i < pef->num_chars; ++i )
  1094.       pprintd1(s, (i & 15 ? " %d" : ("\n%d")), widths[i]);
  1095.     pputs(s, "]");
  1096.     return 0;
  1097. }
  1098.  
  1099. /* Close the device. */
  1100. private int
  1101. pdf_close(gx_device *dev)
  1102. {    gx_device_pdf *pdev = (gx_device_pdf *)dev;
  1103.     stream *s;
  1104.     FILE *tfile = pdev->tfile;
  1105.     long xref;
  1106.     long named_dests_id = 0;
  1107.     long resource_pos;
  1108.  
  1109.     /*
  1110.      * If this is an EPS file, or if the file has produced no marks
  1111.      * at all, we need to tidy up a little so as not to produce
  1112.      * illegal PDF.  We recognize EPS files as having some contents
  1113.      * but no showpage.
  1114.      */
  1115.     if ( pdev->next_page == 0 )
  1116.       { pdf_open_document(pdev);
  1117.         if ( pdev->contents_id != 0 )
  1118.           { pdf_close_page(pdev);
  1119.           }
  1120.       }
  1121.  
  1122.     /*
  1123.      * Write out fonts.  For base fonts, write the encoding
  1124.      * differences.
  1125.      */
  1126.  
  1127.     { int j;
  1128.       const pdf_font *pef;
  1129.  
  1130.       s = pdev->strm;
  1131.       for ( j = 0; j < num_resource_chains; ++j )
  1132.         for ( pef = (const pdf_font *)pdev->resources[resourceFont].chains[j];
  1133.           pef != 0; pef = pef->next
  1134.         ) {
  1135.           pdf_open_obj(pdev, pef->id);
  1136.           if ( font_is_embedded(pef) ) {
  1137.         gs_int_rect bbox;
  1138.         int widths[256];
  1139.  
  1140.         memset(&bbox, 0, sizeof(bbox));
  1141.         memset(widths, 0, sizeof(widths));
  1142.         pprints1(s, "<</Type/Font/Name/%s/Subtype/Type3", pef->frname);
  1143.         pprintld1(s, "/Encoding %ld 0 R", pdev->embedded_encoding_id);
  1144.         pprintd1(s, "/FirstChar 0/LastChar %d/CharProcs",
  1145.              pef->num_chars - 1);
  1146.         pdf_write_char_procs(pdev, pef, &bbox, widths);
  1147.         pprintd4(s, "/FontBBox[%d %d %d %d]/FontMatrix[1 0 0 1 0 0]/Widths",
  1148.              bbox.p.x, bbox.p.y, bbox.q.x, bbox.q.y);
  1149.         pdf_write_widths(pdev, pef, widths);
  1150.         pputs(s, ">>\n");
  1151.         pdf_end_obj(pdev);
  1152.           } else {
  1153.         pprintld1(s, "<</Type/Font/Name/R%ld/Subtype/Type1/BaseFont/",
  1154.               pef->id);
  1155.         pwrite(s, pef->fname.chars, pef->fname.size);
  1156.         if ( pef->differences != 0 )
  1157.           pprintld1(s, "/Encoding %ld 0 R", pef->diff_id);
  1158.         pputs(s, ">>\n");
  1159.         if ( pef->differences != 0 ) {
  1160.           int prev = 256;
  1161.           int i;
  1162.  
  1163.           pdf_end_obj(pdev);
  1164.           pdf_open_obj(pdev, pef->diff_id);
  1165.           pputs(s, "<</Type/Encoding/Differences[");
  1166.           for ( i = 0; i < 256; ++i )
  1167.             if ( pef->differences[i].data != 0 ) {
  1168.               if ( i != prev + 1 )
  1169.             pprintd1(s, "\n%d", i);
  1170.               pputs(s, "/");
  1171.               pwrite(s, pef->differences[i].data,
  1172.                  pef->differences[i].size);
  1173.               prev = i;
  1174.             }
  1175.           pputs(s, "]>>\n");
  1176.         }
  1177.           }
  1178.           pdf_end_obj(pdev);
  1179.         }
  1180.     }
  1181.  
  1182.     /* Create the root (Catalog). */
  1183.  
  1184.     pdf_open_obj(pdev, pdev->pages_id);
  1185.     pputs(s, "<< /Type /Pages /Kids [\n");
  1186.     { int i;
  1187.       for ( i = 0; i < pdev->next_page; ++i )
  1188.         pprintld1(s, "%ld 0 R\n", pdev->page_ids[i]);
  1189.     }
  1190.     pprintd1(s, "] /Count %d\n", pdev->next_page);
  1191.     pdf_write_saved_string(pdev, &pdev->pages_string);
  1192.     pputs(s, ">>\n");
  1193.     pdf_end_obj(pdev);
  1194.     if ( pdev->outlines_id != 0 )
  1195.       { pdfmark_close_outline(pdev);    /* depth must be zero! */
  1196.         pdf_open_obj(pdev, pdev->outlines_id);
  1197.         pprintd1(s, "<< /Count %d", pdev->outlines_open);
  1198.         pprintld2(s, " /First %ld 0 R /Last %ld 0 R >>\n",
  1199.               pdev->outline_levels[0].first.id,
  1200.               pdev->outline_levels[0].last.id);
  1201.         pdf_end_obj(pdev);
  1202.       }
  1203.     if ( pdev->articles != 0 )
  1204.       { pdf_article *part;
  1205.         /* Write the first and last beads of each article. */
  1206.         for ( part = pdev->articles; part != 0; part = part->next )
  1207.           { if ( part->last.id == 0 )
  1208.           { /* Only one bead in the article. */
  1209.             part->first.prev_id = part->first.next_id = part->first.id;
  1210.           }
  1211.         else
  1212.           { /* More than one bead in the article. */
  1213.             part->first.prev_id = part->last.id;
  1214.             part->last.next_id = part->first.id;
  1215.             pdfmark_write_article(pdev, &part->last);
  1216.           }
  1217.         pdfmark_write_article(pdev, &part->first);
  1218.           }
  1219.       }
  1220.     if ( pdev->named_dests != 0 )
  1221.       { pdf_named_dest *pnd;
  1222.         named_dests_id = pdf_begin_obj(pdev);
  1223.         pputs(s, "<<\n");
  1224.         while ( (pnd = pdev->named_dests) != 0 )
  1225.           { pdev->named_dests = pnd->next;
  1226.         pwrite(s, pnd->key.data, pnd->key.size);
  1227.         pprints1(s, " %s\n", pnd->dest);
  1228.         gs_free_string(pdev->pdf_memory, pnd->key.data, pnd->key.size,
  1229.                    "pdf_close(named_dest key)");
  1230.         gs_free_object(pdev->pdf_memory, pnd, "pdf_close(named_dest)");
  1231.           }
  1232.         pputs(s, ">>\n");
  1233.         pdf_end_obj(pdev);
  1234.       }
  1235.     pdf_open_obj(pdev, pdev->root_id);
  1236.     pprintld1(s, "<< /Type /Catalog /Pages %ld 0 R\n", pdev->pages_id);
  1237.     if ( pdev->outlines_id != 0 )
  1238.       pprintld1(s, "/Outlines %ld 0 R\n", pdev->outlines_id);
  1239.     if ( pdev->articles != 0 )
  1240.       { pdf_article *part;
  1241.         pputs(s, "/Threads [ ");
  1242.         while ( (part = pdev->articles) != 0 )
  1243.           { pdev->articles = part->next;
  1244.         pprintld1(s, "%ld 0 R\n", part->id);
  1245.         gs_free_string(pdev->pdf_memory, part->title.data,
  1246.                    part->title.size, "pdf_close(article title)");
  1247.         gs_free_object(pdev->pdf_memory, part, "pdf_close(article)");
  1248.           }
  1249.         pputs(s, "]\n");
  1250.       }
  1251.     if ( named_dests_id != 0 )
  1252.       pprintld1(s, "/Dests %ld 0 R\n", named_dests_id);
  1253.     pdf_write_saved_string(pdev, &pdev->catalog_string);
  1254.     pputs(s, ">>\n");
  1255.     pdf_end_obj(pdev);
  1256.  
  1257.     /* Create the Info directory. */
  1258.     /* This is supposedly optional, but some readers may require it. */
  1259.  
  1260.     if ( pdev->info_id == 0 )
  1261.       { pdev->info_id = pdf_begin_obj(pdev);
  1262.         pputs(s, "<< ");
  1263.         pdf_write_default_info(pdev);
  1264.         pputs(s, ">>\n");
  1265.         pdf_end_obj(pdev);
  1266.     }
  1267.  
  1268.     /* Copy the resources into the main file. */
  1269.  
  1270.     s = pdev->strm;
  1271.     resource_pos = stell(s);
  1272.     sflush(pdev->rstrm);
  1273.     { FILE *rfile = pdev->rfile;
  1274.       long res_end = ftell(rfile);
  1275.       byte buf[sbuf_size];
  1276.  
  1277.       fseek(rfile, 0L, SEEK_SET);
  1278.       while ( res_end > 0 )
  1279.         { uint count = min(res_end, sbuf_size);
  1280.  
  1281.           fread(buf, 1, sbuf_size, rfile);
  1282.           pwrite(s, buf, count);
  1283.           res_end -= count;
  1284.         }
  1285.     }
  1286.  
  1287.     /* Write the cross-reference section. */
  1288.  
  1289.     xref = pdf_stell(pdev);
  1290.     if ( pdev->FirstObjectNumber == 1 )
  1291.       pprintld1(s, "xref\n0 %ld\n0000000000 65535 f \n",
  1292.             pdev->next_id);
  1293.     else
  1294.       pprintld2(s, "xref\n0 1\n0000000000 65535 f \n%ld %ld\n",
  1295.             pdev->FirstObjectNumber,
  1296.             pdev->next_id - pdev->FirstObjectNumber);
  1297.     fseek(tfile, 0L, SEEK_SET);
  1298.     { long i;
  1299.       for ( i = pdev->FirstObjectNumber; i < pdev->next_id; ++i )
  1300.         { ulong pos;
  1301.           char str[21];
  1302.  
  1303.           fread(&pos, sizeof(pos), 1, tfile);
  1304.           if ( pos & rfile_base_position )
  1305.         pos += resource_pos - rfile_base_position;
  1306.           sprintf(str, "%010ld 00000 n \n", pos);
  1307.           pputs(s, str);
  1308.         }
  1309.     }
  1310.  
  1311.     /* Write the trailer. */
  1312.  
  1313.     pputs(s, "trailer\n");
  1314.     pprintld3(s, "<< /Size %ld /Root %ld 0 R /Info %ld 0 R\n",
  1315.           pdev->next_id, pdev->root_id, pdev->info_id);
  1316.     pputs(s, ">>\n");
  1317.     pprintld1(s, "startxref\n%ld\n%%%%EOF\n", xref);
  1318.  
  1319.     /* Release the resource records. */
  1320.  
  1321.     { pdf_resource *pres;
  1322.       pdf_resource *prev;
  1323.       for ( prev = pdev->last_resource; (pres = prev) != 0; )
  1324.         { prev = pres->prev;
  1325.           gs_free_object(pdev->pdf_memory, pres, "pdf_resource");
  1326.         }
  1327.       pdev->last_resource = 0;
  1328.     }
  1329.  
  1330.     gs_free_object(pdev->pdf_memory, pdev->page_ids, "page_ids");
  1331.     pdev->page_ids = 0;
  1332.     pdev->num_page_ids = 0;
  1333.  
  1334.     gdev_vector_close_file((gx_device_vector *)pdev);
  1335.     pdf_close_files(pdev);
  1336.     return 0;
  1337. }
  1338.