1 /*	$Id$ */
2 /*
3  * Copyright (c) 1994-1996 Sam Leffler
4  * Copyright (c) 1994-1996 Silicon Graphics, Inc.
5  * HylaFAX is a trademark of Silicon Graphics
6  *
7  * Permission to use, copy, modify, distribute, and sell this software and
8  * its documentation for any purpose is hereby granted without fee, provided
9  * that (i) the above copyright notices and this permission notice appear in
10  * all copies of the software and related documentation, and (ii) the names of
11  * Sam Leffler and Silicon Graphics may not be used in any advertising or
12  * publicity relating to the software without the specific, prior written
13  * permission of Sam Leffler and Silicon Graphics.
14  *
15  * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND,
16  * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY
17  * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
18  *
19  * IN NO EVENT SHALL SAM LEFFLER OR SILICON GRAPHICS BE LIABLE FOR
20  * ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND,
21  * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
22  * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF
23  * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
24  * OF THIS SOFTWARE.
25  */
26 #include "FaxServer.h"
27 #include "PCFFont.h"
28 #include "StackBuffer.h"
29 #include "FaxFont.h"
30 #include "FaxRequest.h"
31 
32 #include "Sys.h"
33 
34 static void
insert(fxStr & tag,u_int l,const fxStr & s)35 insert(fxStr& tag, u_int l, const fxStr& s)
36 {
37     tag.remove(l,2);
38     tag.insert(s, l);
39 }
40 
41 extern void logDebug(const char* fmt, ...);
42 /*
43  * Read in the PCF font to use for imaging the tag line and
44  * preformat as much of the tag line as possible.
45  */
46 void
setupTagLine(const FaxRequest & req,const fxStr & tagLineFmt)47 FaxModem::setupTagLine(const FaxRequest& req, const fxStr& tagLineFmt)
48 {
49     if (tagLineFont == NULL)
50 	tagLineFont = new PCFFont;
51     if (!tagLineFont->isReady() && conf.tagLineFontFile != "")
52 	(void) tagLineFont->read(conf.tagLineFontFile);
53 
54     time_t t = Sys::now();
55     tm* tm = localtime(&t);
56     char line[1024];
57     strftime(line, sizeof (line)-1, tagLineFmt, tm);
58     tagLine = line;
59     u_int l = 0;
60 
61     int tpages = req.totpages;
62     int npages = req.npages;
63     if (! conf.countSkippedPages) {
64 	tpages -= req.skippages;
65 	npages -= req.nskip;
66     }
67 
68     if (conf.tagLineCoverNumString.length())
69     {
70 	tpages -= req.coverpages;
71 	npages -= req.ncover;
72     }
73 
74     while (l < tagLine.length()) {
75 	l = tagLine.next(l, '%');
76 	if (l >= tagLine.length()-1)
77 	    break;
78 	switch (tagLine[l+1]) {
79 	case 'a': insert(tagLine, l, req.subaddr); break;
80 	case 'c': insert(tagLine, l, req.company); break;
81 	case 'C': insert(tagLine, l, req.fromcompany); break;
82 	case 'd': insert(tagLine, l, req.external); break;
83 	case 'g': insert(tagLine, l, req.location); break;
84 	case 'G': insert(tagLine, l, req.fromlocation); break;
85 	case 'i': insert(tagLine, l, req.jobid); break;
86 	case 'I': insert(tagLine, l, req.groupid); break;
87 	case 'j': insert(tagLine, l, req.jobtag); break;
88 	case 'l': insert(tagLine, l, server.getLocalIdentifier()); break;
89 	case 'm': insert(tagLine, l, req.mailaddr); break;
90 	case 'n':
91 	    if (req.faxnumber == "")
92 		insert(tagLine, l, server.getModemNumber());
93 	    else
94 		insert(tagLine, l, req.faxnumber);
95 	    break;
96 	case 'r': insert(tagLine, l, req.receiver); break;
97 	case 's': insert(tagLine, l, req.sender); break;
98 	case 'S': insert(tagLine, l, req.regarding); break;
99 	case 't': insert(tagLine, l,
100 			fxStr((int)(tpages-npages), "%u")); break;
101 	case 'T': insert(tagLine, l,
102 			fxStr((int)(tpages), "%u")); break;
103 	case 'v': insert(tagLine, l, req.voice); break;
104 	case 'V': insert(tagLine, l, req.fromvoice); break;
105 	case '%': tagLine.remove(l); break;
106 	default:  l += 2; break;
107 	}
108     }
109     /*
110      * Break the tag into fields.
111      */
112     tagLineFields = 0;
113     for (l = 0; l < tagLine.length(); l = tagLine.next(l+1, '|'))
114 	tagLineFields++;
115 }
116 
117 #define	MARGIN_TOP	2
118 #define	MARGIN_BOT	2
119 #define	MARGIN_LEFT	2
120 #define	MARGIN_RIGHT	2
121 #define SLOP_LINES	3
122 
123 /*
124  * Calculate a ``slop factor'' to use in processing the tag line.
125  * This is the amount of space to preallocate for storing the
126  * encoded tag line once its been imaged.  We guestimate that
127  * the encoded data will be <= the amount of space needed to store
128  * the unencoded bitmap raster.  If this number is low then the
129  * encoded raster will be truncated resulting in the tag line
130  * being cropped at the bottom; probably together with a decoding
131  * error of one row at the receiver.
132  */
133 bool
setupTagLineSlop(const Class2Params & params)134 FaxModem::setupTagLineSlop(const Class2Params& params)
135 {
136     if (tagLineFont->isReady() && tagLineFields > 0) {
137 	tagLineSlop = (tagLineFont->fontHeight()+MARGIN_TOP+MARGIN_BOT+SLOP_LINES) *
138 	    howmany(params.pageWidth(),8);
139 	return (true);
140     } else {
141 	tagLineSlop = 0;
142 	return (false);
143     }
144 }
145 
146 #include "MemoryDecoder.h"
147 
148 /*
149  * Image the tag line in place of the top few lines of the page
150  * data and return the encoded tag line at the front of the
151  * data buffer.  The buffer that holds the page data is assumed
152  * to have tagLineSlop extra space allocated in front of the
153  * page data.  The tag line format string is assumed to be
154  * preprocessed by setupTagLine above so that we only need to
155  * setup the current page number.
156  */
157 u_char*
imageTagLine(u_char * buf,u_int fillorder,const Class2Params & params,u_long & totdata,PageType pt)158 FaxModem::imageTagLine(u_char* buf, u_int fillorder, const Class2Params& params, u_long& totdata, PageType pt)
159 {
160     u_int l;
161     u_int pn = pageNumber;
162     u_int pnj = pageNumberOfJob;
163 
164     if (! conf.countSkippedPages) {
165 	pn -= pageNumberSkipped;
166 	pnj -= pageNumberSkipped;
167     }
168     /*
169      * Fill in any per-page variables used in the tag line.
170      * If the TagLineCoverNumString is set, then coverpages
171      * are not counted, and that is used instead of pages numbers on
172      * the cover pages themselves.
173      */
174     fxStr pns, pnjs;
175     if (pt == PAGE_COVER && conf.tagLineCoverNumString.length()) {
176 	// Actually on the cover page, with it CoverNumString set
177 	pns = conf.tagLineCoverNumString;
178 	pnjs = conf.tagLineCoverNumString;
179     } else if (conf.tagLineCoverNumString.length() ) {
180 	// regular page, with it CoverNumString set, adjust page numbers
181 	pns = fxStr::format("%d", pn-pageNumberCovered);
182 	pnjs = fxStr::format("%d", pnj-pageNumberCovered);
183     } else {
184 	// CoverNumString not set, just do it
185 	pns = fxStr::format("%d", pnj);
186 	pnjs = fxStr::format("%d", pnj);
187     }
188 
189     fxStr tag = tagLine;
190     l = 0;
191     while (l < tag.length()) {
192 	l = tag.next(l, '%');
193 	if (l >= tag.length()-1)
194 	    break;
195 	if (tag[l+1] == 'p')
196 	    insert(tag, l, pns);
197 	if (tag[l+1] == 'P')
198 	    insert(tag, l, pnjs);
199 	else
200 	    l += 2;
201     }
202     /*
203      * Setup the raster in which the tag line is imaged.
204      *
205      * The font size information received from the font functions
206      * is suitable for VR_FINE.  Thus VR_FINE is used as the reference
207      * resolution, and all other resolutions must be scaled.
208      */
209     u_int w = params.pageWidth();
210     u_int h = (tagLineFont->fontHeight()*2)+MARGIN_TOP+MARGIN_BOT;	// max height - double VR_FINE
211     u_int th = 0;							// actual tagline height
212     switch(params.vr) {
213 	case VR_NORMAL:
214 	case VR_200X100:
215 	    th = (tagLineFont->fontHeight()/2)+MARGIN_TOP+MARGIN_BOT;	// half VR_FINE
216 	    break;
217 	case VR_FINE:
218 	case VR_200X200:
219 	    th = tagLineFont->fontHeight()+MARGIN_TOP+MARGIN_BOT;	// reference resolution
220 	    break;
221 	case VR_R8:
222 	case VR_R16:
223 	case VR_200X400:
224 	case VR_300X300:	// not proportionate but legible
225 	    th = (tagLineFont->fontHeight()*2)+MARGIN_TOP+MARGIN_BOT;	// double VR_FINE
226 	    break;
227     }
228     /*
229      * imageText assumes that raster is word-aligned; we use
230      * longs here to optimize the scaling done below for the
231      * low res case.  This should satisfy the word-alignment.
232      *
233      * NB: The +SLOP_LINES below is for the case where we need to
234      *     re-encode 2D-encoded data.  An extra 3 rows is sufficient
235      *     because the number of consecutive 2D-encoded rows is bounded
236      *     by the K parameter in the CCITT spec.
237      */
238     u_int lpr = howmany(w, sizeof(u_long)*8);		// longs/raster row
239     u_long* raster = new u_long[(h+SLOP_LINES)*lpr];	// decoded raster
240     memset(raster,0,(h+SLOP_LINES)*lpr*sizeof (u_long));// clear raster to white
241     /*
242      * Break the tag into fields and render each piece of
243      * text centered in its field.  Experiments indicate
244      * that rendering the text over white is better than,
245      * say, rendering it over the original page.
246      */
247     l = 0;
248     /*
249      * imageText produces good dimensioned fonts at 1728 pixels/row. At VR_R16
250      * and VR_300X300, both being wider than 1728, text appears shrinked
251      * horizontally; while VR_300 is still ok, VR_R16 is too small. To help
252      * streching the text horizontally we force text imaging to still use
253      * 1728 instead of VR_R16's 3456 (3456 / 2 = 1728); text will be
254      * imaged the 1st (left) half of the line, it will be stretched after
255      * imaging takes place.
256      */
257     u_int fieldWidth = params.pageWidth() / (params.vr == VR_R16 ? 2 : 1) / tagLineFields;
258     for (u_int f = 0; f < tagLineFields; f++) {
259 	fxStr tagField = tag.token(l, '|');
260 	u_int fw, fh;
261 	tagLineFont->strWidth(tagField, fw, fh);
262 	u_int xoff = f*fieldWidth;
263 	if (fw < fieldWidth)
264 	    xoff += (fieldWidth-fw)/2;
265 	else
266 	    xoff += MARGIN_LEFT;
267 	(void) tagLineFont->imageText(tagField, (u_short*) raster, w, h,
268 	    xoff, MARGIN_RIGHT, MARGIN_TOP, MARGIN_BOT);
269     }
270 
271     /*
272      * Scale image data as needed (see notes above).
273      */
274 
275     if (params.vr == VR_NORMAL || params.vr == VR_200X100) {
276 	/*
277 	 * These resolutions require vertical "shrinking" of the
278 	 * tagline image.  We make 1 line out of 2.
279 	 * (Note the ``or'' used to generate the final samples.)
280 	 *
281 	 * Details:
282 	 * - image is in lines 1 through y
283 	 * - available lines are 1 through y/2
284 	 * - start at the top of the image
285 	 * - line 1 is ORed with line 2 to get new line 1
286 	 * - line 3 is ORed with line 4 to get new line 2
287 	 * - ...
288 	 * - line y is ORed with line y+1 to get new line (y+1)/2
289 	 */
290 	u_long* l1 = raster+MARGIN_TOP*lpr;
291 	u_long* l2 = l1+lpr;
292 	u_long* l3 = raster+MARGIN_TOP*lpr;
293 	for (u_int nr = th-(MARGIN_TOP+MARGIN_BOT); nr; nr--) {
294 	    for (u_int nl = lpr; nl; nl--)
295 		*l3++ = *l1++ | *l2++;
296 	    l1 += lpr;
297 	    l2 += lpr;
298 	}
299 	memset(l3, 0, MARGIN_BOT*lpr*sizeof (u_long));
300     }
301     if (params.vr == VR_R8 || params.vr == VR_R16 || params.vr == VR_200X400 || params.vr == VR_300X300) {
302 	/*
303 	 * These resolutions require vertical "stretching" of the
304 	 * tagline image.  We make 2 lines out of 1.
305 	 * Go bottom-to-top since the image resides in the top half and the
306 	 * bottom data can be overwritten since it is unset.
307 	 *
308 	 * Details:
309 	 * - image is in lines 1 through y/2
310 	 * - available lines are 1 through y
311 	 * - we use 2 pointers, 1st starting at line y/2 the other at line y
312 	 * - line y/2   copied in line y   and y-1
313 	 * - line y/2-1 copied in line y-2 and y-2-1
314 	 * - ...
315 	 */
316 	// bottom of actual image
317 	u_long* l1 = raster - 1 + lpr * (MARGIN_TOP + (th-MARGIN_TOP-MARGIN_BOT)/2 + 2);
318 	// bottom of available image
319 	u_long* l2 = raster - 1 + lpr * (MARGIN_TOP + (th-MARGIN_TOP-MARGIN_BOT) + 1);
320 
321 	/* stretch vertically (going backwards, R->L, B->T) */
322 	for (u_int nr = (th-(MARGIN_TOP+MARGIN_BOT))/2; nr; nr--) {
323 	    for (u_int nl = lpr; nl; nl--) {
324 		*(l2 - lpr) = *l1;	/* y/2 copied into y-1 */
325 		*l2 = *l1;		/* y/2 copied into y */
326 		l2--;			/* left 1 long */
327 		l1--;			/* left 1 long */
328 	    }
329 	    /* after previous loop, l1 and l2 are up 1 line; l2 needs 1 more */
330 	    l2 -= lpr;			/* 2nd ptr up 1 line */
331 	}
332 	if (params.vr == VR_R16) {
333 	    /*
334 	     * hr is twice the hr in which data is imaged.
335 	     * We need to strech the image horizontally:
336 	     * 1234567890ABCDEFGHIJ -> 11223344556677889900
337 	     * (ABCDEFGHIJ is whitespace)
338 	     */
339 
340 	    /* Reset ptr to begin of image */
341 	    l1 = raster + MARGIN_TOP*lpr;               // begin of 1st line
342 	    l2 = raster + MARGIN_TOP*lpr + lpr - 1;     // end of 1st line
343 	    for (u_int nr = th-(MARGIN_TOP+MARGIN_BOT); nr; nr--) {
344 		/*
345 		 * 0      lpr/2      lpr
346 		 * |        |         |
347 		 * 1234567890__________
348 		 * 1234567890________00  x/2   copied into x   and x-1
349 		 * 1234567890______9900  x/2-1 copied into x-2 and x-3
350 		 * ...
351 		 * 11223344556677889900
352 		 */
353 		u_int bpl = sizeof(u_long) * 8;		// bits per u_long
354 		for (u_int nl = lpr/2 - 1; nl ; nl--) {
355 		    // make 2 longs out of 1 (ABCD -> AABB CCDD)
356 		    int pos = 0;
357 		    for (u_int i = 0; i < (bpl/8); i++) {
358 			if (i == 0 || i == bpl/8/2) {
359 			    *l2 = (u_long) 0;
360 			    pos = bpl - 2;
361 			}
362 			// put pairs of bits from l1 into the right places within l2
363 			*l2 |= (u_long)((*(l1+nl) & (1<<(bpl-8*i-5))) >> (bpl-8*i-5) << pos);
364 			*l2 |= ((u_long)((*(l1+nl) & (1<<(bpl-8*i-5))) >> (bpl-8*i-5) << pos)) << 1;
365 			pos -= 2;
366 
367 			*l2 |= (u_long)((*(l1+nl) & (1<<(bpl-8*i-6))) >> (bpl-8*i-6) << pos);
368 			*l2 |= ((u_long)((*(l1+nl) & (1<<(bpl-8*i-6))) >> (bpl-8*i-6) << pos)) << 1;
369 			pos -= 2;
370 
371 			*l2 |= (u_long)((*(l1+nl) & (1<<(bpl-8*i-7))) >> (bpl-8*i-7) << pos);
372 			*l2 |= ((u_long)((*(l1+nl) & (1<<(bpl-8*i-7))) >> (bpl-8*i-7) << pos)) << 1;
373 			pos -= 2;
374 
375 			*l2 |= (u_long)((*(l1+nl) & (1<<(bpl-8*i-8))) >> (bpl-8*i-8) << pos);
376 			*l2 |= ((u_long)((*(l1+nl) & (1<<(bpl-8*i-8))) >> (bpl-8*i-8) << pos)) << 1;
377 			pos -= 2;
378 
379 			*l2 |= (u_long)((*(l1+nl) & (1<<(bpl-8*i-1))) >> (bpl-8*i-1) << pos);
380 			*l2 |= ((u_long)((*(l1+nl) & (1<<(bpl-8*i-1))) >> (bpl-8*i-1) << pos)) << 1;
381 			pos -= 2;
382 
383 			*l2 |= (u_long)((*(l1+nl) & (1<<(bpl-8*i-2))) >> (bpl-8*i-2) << pos);
384 			*l2 |= ((u_long)((*(l1+nl) & (1<<(bpl-8*i-2))) >> (bpl-8*i-2) << pos)) << 1;
385 			pos -= 2;
386 
387 			*l2 |= (u_long)((*(l1+nl) & (1<<(bpl-8*i-3))) >> (bpl-8*i-3) << pos);
388 			*l2 |= ((u_long)((*(l1+nl) & (1<<(bpl-8*i-3))) >> (bpl-8*i-3) << pos)) << 1;
389 			pos -= 2;
390 
391 			*l2 |= (u_long)((*(l1+nl) & (1<<(bpl-8*i-4))) >> (bpl-8*i-4) << pos);
392 			*l2 |= ((u_long)((*(l1+nl) & (1<<(bpl-8*i-4))) >> (bpl-8*i-4) << pos)) << 1;
393 			pos -= 2;
394 			if (pos < 0) *l2--;
395 		    }
396 		}
397 		l1 += lpr;              // begin of next line
398 		l2 = l1 + lpr - 1;      // end of next line
399 	    }
400 	}
401 	memset(l2, 0, MARGIN_BOT*lpr*sizeof (u_long));
402     }
403     MemoryDecoder dec(buf, w, totdata, fillorder, params.is2D(), (params.df == DF_2DMMR));
404     u_char* encbuf = dec.encodeTagLine(raster, th, tagLineSlop);
405     totdata = dec.getCC();
406     return (encbuf);
407 }
408