1 /* This file is part of the GNU plotutils package.  Copyright (C) 1995,
2    1996, 1997, 1998, 1999, 2000, 2005, 2008, Free Software Foundation, Inc.
3 
4    The GNU plotutils package is free software.  You may redistribute it
5    and/or modify it under the terms of the GNU General Public License as
6    published by the Free Software foundation; either version 2, or (at your
7    option) any later version.
8 
9    The GNU plotutils package is distributed in the hope that it will be
10    useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12    General Public License for more details.
13 
14    You should have received a copy of the GNU General Public License along
15    with the GNU plotutils package; see the file COPYING.  If not, write to
16    the Free Software Foundation, Inc., 51 Franklin St., Fifth Floor,
17    Boston, MA 02110-1301, USA. */
18 
19 /* This file contains routines for creating, manipulating, and deleting a
20    special sort of output buffer: a plOutbuf.  They are invoked by drawing
21    methods for Plotters that do not do real-time output.  Such Plotters
22    store the device code for each page of graphics in a plOutbuf.  Along
23    with a page of device code, a plOutbuf optionally stores bounding box
24    information for the page.
25 
26    plOutbufs are resized when they are too full.  The strange resizing
27    method (_UPDATE_BUFFER) is needed because on many systems, sprintf()
28    does not return the number of characters it writes.  _UPDATE_BUFFER must
29    be called after each call to sprintf(); it is not invoked automatically.
30 
31    Output buffers of this sort are a bit of a kludge.  They may eventually
32    be replaced or supplemented by an in-core object hierarchy, which
33    deletepl() will scan.
34 
35    When erase() is invoked on the Plotter, _RESET_OUTBUF is called, to
36    remove all graphics from the plOutbuf.  There is provision for keeping a
37    section of initialization code in the plOutbuf, untouched.  This is
38    arranged by a previous call to _FREEZE_OUTBUF.  Anything in the plOutbuf
39    at that time will be untouched by a later call to _RESET_OUTBUF. */
40 
41 #include "sys-defines.h"
42 #include "extern.h"
43 
44 /* Initial length for a plOutbuf (should be large enough to handle any
45    single one of our sprintf's or strcpy's [including final NUL], or other
46    write operations, without overflow).  Note: in p_defplot.c we write long
47    blocks of Postscript initialization code (see p_header.h) into a
48    plOutbuf, so this should be quite large.  We may also write large
49    substrings into a plOutbuf in c_emit.c. */
50 #define INITIAL_OUTBUF_LEN 8192
51 
52 /* New (larger) length of a plOutbuf, as function of the old; used when
53    reallocating due to exhaustion of storage. */
54 #define NEW_OUTBUF_LEN(old_outbuf_len) ((old_outbuf_len) < 10000000 ? 2 * (old_outbuf_len) : (old_outbuf_len) + 10000000)
55 
56 plOutbuf *
_new_outbuf(void)57 _new_outbuf (void)
58 {
59   plOutbuf *bufp;
60 
61   bufp = (plOutbuf *)_pl_xmalloc(sizeof(plOutbuf));
62   bufp->header = (plOutbuf *)NULL;
63   bufp->trailer = (plOutbuf *)NULL;
64   bufp->base = (char *)_pl_xmalloc(INITIAL_OUTBUF_LEN * sizeof(char));
65   bufp->len = (unsigned long)INITIAL_OUTBUF_LEN;
66   bufp->next = NULL;
67   bufp->reset_point = bufp->base;
68   bufp->reset_contents = (unsigned long)0L;
69   _reset_outbuf (bufp);
70 
71   return bufp;
72 }
73 
74 void
_reset_outbuf(plOutbuf * bufp)75 _reset_outbuf (plOutbuf *bufp)
76 {
77   int i;
78 
79   *(bufp->reset_point) = '\0';
80   bufp->point = bufp->reset_point;
81   bufp->contents = bufp->reset_contents;
82 
83   /* also initialize elements used by some drivers */
84 
85   /* initialize bounding box to an empty (self-contradictory) box */
86   bufp->xrange_min = DBL_MAX;
87   bufp->xrange_max = -(DBL_MAX);
88   bufp->yrange_min = DBL_MAX;
89   bufp->yrange_max = -(DBL_MAX);
90 
91   /* initialize `font used' arrays for the page */
92   for (i = 0; i < PL_NUM_PS_FONTS; i++)
93     bufp->ps_font_used[i] = false;
94   for (i = 0; i < PL_NUM_PCL_FONTS; i++)
95     bufp->pcl_font_used[i] = false;
96 }
97 
98 void
_freeze_outbuf(plOutbuf * bufp)99 _freeze_outbuf (plOutbuf *bufp)
100 {
101   bufp->reset_point = bufp->point;
102   bufp->reset_contents = bufp->contents;
103 }
104 
105 void
_delete_outbuf(plOutbuf * bufp)106 _delete_outbuf (plOutbuf *bufp)
107 {
108   if (bufp)
109     {
110       free (bufp->base);
111       free (bufp);
112     }
113 }
114 
115 /* UPDATE_BUFFER is called manually, after each sprintf() and other object
116    write operation.  It assumes that the buffer is always a null-terminated
117    string, so that strlen() can be used, to determine how many additional
118    characters were added.  */
119 
120 void
_update_buffer(plOutbuf * bufp)121 _update_buffer (plOutbuf *bufp)
122 {
123   int additional;
124 
125   /* determine how many add'l chars were added */
126   additional = strlen (bufp->point);
127   bufp->point += additional;
128   bufp->contents += additional;
129 
130   if (bufp->contents + 1 > bufp->len) /* need room for NUL */
131     /* shouldn't happen! */
132     {
133       fprintf (stderr, "libplot: output buffer overrun\n");
134       exit (EXIT_FAILURE);
135     }
136   if (bufp->contents > (bufp->len >> 1))
137     /* expand buffer */
138     {
139       unsigned long oldlen, newlen;
140 
141       oldlen = bufp->len;
142       newlen = NEW_OUTBUF_LEN(oldlen);
143 
144       bufp->base =
145 	(char *)_pl_xrealloc (bufp->base, newlen * sizeof(char));
146       bufp->len = newlen;
147       bufp->point = bufp->base + bufp->contents;
148       bufp->reset_point = bufp->base + bufp->reset_contents;
149     }
150 }
151 
152 /* A variant of _UPDATE_BUFFER in which the caller specifies how many bytes
153    have been added.  Used in cases when the buffer contains something other
154    than a null-terminated string (e.g., raw binary bytes). */
155 
156 void
_update_buffer_by_added_bytes(plOutbuf * bufp,int additional)157 _update_buffer_by_added_bytes (plOutbuf *bufp, int additional)
158 {
159   bufp->point += additional;
160   bufp->contents += additional;
161 
162   if (bufp->contents + 1 > bufp->len) /* need room for NUL */
163     /* shouldn't happen! */
164     {
165       fprintf (stderr, "libplot: output buffer overrun\n");
166       exit (EXIT_FAILURE);
167     }
168   if (bufp->contents > (bufp->len >> 1))
169     /* expand buffer */
170     {
171       unsigned long oldlen, newlen;
172 
173       oldlen = bufp->len;
174       newlen = NEW_OUTBUF_LEN(oldlen);
175 
176       bufp->base =
177 	(char *)_pl_xrealloc (bufp->base, newlen * sizeof(char));
178       bufp->len = newlen;
179       bufp->point = bufp->base + bufp->contents;
180       bufp->reset_point = bufp->base + bufp->reset_contents;
181     }
182 }
183 
184 /* update bounding box information for a plOutbuf, to take account of a
185    point being plotted on the associated page */
186 void
_update_bbox(plOutbuf * bufp,double x,double y)187 _update_bbox (plOutbuf *bufp, double x, double y)
188 {
189   if (x > bufp->xrange_max) bufp->xrange_max = x;
190   if (x < bufp->xrange_min) bufp->xrange_min = x;
191   if (y > bufp->yrange_max) bufp->yrange_max = y;
192   if (y < bufp->yrange_min) bufp->yrange_min = y;
193 }
194 
195 /* return bounding box information for a plOutbuf */
196 void
_bbox_of_outbuf(plOutbuf * bufp,double * xmin,double * xmax,double * ymin,double * ymax)197 _bbox_of_outbuf (plOutbuf *bufp, double *xmin, double *xmax, double *ymin, double *ymax)
198 {
199   double page_x_min = DBL_MAX;
200   double page_y_min = DBL_MAX;
201   double page_x_max = -(DBL_MAX);
202   double page_y_max = -(DBL_MAX);
203 
204   if (bufp)
205     {
206       page_x_max = bufp->xrange_max;
207       page_x_min = bufp->xrange_min;
208       page_y_max = bufp->yrange_max;
209       page_y_min = bufp->yrange_min;
210     }
211 
212   *xmin = page_x_min;
213   *ymin = page_y_min;
214   *xmax = page_x_max;
215   *ymax = page_y_max;
216 }
217 
218 /* compute bounding box information for a linked list of plOutbufs
219    (i.e. pages), starting with a specified plOutbuf (i.e., page) */
220 void
_bbox_of_outbufs(plOutbuf * bufp,double * xmin,double * xmax,double * ymin,double * ymax)221 _bbox_of_outbufs (plOutbuf *bufp, double *xmin, double *xmax, double *ymin, double *ymax)
222 {
223   double doc_x_min = DBL_MAX;
224   double doc_y_min = DBL_MAX;
225   double doc_x_max = -(DBL_MAX);
226   double doc_y_max = -(DBL_MAX);
227   double page_x_min, page_x_max, page_y_min, page_y_max;
228   plOutbuf *page = bufp;
229 
230   while (page)
231     {
232       page_x_max = page->xrange_max;
233       page_x_min = page->xrange_min;
234       page_y_max = page->yrange_max;
235       page_y_min = page->yrange_min;
236 
237       if (!((page_x_max < page_x_min || page_y_max < page_y_min)))
238 	/* nonempty page */
239 	{
240 	  if (page_x_max > doc_x_max) doc_x_max = page_x_max;
241 	  if (page_y_max > doc_y_max) doc_y_max = page_y_max;
242 	  if (page_x_min < doc_x_min) doc_x_min = page_x_min;
243 	  if (page_y_min < doc_y_min) doc_y_min = page_y_min;
244 	}
245       page = page->next;
246     }
247 
248   *xmin = doc_x_min;
249   *ymin = doc_y_min;
250   *xmax = doc_x_max;
251   *ymax = doc_y_max;
252 }
253