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