aboutsummaryrefslogtreecommitdiffstats
path: root/z49.c
diff options
context:
space:
mode:
Diffstat (limited to 'z49.c')
-rw-r--r--z49.c299
1 files changed, 282 insertions, 17 deletions
diff --git a/z49.c b/z49.c
index 19fddda..4f90af1 100644
--- a/z49.c
+++ b/z49.c
@@ -1,6 +1,6 @@
/*@z49.c:PostScript Back End:PS_BackEnd@**************************************/
/* */
-/* THE LOUT DOCUMENT FORMATTING SYSTEM (VERSION 3.22) */
+/* THE LOUT DOCUMENT FORMATTING SYSTEM (VERSION 3.23) */
/* COPYRIGHT (C) 1991, 2000 Jeffrey H. Kingston */
/* */
/* Jeffrey H. Kingston (jeff@cs.usyd.edu.au) */
@@ -70,6 +70,131 @@ static FILE *out_fp; /* file to print PostScript on */
/*****************************************************************************/
/* */
+/* Data structures for checking links */
+/* */
+/* We keep a hash table of all dest points, and an ordinary list of all */
+/* source points. To check that no dest point appears twice, we consult */
+/* the hash table once for each dest point to ensure it is not already */
+/* there. To check that every source point has an dest, we run through */
+/* the list of source points at end of run and look each one up in the */
+/* dest point hash table. */
+/* */
+/*****************************************************************************/
+
+/*****************************************************************************/
+/* */
+/* LINK_DEST_TABLE */
+/* */
+/* A symbol table permitting access to link dest name objects. */
+/* The table will automatically enlarge to accept any number of entries. */
+/* */
+/* ltab_new(newsize) New empty table, newsize capacity */
+/* ltab_insert(x, &S) Insert new link dest name object x into S */
+/* ltab_retrieve(str, S) Retrieve link dest name object named str */
+/* ltab_debug(S, fp) Debug print of table S to file fp */
+/* */
+/*****************************************************************************/
+
+typedef struct
+{ int linktab_size; /* size of table */
+ int linktab_count; /* number of objects held */
+ OBJECT linktab_item[1];
+} *LINK_DEST_TABLE;
+
+#define ltab_size(S) (S)->linktab_size
+#define ltab_count(S) (S)->linktab_count
+#define ltab_item(S, i) (S)->linktab_item[i]
+
+#define hash(pos, str, S) \
+{ FULL_CHAR *p = str; \
+ pos = *p++; \
+ while( *p ) pos += *p++; \
+ pos = pos % ltab_size(S); \
+}
+
+static LINK_DEST_TABLE ltab_new(int newsize)
+{ LINK_DEST_TABLE S; int i;
+ /* ifdebug(DMA, D, DebugRegisterUsage(MEM_LINK_TAB, 1,
+ 2*sizeof(int) + newsize * sizeof(OBJECT))); */
+ S = (LINK_DEST_TABLE)
+ malloc(2*sizeof(int) + newsize * sizeof(OBJECT));
+ if( S == (LINK_DEST_TABLE) NULL )
+ Error(43, 1, "run out of memory enlarging link dest table", FATAL, no_fpos);
+ ltab_size(S) = newsize;
+ ltab_count(S) = 0;
+ for( i = 0; i < newsize; i++ ) ltab_item(S, i) = nilobj;
+ return S;
+} /* end ltab_new */
+
+static void ltab_insert(OBJECT x, LINK_DEST_TABLE *S);
+
+static LINK_DEST_TABLE ltab_rehash(LINK_DEST_TABLE S, int newsize)
+{ LINK_DEST_TABLE NewS; int i;
+ NewS = ltab_new(newsize);
+ for( i = 1; i <= ltab_size(S); i++ )
+ { if( ltab_item(S, i) != nilobj )
+ ltab_insert(ltab_item(S, i), &NewS);
+ }
+ free(S);
+ return NewS;
+} /* end ltab_rehash */
+
+static void ltab_insert(OBJECT x, LINK_DEST_TABLE *S)
+{ int pos; OBJECT z, link, y;
+ if( ltab_count(*S) == ltab_size(*S) - 1 ) /* one less since 0 unused */
+ *S = ltab_rehash(*S, 2*ltab_size(*S));
+ hash(pos, string(x), *S);
+ if( ltab_item(*S, pos) == nilobj ) New(ltab_item(*S, pos), ACAT);
+ z = ltab_item(*S, pos);
+ for( link = Down(z); link != z; link = NextDown(link) )
+ { Child(y, link);
+ if( StringEqual(string(x), string(y)) )
+ { Error(43, 2, "link name %s used twice (first at%s)",
+ WARN, &fpos(x), string(x), EchoFilePos(&fpos(y)));
+ }
+ }
+ Link(ltab_item(*S, pos), x);
+} /* end ltab_insert */
+
+static OBJECT ltab_retrieve(FULL_CHAR *str, LINK_DEST_TABLE S)
+{ OBJECT x, link, y; int pos;
+ hash(pos, str, S);
+ x = ltab_item(S, pos);
+ if( x == nilobj ) return nilobj;
+ for( link = Down(x); link != x; link = NextDown(link) )
+ { Child(y, link);
+ if( StringEqual(str, string(y)) ) return y;
+ }
+ return nilobj;
+} /* end ltab_retrieve */
+
+#if DEBUG_ON
+static void ltab_debug(LINK_DEST_TABLE S, FILE *fp)
+{ int i; OBJECT x, link, y;
+ fprintf(fp, " table size: %d; current number of keys: %d\n",
+ ltab_size(S), ltab_count(S));
+ for( i = 0; i < ltab_size(S); i++ )
+ { x = ltab_item(S, i);
+ fprintf(fp, "ltab_item(S, %d) =", i);
+ if( x == nilobj )
+ fprintf(fp, " <nilobj>");
+ else if( type(x) != ACAT )
+ fprintf(fp, " not ACAT!");
+ else for( link = Down(x); link != x; link = NextDown(link) )
+ { Child(y, link);
+ fprintf(fp, " %s",
+ is_word(type(y)) ? string(y) : AsciiToFull("not-WORD!"));
+ }
+ fprintf(fp, "\n");
+ }
+} /* end ltab_debug */
+#endif
+
+static LINK_DEST_TABLE link_dest_tab; /* the link dest names */
+static OBJECT link_source_list; /* the link source names */
+
+/*****************************************************************************/
+/* */
/* Print a number x on file fp. */
/* */
/*****************************************************************************/
@@ -106,6 +231,8 @@ static void PS_PrintInitialize(FILE *fp)
New(needs, ACAT);
New(supplied, ACAT);
debug0(DPO, DD, "PS_PrintInitialize returning.");
+ link_dest_tab = ltab_new(200);
+ New(link_source_list, ACAT);
} /* end PS_PrintInitialize */
@@ -388,10 +515,10 @@ static void PS_PrintBeforeFirstPage(FULL_LENGTH h, FULL_LENGTH v,
for( fnum = FirstFile(PREPEND_FILE); fnum != NO_FILE; fnum=NextFile(fnum) )
{ FULL_CHAR buff[MAX_BUFF]; FILE *fp;
if( (fp = OpenFile(fnum, FALSE, FALSE)) == null )
- Error(49, 1, "cannot open %s file %s", WARN, PosOfFile(fnum),
+ Error(49, 3, "cannot open %s file %s", WARN, PosOfFile(fnum),
KW_PREPEND, FileName(fnum));
else if( StringFGets(buff, MAX_BUFF, fp) == NULL )
- Error(49, 2, "%s file %s is empty", WARN, PosOfFile(fnum),
+ Error(49, 4, "%s file %s is empty", WARN, PosOfFile(fnum),
KW_PREPEND, FileName(fnum));
else
{
@@ -401,7 +528,7 @@ static void PS_PrintBeforeFirstPage(FULL_LENGTH h, FULL_LENGTH v,
Link(supplied, tmp);
}
else
- Error(49, 3, "%s file %s lacks PostScript BeginResource comment",
+ Error(49, 5, "%s file %s lacks PostScript BeginResource comment",
WARN, PosOfFile(fnum), KW_PREPEND, FileName(fnum));
StringFPuts(buff, out_fp);
p2("%% %s file %s\n", KW_PREPEND, FileName(fnum));
@@ -415,6 +542,10 @@ static void PS_PrintBeforeFirstPage(FULL_LENGTH h, FULL_LENGTH v,
fputs("%%EndProlog\n\n", out_fp);
fputs("%%BeginSetup\n", out_fp);
MapPrintEncodings();
+
+ /* pdfmark compatibility code, as in the pdfmark Reference Manual p10 */
+ p0("/pdfmark where {pop} {userdict /pdfmark /cleartomark load put} ifelse\n");
+
/* FontPrintPageSetup(out_fp); */
fputs("%%EndSetup\n\n", out_fp);
fprintf(out_fp, "%%%%Page: ");
@@ -494,7 +625,7 @@ static void PS_PrintBetweenPages(FULL_LENGTH h, FULL_LENGTH v, FULL_CHAR *label)
currentcolour = NO_COLOUR;
if( Encapsulated )
{ PS_PrintAfterLastPage();
- Error(49, 4, "truncating -EPS document at end of first page",
+ Error(49, 6, "truncating -EPS document at end of first page",
FATAL, no_fpos);
}
fprintf(out_fp, "\n%%%%Page: ");
@@ -711,18 +842,33 @@ static void PS_PrintPlainGraphic(OBJECT x, FULL_LENGTH xmk,
/*****************************************************************************/
/* */
-/* PS_PrintUnderline(fnum, xstart, xstop, ymk) */
+/* PS_PrintUnderline(fnum, col, xstart, xstop, ymk) */
/* */
-/* Draw an underline suitable for font fnum, from xstart to xstop at the */
-/* appropriate distance below mark ymk. */
+/* Draw an underline suitable for font fnum, in colour col, from xstart to */
+/* xstop at the appropriate distance below mark ymk. */
/* */
/*****************************************************************************/
-static void PS_PrintUnderline(FONT_NUM fnum, FULL_LENGTH xstart,
- FULL_LENGTH xstop, FULL_LENGTH ymk)
+static void PS_PrintUnderline(FONT_NUM fnum, COLOUR_NUM col,
+ FULL_LENGTH xstart, FULL_LENGTH xstop, FULL_LENGTH ymk)
{
- debug4(DPO, DD, "PrintUnderline(fnum %d, xstart %s, xstop %s, ymk %s )",
- fnum, EchoLength(xstart), EchoLength(xstop), EchoLength(ymk));
+ debug5(DPO, DD, "PrintUnderline(fnt %d, col %d, xstart %s, xstop %s, ymk %s)",
+ fnum, col, EchoLength(xstart), EchoLength(xstop), EchoLength(ymk));
+
+ /* if colour is different to previously then print change */
+ if( col != currentcolour )
+ { currentcolour = col;
+ if( currentcolour > 0 )
+ { fprintf(out_fp, "%s", ColourCommand(currentcolour));
+ if( ++wordcount >= 5 )
+ { putc('\n', out_fp);
+ wordcount = 0;
+ }
+ else putc(' ', out_fp);
+ }
+ }
+
+ /* now print the underline command */
fprintf(out_fp, "%d %d %d %d ul\n", xstart, xstop,
ymk - finfo[fnum].underline_pos, finfo[fnum].underline_thick);
debug0(DPO, DD, "PrintUnderline returning.");
@@ -797,7 +943,7 @@ static void PS_SaveGraphicState(OBJECT x)
fprintf(out_fp, "gsave\n");
gs_stack_top++;
if( gs_stack_top >= MAX_GS )
- Error(49, 5, "rotations, graphics etc. too deeply nested (max is %d)",
+ Error(49, 7, "rotations, graphics etc. too deeply nested (max is %d)",
FATAL, &fpos(x), MAX_GS);
gs_stack[gs_stack_top].gs_font = currentfont;
gs_stack[gs_stack_top].gs_colour = currentcolour;
@@ -869,7 +1015,7 @@ void PS_PrintGraphicObject(OBJECT x)
/* ignore: @Wide, indexes are sometimes inserted by Manifest */
}
else
- { Error(49, 6, "error in left parameter of %s",
+ { Error(49, 8, "error in left parameter of %s",
WARN, &fpos(x), KW_GRAPHIC);
debug1(DPO, D, " type(y) = %s, y =", Image(type(y)));
ifdebug(DPO, D, DebugObject(y));
@@ -880,7 +1026,7 @@ void PS_PrintGraphicObject(OBJECT x)
default:
- Error(49, 7, "error in left parameter of %s", WARN, &fpos(x), KW_GRAPHIC);
+ Error(49, 9, "error in left parameter of %s", WARN, &fpos(x), KW_GRAPHIC);
debug1(DPO, D, " type(x) = %s, x =", Image(type(x)));
ifdebug(DPO, D, DebugObject(x));
break;
@@ -1070,10 +1216,10 @@ void PS_PrintGraphicInclude(OBJECT x, FULL_LENGTH colmark, FULL_LENGTH rowmark)
}
else
{ if( StringBeginsWith(buff, AsciiToFull("%%LanguageLevel:")) )
- Error(49, 8, "ignoring LanguageLevel comment in %s file %s",
+ Error(49, 10, "ignoring LanguageLevel comment in %s file %s",
WARN, &fpos(x), KW_INCGRAPHIC, string(full_name));
if( StringBeginsWith(buff, AsciiToFull("%%Extensions:")) )
- Error(49, 9, "ignoring Extensions comment in %s file %s",
+ Error(49, 11, "ignoring Extensions comment in %s file %s",
WARN, &fpos(x), KW_INCGRAPHIC, string(full_name));
if( !strip_out(buff) ) StringFPuts(buff, out_fp);
state = (StringFGets(buff, MAX_BUFF, fp) == NULL) ? FINISHED : SKIPPING;
@@ -1102,6 +1248,122 @@ void PS_PrintGraphicInclude(OBJECT x, FULL_LENGTH colmark, FULL_LENGTH rowmark)
wordcount = 0;
debug0(DPO, D, "PS_PrintGraphicInclude returning.");
} /* end PS_PrintGraphicInclude */
+/*****************************************************************************/
+/* */
+/* char *ConvertToPDFName(name) */
+/* */
+/* Convert string(name) to a suitable PDF label. The result is in static */
+/* memory and must be copied before the next call to ConvertToPDFName. */
+/* */
+/* At present our algorithm is to prefix the label with "LOUT" and to */
+/* replace all non-alphanumerics by one underscore. */
+/* */
+/*****************************************************************************/
+#define in_range(ch, a, b) ( (ch) >= (a) && (ch) <= (b) )
+#define is_lower(ch) in_range(ch, 'a', 'z')
+#define is_upper(ch) in_range(ch, 'A', 'Z')
+#define is_digit(ch) in_range(ch, '0', '9')
+#define is_alphanum(ch) (is_lower(ch) || is_upper(ch) || is_digit(ch))
+
+char *ConvertToPDFName(OBJECT name)
+{ static char buff[200];
+ char *q;
+ FULL_CHAR *p;
+ strcpy(buff, "LOUT");
+ q = &buff[strlen(buff)];
+ for( p = string(name); *p != '\0'; p++ )
+ {
+ if( q >= &buff[199] )
+ Error(49, 12, "tag %s is too long", FATAL, &fpos(name), string(name));
+ if( is_alphanum(*p) )
+ *q++ = (char) *p;
+ else
+ *q++ = '_';
+ }
+ *q++ = '\0';
+ return buff;
+}
+
+
+/*****************************************************************************/
+/* */
+/* PS_LinkSource(name, llx, lly, urx, ury) */
+/* */
+/* Print a link source point. */
+/* */
+/*****************************************************************************/
+
+static void PS_LinkSource(OBJECT name, FULL_LENGTH llx, FULL_LENGTH lly,
+ FULL_LENGTH urx, FULL_LENGTH ury)
+{ debug5(DPO, D, "PS_LinkSource(%s, %d, %d, %d, %d)", EchoObject(name),
+ llx, lly, urx, ury);
+
+ /* print the link source point */
+ fprintf(out_fp,
+ "\n[ /Rect [%d %d %d %d] /Subtype /Link /Dest /%s /ANN pdfmark\n",
+ llx, lly, urx, ury, ConvertToPDFName(name));
+
+ /* remember it so that at end of run can check if it has an dest point */
+ Link(link_source_list, name);
+ debug0(DPO, D, "PS_LinkSource returning.");
+} /* end PS_LinkSource */
+
+
+/*****************************************************************************/
+/* */
+/* PS_LinkDest(name, llx, lly, urx, ury) */
+/* */
+/* Print a link dest point (note llx etc are not used), after making sure */
+/* that no previously printed dest point has the same name. */
+/* */
+/*****************************************************************************/
+
+static void PS_LinkDest(OBJECT name, FULL_LENGTH llx, FULL_LENGTH lly,
+ FULL_LENGTH urx, FULL_LENGTH ury)
+{ OBJECT prev;
+ debug5(DPO, D, "PS_LinkDest(%s, %d, %d, %d, %d)", EchoObject(name),
+ llx, lly, urx, ury);
+
+ prev = ltab_retrieve(string(name), link_dest_tab);
+ if( prev == nilobj )
+ {
+ /* not used previously, so print it and remember it */
+ fprintf(out_fp, "\n[ /Dest /%s /DEST pdfmark\n", ConvertToPDFName(name));
+ ltab_insert(name, &link_dest_tab);
+ }
+ else
+ {
+ /* used previously, so don't print it, and warn the user */
+ Error(49, 13, "link destination %s ignored (there is already one at%s)",
+ WARN, &fpos(name), string(name), EchoFilePos(&fpos(prev)));
+ }
+ debug0(DPO, D, "PS_LinkDest returning.");
+} /* end PS_LinkDest */
+
+
+/*****************************************************************************/
+/* */
+/* PS_LinkCheck() */
+/* */
+/* Called at end of run; will check that for every link source point there */
+/* is a link dest point. */
+/* */
+/*****************************************************************************/
+
+static void PS_LinkCheck()
+{ OBJECT y, link;
+ debug0(DPO, D, "PS_LinkCheck()");
+
+ for( link=Down(link_source_list); link!=link_source_list; link=NextDown(link) )
+ { Child(y, link);
+ assert( is_word(type(y)), " PS_LinkCheck: !is_word(type(y))!");
+ if( ltab_retrieve(string(y), link_dest_tab) == nilobj )
+ Error(49, 14, "link name %s has no destination point", WARN, &fpos(y),
+ string(y));
+ }
+
+ debug0(DPO, D, "PS_LinkCheck returning.");
+} /* end PS_LinkCheck */
/*****************************************************************************/
@@ -1143,6 +1405,9 @@ static struct back_end_rec ps_back = {
PS_DefineGraphicNames,
PS_SaveTranslateDefineSave,
PS_PrintGraphicInclude,
+ PS_LinkSource,
+ PS_LinkDest,
+ PS_LinkCheck,
};
BACK_END PS_BackEnd = &ps_back;