/* scs2pdf -- Converts scs from standard input into PDF.
* Copyright (C) 2000 Michael Madore
*
* This file is part of TN5250.
*
* TN5250 is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* TN5250 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
/* Modified by James Rich to follow the Adobe PDF-1.3 specification found
* at http://partners.adobe.com/asn/developer/acrosdk/docs/PDFRef.pdf
*/
#include "tn5250-private.h"
/*
#define DEBUG
#define VERBOSE
*/
/* Define a default page size of 8.5x11 inches. These numbers are a little
* magic. The iSeries sends a page size in terms of characters per inch,
* but PDF uses points. The ratio of the numbers below and 1440 is the
* same ratio as 612 and 72. So multiplying the ratio of numbers below over
* 1440 by 72 will result in a 8.5x11 inch page,
* i.e. (12240/1440) = (612/72) = 8.5 = page width
*
* Note that the 1440 constant came from Michael Madore.
*/
#define DEFAULT_PAGE_WIDTH 12240
#define DEFAULT_PAGE_LENGTH 15840
/* Define a maximum number of columns that fit in the default page size
* above. If the page size is not specified and we are given more characters
* to print on a line than MAX_COLUMNS we increase the page width.
*/
#define MAX_COLUMNS 80
/* Define font names with a number that the PDF can use. Numbers must be
* greater than 0, but it doesn't matter what numbers are chosen.
*/
#define COURIER 1
#define COURIER_BOLD 2
void scs2pdf_nl (Tn5250SCS * This);
void scs2pdf_pp (Tn5250SCS * This);
void scs2pdf_ff (Tn5250SCS * This);
int scs2pdf_ahpp (int *curpos, int *boldchars);
void scs2pdf_process2b (Tn5250SCS * This);
void scs2pdf_default (Tn5250SCS * This);
void do_newpage (Tn5250SCS * This);
int pdf_header ();
int pdf_catalog (int objnum, int outlinesobject, int pageobject);
int pdf_outlines (int objnum);
int pdf_begin_stream (int objnum, int fontname, int fontsize);
int pdf_end_stream ();
int pdf_stream_length (int objnum, int objlength);
int pdf_pages (int objnum, int pagechildren, int pages);
int pdf_page (int objnum, int parent, int contents, int procset, int font,
int boldfont, int pagewidth, int pagelength);
int pdf_procset (int objnum);
int pdf_font (int objnum, int fontname);
void pdf_xreftable (int objnum);
void pdf_trailer (int offset, int size, int root);
int pdf_process_char (char character, int flush);
Tn5250SCS *tn5250_scs2pdf_new ();
struct _Tn5250SCSPrivate
{
int newfontsize;
unsigned long objcount;
unsigned long streamsize;
unsigned long filesize;
int fontsize;
unsigned int pagenumber;
int columncheck;
int boldchars;
int do_bold;
char text[255];
int newpage;
};
unsigned char nextchar;
Tn5250CharMap *map;
struct _expanding_array
{
int *data;
int elems;
int alloc;
};
typedef struct _expanding_array expanding_array;
expanding_array *array_new ();
void array_append_val (expanding_array * array, int value);
int array_index (expanding_array * array, int idx);
void array_free (expanding_array * array);
expanding_array *textobjects;
expanding_array *ObjectList;
FILE *outfile;
int
main ()
{
int pageparent, procsetobject, fontobject, boldfontobject, rootobject;
int i;
Tn5250SCS *scs = NULL;
ObjectList = array_new ();
textobjects = array_new ();
/* This allows the user to select an output file other than stdout.
* I don't know that this will ever be useful since you do pretty much
* anything with output redirection. Maybe some architectures will use
* this.
*/
if ((getenv ("TN5250_PDF")) != NULL)
{
outfile = fopen (getenv ("TN5250_PDF"), "w");
if (outfile == NULL)
{
fprintf (stderr, "Could not open output file.\n");
exit (-1);
}
}
else
{
outfile = stdout;
}
/* Get the appropriate CCSID map from the user. lp5250d will set this
* environment variable appropriately from the setting in the user's
* .tn5250rc file.
*/
if ((getenv ("TN5250_CCSIDMAP")) != NULL)
{
map = tn5250_char_map_new (getenv ("TN5250_CCSIDMAP"));
}
else
{
map = tn5250_char_map_new ("37");
}
/* Initialize the scs toolkit */
scs = tn5250_scs2pdf_new ();
if (scs == NULL)
{
return (-1);
}
scs->cpi = 0;
scs->data->newfontsize = 0;
scs->data->objcount = 0;
scs->data->streamsize = 0;
scs->data->filesize = 0;
scs->data->fontsize = 10;
scs->data->pagenumber = 1;
scs->column = 1;
scs->data->columncheck = 0;
scs->data->boldchars = 0;
scs->data->do_bold = 0;
scs->data->text[0] = '\0';
scs->data->newpage = 0;
/* Write out the PDF header. filesize tracks how big the PDF is since
* we need that information later.
*/
scs->data->filesize += pdf_header ();
/* ObjectList contains an entry for the filesize when the object was
* created. Since the cross reference of a PDF needs to know what the
* byte count is for the beginning of each object we use ObjectList to
* track it.
*/
array_append_val (ObjectList, scs->data->filesize);
scs->data->objcount++;
scs->data->filesize +=
pdf_begin_stream (scs->data->objcount, COURIER, scs->data->fontsize);
#ifdef DEBUG
fprintf (stderr, "objcount = %d\n", scs->data->objcount);
#endif
/* Turn control over to the SCS toolkit and run the event loop */
scs_main (scs);
array_append_val (textobjects, scs->data->objcount);
scs->data->streamsize += pdf_process_char ('\0', 1);
scs->data->filesize += scs->data->streamsize;
scs->data->filesize += pdf_end_stream ();
array_append_val (ObjectList, scs->data->filesize);
scs->data->objcount++;
scs->data->filesize +=
pdf_stream_length (scs->data->objcount, scs->data->streamsize);
#ifdef DEBUG
fprintf (stderr, "stream length objcount = %d\n", scs->data->objcount);
#endif
array_append_val (ObjectList, scs->data->filesize);
scs->data->objcount++;
scs->data->filesize += pdf_catalog (scs->data->objcount,
scs->data->objcount + 1,
scs->data->objcount + 5);
rootobject = scs->data->objcount;
#ifdef DEBUG
fprintf (stderr, "catalog objcount = %d\n", scs->data->objcount);
#endif
array_append_val (ObjectList, scs->data->filesize);
scs->data->objcount++;
scs->data->filesize += pdf_outlines (scs->data->objcount);
#ifdef DEBUG
fprintf (stderr, "outlines objcount = %d\n", scs->data->objcount);
#endif
array_append_val (ObjectList, scs->data->filesize);
scs->data->objcount++;
scs->data->filesize += pdf_procset (scs->data->objcount);
procsetobject = scs->data->objcount;
#ifdef DEBUG
fprintf (stderr, "procedure set objcount = %d\n", scs->data->objcount);
#endif
/* We need to create a font object for every font we use. Since we don't
* necessarily know if we used bold just make a bold font object anyway.
* It doesn't hurt to have objects that aren't used.
*/
array_append_val (ObjectList, scs->data->filesize);
scs->data->objcount++;
scs->data->filesize += pdf_font (scs->data->objcount, COURIER);
fontobject = scs->data->objcount;
#ifdef DEBUG
fprintf (stderr, "font objcount = %d\n", scs->data->objcount);
#endif
array_append_val (ObjectList, scs->data->filesize);
scs->data->objcount++;
scs->data->filesize += pdf_font (scs->data->objcount, COURIER_BOLD);
boldfontobject = scs->data->objcount;
#ifdef DEBUG
fprintf (stderr, "bold font objcount = %d\n", scs->data->objcount);
#endif
/* If the page size was not specified and we have lines longer than we
* think we can fit on a page increase the page width a half inch at a time.
*/
if ((scs->pagewidth == 0) && (scs->data->columncheck > MAX_COLUMNS))
{
for (i = 0; i < 15; i++)
{
if (scs->data->columncheck < (MAX_COLUMNS + (5 * i)))
{
#ifdef DEBUG
fprintf (stderr, "columncheck = %d, pagewidth = %d\n",
scs->data->columncheck, scs->pagewidth);
#endif
break;
}
scs->pagewidth = DEFAULT_PAGE_WIDTH + (720 * i);
}
}
array_append_val (ObjectList, scs->data->filesize);
scs->data->objcount++;
scs->data->filesize += pdf_pages (scs->data->objcount,
scs->data->objcount + 1,
scs->data->pagenumber);
pageparent = scs->data->objcount;
#ifdef DEBUG
fprintf (stderr, "pages objcount = %d\n", scs->data->objcount);
#endif
for (i = 0; i < scs->data->pagenumber; i++)
{
array_append_val (ObjectList, scs->data->filesize);
scs->data->objcount++;
scs->data->filesize += pdf_page (scs->data->objcount,
pageparent,
array_index (textobjects, i),
procsetobject, fontobject,
boldfontobject, scs->pagewidth,
scs->pagelength);
#ifdef DEBUG
fprintf (stderr, "page objcount = %d\n", scs->data->objcount);
#endif
}
pdf_xreftable (scs->data->objcount);
pdf_trailer (scs->data->filesize, scs->data->objcount + 1, rootobject);
array_free (textobjects);
array_free (ObjectList);
tn5250_char_map_destroy (map);
free (scs);
return (0);
}
/* This initializes the scs callbacks
*/
Tn5250SCS *
tn5250_scs2pdf_new ()
{
Tn5250SCS *scs = tn5250_scs_new ();
if (scs == NULL)
{
fprintf (stderr,
"Unable to allocate memory in tn5250_scs2pdf_new ()!\n");
return NULL;
}
scs->data = tn5250_new (struct _Tn5250SCSPrivate, 1);
if (scs->data == NULL)
{
free (scs);
return NULL;
}
/* And now set up our callbacks */
scs->pp = scs2pdf_pp;
scs->nl = scs2pdf_nl;
scs->rnl = scs2pdf_nl;
scs->ff = scs2pdf_ff;
scs->rff = scs2pdf_ff;
scs->process2b = scs2pdf_process2b;
scs->scsdefault = scs2pdf_default;
return scs;
}
void
scs2pdf_default (Tn5250SCS * This)
{
if (This->data->newpage == 1)
{
do_newpage (This);
}
This->data->streamsize +=
pdf_process_char (tn5250_char_map_to_local (map, This->curchar), 0);
/* If you want to feed this program non-EBCDIC text then uncomment
* the line below and comment the line above.
*
* streamsize += pdf_process_char(curchar, 0);
*/
This->column = This->column + 1;
/* If we are currently printing bold character then decrement the
* bold character count (boldchars) given above by
* scs2pdf_pp() until there aren't any bold characters left
* to print. When out of bold character reset the font and allow
* changing to bold again.
*/
if (This->data->boldchars > 0)
{
This->data->boldchars = This->data->boldchars - 1;
if (This->data->boldchars == 0)
{
#ifdef DEBUG
fprintf (stderr, "Ending bold font\n");
#endif
This->data->do_bold = 0;
This->data->streamsize += pdf_process_char ('\0', 1);
sprintf (This->data->text, "\t\t/F%d %d Tf\n",
COURIER, This->data->fontsize);
fprintf (outfile, "%s", This->data->text);
This->data->streamsize += strlen (This->data->text);
}
}
return;
}
/* Custom process 0x2B function */
void
scs2pdf_process2b (Tn5250SCS * This)
{
scs_process2b (This);
/* If scs_process2b() wants to change the CPI (newfontsize), flush
* the buffer and send the new font to the PDF. Note that right
* now we always reset the font to COURIER, even if we are using
* a different font. This will be changed soon.
*/
if (This->cpi > 0)
{
#ifdef DEBUG
fprintf (stderr, "Changing font size to %d\n", This->cpi);
#endif
This->data->streamsize += pdf_process_char ('\0', 1);
sprintf (This->data->text, "\t\t/F%d %d Tf\n", COURIER, This->cpi);
fprintf (outfile, "%s", This->data->text);
This->data->streamsize += strlen (This->data->text);
This->data->fontsize = This->cpi;
This->cpi = 0;
}
return;
}
/* Handle new lines uniquely to track column number
*/
void
scs2pdf_nl (Tn5250SCS * This)
{
scs_nl (This);
/* If the current position on the page (column) is further to the
* right than our current maximum line length (columncheck)
* increase our length. This is used later to see if we need a
* larger page width if the page width is unspecified.
*/
if (This->column > This->data->columncheck)
{
This->data->columncheck = This->column;
}
/* On newline flush the buffer and move the active line down
* 12 points.
*/
This->data->streamsize += pdf_process_char ('\0', 1);
fprintf (outfile, "0 -12 Td\n");
This->data->streamsize += 9;
This->column = 1;
return;
}
void
scs2pdf_ff (Tn5250SCS * This)
{
scs_ff (This);
This->data->newpage = 1;
return;
}
/* This function is different than what is in scs.c because we want to pass
* a pointer to the number of bold characters to print to scs2pdf_ahpp()
* (which is a unique version of scs_ahpp()). Since this program is the only
* one that handles bold this is unique.
*/
void
scs2pdf_pp (Tn5250SCS * This)
{
unsigned char curchar;
int bytes;
bytes = 0;
This->data->boldchars = 0;
curchar = fgetc (stdin);
switch (curchar)
{
case SCS_RDPP:
{
scs_rdpp (NULL);
break;
}
case SCS_AVPP:
{
scs_avpp (NULL);
break;
}
case SCS_AHPP:
{
bytes = scs2pdf_ahpp (&(This->column), &(This->data->boldchars));
break;
}
case SCS_RRPP:
{
scs_rrpp (NULL);
break;
}
default:
{
fprintf (stderr, "ERROR: Unknown 0x34 command %x\n", curchar);
}
}
This->data->streamsize += bytes;
/* If scs2pdf_ahpp() tells us that there are bold characters
* to write and we aren't already in the middle of writing some
* bold character, flush the buffer and send the bold font to the
* PDF.
*/
if ((This->data->boldchars > 0) && (This->data->do_bold == 0))
{
This->data->do_bold = 1;
#ifdef DEBUG
fprintf (stderr, "Starting bold font\n");
#endif
This->data->streamsize += pdf_process_char ('\0', 1);
sprintf (This->data->text, "\t\t/F%d %d Tf\n",
COURIER_BOLD, This->data->fontsize);
fprintf (outfile, "%s", This->data->text);
This->data->streamsize += strlen (This->data->text);
}
return;
}
/* This function is different than what is in scs.c because we want to pass
* a pointer to the number of bold characters to print. Since this program
* is the only one that handles bold this is unique.
*/
int
scs2pdf_ahpp (int *curpos, int *boldchars)
{
int position, bytes;
int i;
bytes = 0;
position = fgetc (stdin);
#ifdef DEBUG
fprintf (stderr, "AHPP %d (current position: %d)\n", position, *curpos);
#endif
if ((*curpos - 1) > position)
{
/* Frank Richter <frichter@esda.com> noticed that we should be
* going back and printing over the same line if *curpos is greater
* than position. Without this his reports were wrong. His patch
* fixes the printouts. What this does now is to go back to the
* beginning of the line and print blanks over what is already there
* up to position. At that point we will receive the same text that
* is already at position, which we will print over the top of itself.
* This is gives a bold effect on real SCS printers. This ought to
* be handled a better way to get bold.
*/
*boldchars = *curpos - position;
bytes += pdf_process_char ('\0', 1);
fprintf (outfile, "0 0 Td\n");
bytes += 7;
for (i = 0; i < position - 1; i++)
{
bytes += pdf_process_char (' ', 0);
}
}
else
{
for (i = 0; i < (position - *curpos); i++)
{
bytes += pdf_process_char (' ', 0);
}
}
*curpos = position;
return (bytes);
}
void
do_newpage (Tn5250SCS * This)
{
/* We need to know what stream objects (textobjects) were
* put on this page. We put one stream object on each
* page.
*/
array_append_val (textobjects, This->data->objcount);
This->data->streamsize += pdf_process_char ('\0', 1);
This->data->filesize += This->data->streamsize;
This->data->filesize += pdf_end_stream ();
array_append_val (ObjectList, This->data->filesize);
This->data->objcount = This->data->objcount + 1;
#ifdef DEBUG
fprintf (stderr, "objcount: %d\n", This->data->objcount);
#endif
This->data->filesize += pdf_stream_length (This->data->objcount,
This->data->streamsize);
This->data->streamsize = 0;
#ifdef DEBUG
fprintf (stderr, "objcount = %d\n", This->data->objcount);
#endif
/* Ending the stream object above and starting a new one
* here constitutes a new page, in conjuction with the
* pdf_page() function below.
*/
array_append_val (ObjectList, This->data->filesize);
This->data->objcount = This->data->objcount + 1;
This->data->filesize += pdf_begin_stream (This->data->objcount,
COURIER, This->data->fontsize);
#ifdef DEBUG
fprintf (stderr, "objcount = %d\n", This->data->objcount);
#endif
This->data->pagenumber++;
#ifdef DEBUG
fprintf (stderr, "pagenumber: %d\n", This->data->pagenumber);
#endif
This->column = 1;
This->data->newpage = 0;
return;
}
/* This header is required on all PDFs to identify what level of the PDF
* specification was used to create this PDF.
*/
int
pdf_header ()
{
char *text = "%PDF-1.3\n\n";
fprintf (outfile, "%s", text);
return (strlen (text));
}
/* This is required to tell the reader where to find stuff.*/
int
pdf_catalog (int objnum, int outlinesobject, int pageobject)
{
char text[255];
sprintf (text,
"%d 0 obj\n"
"\t<<\n"
"\t\t/Type /Catalog\n"
"\t\t/Outlines %d 0 R\n"
"\t\t/Pages %d 0 R\n"
"\t>>\n" "endobj\n\n", objnum, outlinesobject, pageobject);
fprintf (outfile, "%s", text);
return (strlen (text));
}
/* We don't really use outlines but they are required.*/
int
pdf_outlines (int objnum)
{
char text[255];
sprintf (text,
"%d 0 obj\n"
"\t<<\n"
"\t\t/Type Outlines\n"
"\t\t/Count 0\n" "\t>>\n" "endobj\n\n", objnum);
fprintf (outfile, "%s", text);
return (strlen (text));
}
/* Each time we begin a page we use this to start the stream object that the
* page will contain.
*/
int
pdf_begin_stream (int objnum, int fontname, int fontsize)
{
char text[255];
sprintf (text,
"%d 0 obj\n"
"\t<<\n"
"\t\t/Length %d 0 R\n"
"\t>>\n"
"stream\n"
"\tBT\n" "\t\t/F%d %d Tf\n" "\t\t36 756 Td\n",
objnum, objnum + 1, fontname, fontsize);
fprintf (outfile, "%s", text);
return (strlen (text));
}
/* And this ends the stream object started above.*/
int
pdf_end_stream ()
{
char *text = "\tET\n" "endstream\n" "endobj\n\n";
fprintf (outfile, "%s", text);
return (strlen (text));
}
/* Since we don't know how long the stream object is when we start it we use
* an indirect object to specify its length. That indirect object points to
* this function's output which is the length of the stream object created.
*/
int
pdf_stream_length (int objnum, int objlength)
{
char text[255];
/* Add 32 to objlength since that is how many characters pdf_begin_stream()
* and pdf_end_stream() add to the stream. These functions already add
* to the filesize correctly so we don't add 32 to filesize anywhere.
*/
sprintf (text, "%d 0 obj\n" "\t%d\n" "endobj\n\n", objnum, objlength + 32);
fprintf (outfile, "%s", text);
return (strlen (text));
}
/* This starts the page tree. We only have one root (this function) which
* contains all the page leaves (created by pdf_page()).
*/
int
pdf_pages (int objnum, int pagechildren, int pages)
{
char text[255];
int bytes;
int i;
sprintf (text,
"%d 0 obj\n" "\t<<\n" "\t\t/Type /Pages\n" "\t\t/Kids [", objnum);
fprintf (outfile, "%s", text);
bytes = strlen (text);
sprintf (text, " %d 0 R\n", pagechildren);
fprintf (outfile, "%s", text);
bytes += strlen (text);
for (i = 1; i < pages; i++)
{
sprintf (text, "\t\t %d 0 R\n", pagechildren + i);
fprintf (outfile, "%s", text);
bytes += strlen (text);
}
sprintf (text,
"\t\t ]\n" "\t\t/Count %d\n" "\t>>\n" "endobj\n\n", pages);
fprintf (outfile, "%s", text);
bytes += strlen (text);
return (bytes);
}
/* This describes the page size and contents for a page. This is called once
* for each page that is in the PDF.
*/
int
pdf_page (int objnum, int parent, int contents, int procset, int font,
int boldfont, int pagewidth, int pagelength)
{
char text[255];
float width, length;
/* PDF uses 72 points per inch so 8.5x11 in. page is 612x792 points
* You can set MediaBox to [0 0 612 792] to force this
*/
if (pagewidth == 0)
{
#ifdef DEBUG
fprintf (stderr, "No page width given, using default.\n");
#endif
pagewidth = DEFAULT_PAGE_WIDTH;
}
if (pagelength == 0)
{
#ifdef DEBUG
fprintf (stderr, "No page length given, using default.\n");
#endif
pagelength = DEFAULT_PAGE_LENGTH;
}
width = (pagewidth / 1440.0) * 72;
length = (pagelength / 1440.0) * 72;
#ifdef DEBUG
fprintf (stderr, "Setting page to %d (%f) by %d (%f) points\n",
(int) width, width, (int) length, length);
#endif
sprintf (text,
"%d 0 obj\n"
"\t<<\n"
"\t\t/Type /Page\n"
"\t\t/Parent %d 0 R\n"
"\t\t/MediaBox [0 0 %d %d]\n"
"\t\t/Contents %d 0 R\n"
"\t\t/Resources\n"
"\t\t\t<<\n"
"\t\t\t\t/ProcSet %d 0 R\n"
"\t\t\t\t/Font\n"
"\t\t\t\t\t<< /F%d %d 0 R\n"
"\t\t\t\t\t /F%d %d 0 R >>\n"
"\t\t\t>>\n"
"\t>>\n"
"endobj\n\n",
objnum, parent, (int) width, (int) length,
contents, procset, COURIER, font, COURIER_BOLD, boldfont);
fprintf (outfile, "%s", text);
return (strlen (text));
}
/* The required procedure set.*/
int
pdf_procset (int objnum)
{
char text[255];
sprintf (text, "%d 0 obj\n" "\t[/PDF /Text]\n" "endobj\n\n", objnum);
fprintf (outfile, "%s", text);
return (strlen (text));
}
/* This creates the font objects used in the PDF.*/
int
pdf_font (int objnum, int fontname)
{
char text[255];
switch (fontname)
{
case COURIER:
{
sprintf (text,
"%d 0 obj\n"
"\t<<\n"
"\t\t/Type /Font\n"
"\t\t/Subtype /Type1\n"
"\t\t/Name /F%d\n"
"\t\t/BaseFont /Courier\n"
"\t\t/Encoding /WinAnsiEncoding\n" "\t>>\n" "endobj\n\n",
objnum, fontname);
break;
}
case COURIER_BOLD:
{
sprintf (text,
"%d 0 obj\n"
"\t<<\n"
"\t\t/Type /Font\n"
"\t\t/Subtype /Type1\n"
"\t\t/Name /F%d\n"
"\t\t/BaseFont /Courier-Bold\n"
"\t\t/Encoding /WinAnsiEncoding\n" "\t>>\n" "endobj\n\n",
objnum, fontname);
break;
}
default:
{
sprintf (text,
"%d 0 obj\n"
"\t<<\n"
"\t\t/Type /Font\n"
"\t\t/Subtype /Type1\n"
"\t\t/Name /F%d\n"
"\t\t/BaseFont /Courier\n"
"\t\t/Encoding /WinAnsiEncoding\n" "\t>>\n" "endobj\n\n",
objnum, fontname);
break;
}
}
fprintf (outfile, "%s", text);
return (strlen (text));
}
/* The required cross reference table.*/
void
pdf_xreftable (int objnum)
{
int curobj;
/* This part is important to get right or the PDF cannot be read.
* The cross reference section always begins with the keyword 'xref'
*/
fprintf (outfile, "xref\n");
/* Then we follow with one or more cross reference subsections. Since
* this is always the first revision this cross reference will have no
* more than one subsection. The subsection numbering begins with 0.
* After the subsection number we must indicate how many entries (objects)
* are in this subsection.
*/
fprintf (outfile, "0 %d\n", objnum + 1);
/* The entries consist of a 10-digit byte offset (the number of bytes
* from the beginning of the file to the beginning of the object to
* which the entry refers), followed by a space, followed by a 5-digit
* generation number, followed by a two character end of line sequence
* (either carriage return and line feed or space and line feed).
* Object 0 is always free and always has a generation number of 65535
* so we list that first. We will never have more free entries.
*/
fprintf (outfile, "0000000000 65535 f \n");
/* The generation number will always be zeros for all in-use objects
* since we are not updating anything. We must have an entry for all
* objects we created.
*/
for (curobj = 0; curobj < objnum; curobj++)
{
fprintf (outfile, "%010d 00000 n \n", array_index (ObjectList, curobj));
}
}
/* And the required trailer.*/
void
pdf_trailer (int offset, int size, int root)
{
char text[255];
sprintf (text,
"\ntrailer\n"
"\t<<\n"
"\t\t/Size %d\n"
"\t\t/Root %d 0 R\n"
"\t>>\n" "startxref\n" "%d\n" "%%%%EOF\n", size, root, offset);
fprintf (outfile, "%s", text);
}
/* Here we process the characters given in the input stream (stdin). If
* flush is 1 then flush the buffer. The buffer is 255 bytes long since
* that is what Adobe recommends because of limitations of some operating
* environments.
*/
int
pdf_process_char (char character, int flush)
{
static int bufloc = 0;
static char buf[249];
int byteswritten;
byteswritten = 0;
if (character == '(' || character == ')')
{
byteswritten = pdf_process_char ('\\', 0);
}
if (bufloc >= 247 || flush == 1)
{
/* This should never happen */
if (bufloc > 247)
{
buf[247] = character;
buf[248] = '\0';
}
else
{
buf[bufloc] = character;
buf[bufloc + 1] = '\0';
}
fprintf (outfile, "(%s) Tj\n", buf);
byteswritten += strlen (buf);
memset (buf, '\0', 249);
bufloc = 0;
return (byteswritten + 6);
}
else
{
buf[bufloc] = character;
bufloc++;
return (byteswritten);
}
}
expanding_array *
array_new ()
{
expanding_array *array = NULL;
array = malloc (sizeof (expanding_array));
if (array == NULL)
return NULL;
array->data = NULL;
array->elems = 0;
array->alloc = 0;
return array;
}
void
array_append_val (expanding_array * array, int value)
{
int *newdata;
array->elems++;
if (array->elems > array->alloc)
{
array->alloc += 64;
if (array->data == NULL)
{
array->data = malloc (sizeof (int) * array->alloc);
}
else
{
newdata = realloc (array->data, sizeof (int) * array->alloc);
if (newdata == NULL)
free (array->data);
array->data = newdata;
}
if (array->data == NULL)
{
fprintf (stderr, "array_append_val: Out of memory!\n");
exit (1);
}
}
array->data[array->elems - 1] = value;
return;
}
int
array_index (expanding_array * array, int idx)
{
return array->data[idx];
}
void
array_free (expanding_array * array)
{
if (array->data != NULL)
{
free (array->data);
}
free (array);
array = NULL;
return;
}
syntax highlighted by Code2HTML, v. 0.9.1