/*
* ratPrint.c --
*
* This file contains the code for a simple prettyprinter.
* Unfortunately it is currently limited to iso8859-1 characters.
*
* TkRat software and its included text is Copyright 1996-2002 by
* Martin Forssén
*
* The full text of the legal notice is contained in the file called
* COPYRIGHT, included with this distribution.
*/
#include "ratFolder.h"
#include <tk.h>
typedef enum {
FONT_SMALL,
FONT_NORMAL,
FONT_BOLD,
FONT_BIG
} RatFont;
/*
* Misc defines and variables describing the size of the page and
* other related data
*/
#define PS_LEFT_MARGIN 50
#define PS_RIGHT_MARGIN 25
#define PS_TOP_MARGIN 25
#define PS_BOTTOM_MARGIN 25
static int ps_xsize, ps_ysize;
static int portrait;
static int fontsize;
static int resolution;
static char *font, *boldfont;
static int *font_wx, *boldfont_wx;
static int yPos;
static int pagenum;
#define DATEWIDTH fontsize*6
#define PAGENUMWIDTH fontsize*4
#define SPACE 5
#define HINDENT 20
#define CHECK_NEWPAGE(x, y) {if (yPos < SPACE) Newpage(x, y, NULL, NULL);}
/*
* PostScript prolog, partly stolen from the tk 8.2b1 one
*/
static char *prolog = "\n\
%%BeginProlog\n\
% Define the array ISOLatin1Encoding (which specifies how characters are\n\
% encoded for ISO-8859-1 fonts), if it isn't already present (Postscript\n\
% level 2 is supposed to define it, but level 1 doesn't).\n\
\n\
systemdict /ISOLatin1Encoding known not {\n\
/ISOLatin1Encoding [\n\
/space /space /space /space /space /space /space /space\n\
/space /space /space /space /space /space /space /space\n\
/space /space /space /space /space /space /space /space\n\
/space /space /space /space /space /space /space /space\n\
/space /exclam /quotedbl /numbersign /dollar /percent /ampersand\n\
/quoteright\n\
/parenleft /parenright /asterisk /plus /comma /minus /period /slash\n\
/zero /one /two /three /four /five /six /seven\n\
/eight /nine /colon /semicolon /less /equal /greater /question\n\
/at /A /B /C /D /E /F /G\n\
/H /I /J /K /L /M /N /O\n\
/P /Q /R /S /T /U /V /W\n\
/X /Y /Z /bracketleft /backslash /bracketright /asciicircum \
/underscore\n\
/quoteleft /a /b /c /d /e /f /g\n\
/h /i /j /k /l /m /n /o \n\
/p /q /r /s /t /u /v /w\n\
/x /y /z /braceleft /bar /braceright /asciitilde /space\n\
/space /space /space /space /space /space /space /space\n\
/space /space /space /space /space /space /space /space\n\
/dotlessi /grave /acute /circumflex /tilde /macron /breve /dotaccent\n\
/dieresis /space /ring /cedilla /space /hungarumlaut /ogonek /caron\n\
/space /exclamdown /cent /sterling /currency /yen /brokenbar /section\n\
/dieresis /copyright /ordfeminine /guillemotleft /logicalnot /hyphen\n\
/registered /macron\n\
/degree /plusminus /twosuperior /threesuperior /acute /mu /paragraph\n\
/periodcentered\n\
/cedillar /onesuperior /ordmasculine /guillemotright /onequarter\n\
/onehalf /threequarters /questiondown\n\
/Agrave /Aacute /Acircumflex /Atilde /Adieresis /Aring /AE /Ccedilla\n\
/Egrave /Eacute /Ecircumflex /Edieresis /Igrave /Iacute /Icircumflex\n\
/Idieresis\n\
/Eth /Ntilde /Ograve /Oacute /Ocircumflex /Otilde /Odieresis \
/multiply\n\
/Oslash /Ugrave /Uacute /Ucircumflex /Udieresis /Yacute /Thorn\n\
/germandbls\n\
/agrave /aacute /acircumflex /atilde /adieresis /aring /ae /ccedilla\n\
/egrave /eacute /ecircumflex /edieresis /igrave /iacute /icircumflex\n\
/idieresis\n\
/eth /ntilde /ograve /oacute /ocircumflex /otilde /odieresis /divide\n\
/oslash /ugrave /uacute /ucircumflex /udieresis /yacute /thorn\n\
/ydieresis\n\
] def\n\
} if\n\
\n\
% font ISOEncode font \n\
% This procedure changes the encoding of a font from the default\n\
% Postscript encoding to ISOLatin1. It's typically invoked just\n\
% before invoking \"setfont\". The body of this procedure comes from\n\
% Section 5.6.1 of the Postscript book.\n\
\n\
/ISOEncode {\n\
dup length dict begin\n\
{1 index /FID ne {def} {pop pop} ifelse} forall\n\
/Encoding ISOLatin1Encoding def\n\
currentdict\n\
end \n\
\n\
% I'm not sure why it's necessary to use \"definefont\" on this new\n\
% font, but it seems to be important; just use the name \"Temporary\"\n\
% for the font. \n\
\n\
/Temporary exch definefont \n\
} bind def \n\
";
/*
* Width table for Times-Roman with the iso8859-1 encoding
*/
static int tir_wx[256] = {
250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250,
250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250,
250, 250, 250, 333, 408, 500, 500, 833, 778, 333, 333, 333, 500, 564, 250,
564, 250, 278, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 278, 278,
564, 564, 564, 444, 921, 722, 667, 667, 722, 611, 556, 722, 722, 333, 389,
722, 611, 889, 722, 722, 556, 722, 667, 556, 611, 722, 722, 944, 722, 722,
611, 333, 278, 333, 469, 500, 333, 444, 500, 444, 500, 444, 333, 500, 500,
278, 278, 500, 278, 778, 500, 500, 500, 500, 333, 389, 278, 500, 500, 722,
500, 500, 444, 480, 200, 480, 541, 250, 250, 250, 250, 250, 250, 250, 250,
250, 250, 250, 250, 250, 250, 250, 250, 250, 278, 333, 333, 333, 333, 333,
333, 333, 333, 250, 333, 333, 250, 333, 333, 333, 250, 333, 500, 500, 500,
500, 200, 500, 333, 760, 276, 500, 564, 333, 760, 333, 400, 564, 300, 300,
333, 500, 453, 250, 333, 300, 310, 500, 750, 750, 750, 444, 722, 722, 722,
722, 722, 722, 889, 667, 611, 611, 611, 611, 333, 333, 333, 333, 722, 722,
722, 722, 722, 722, 722, 564, 722, 722, 722, 722, 722, 722, 556, 500, 444,
444, 444, 444, 444, 444, 667, 444, 444, 444, 444, 444, 278, 278, 278, 278,
500, 500, 500, 500, 500, 500, 500, 564, 500, 500, 500, 500, 500, 500, 500,
500
};
/*
* Width table for Times-Bold with the iso8859-1 encoding
*/
static int tib_wx[256] = {
250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250,
250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250,
250, 250, 250, 333, 555, 500, 500, 1000, 833, 333, 333, 333, 500, 570, 250,
570, 250, 278, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 333, 333,
570, 570, 570, 500, 930, 722, 667, 722, 722, 667, 611, 778, 778, 389, 500,
778, 667, 944, 722, 778, 611, 778, 722, 556, 667, 722, 722, 1000, 722, 722,
667, 333, 278, 333, 581, 500, 333, 500, 556, 444, 556, 444, 333, 500, 556,
278, 333, 556, 278, 833, 556, 500, 556, 556, 444, 389, 333, 556, 500, 722,
500, 500, 444, 394, 220, 394, 520, 250, 250, 250, 250, 250, 250, 250, 250,
250, 250, 250, 250, 250, 250, 250, 250, 250, 278, 333, 333, 333, 333, 333,
333, 333, 333, 250, 333, 333, 250, 333, 333, 333, 250, 333, 500, 500, 500,
500, 220, 500, 333, 747, 300, 500, 570, 333, 747, 333, 400, 570, 300, 300,
333, 556, 540, 250, 333, 300, 330, 500, 750, 750, 750, 500, 722, 722, 722,
722, 722, 722, 1000, 722, 667, 667, 667, 667, 389, 389, 389, 389, 722, 722,
778, 778, 778, 778, 778, 570, 778, 722, 722, 722, 722, 722, 611, 556, 500,
500, 500, 500, 500, 500, 722, 444, 444, 444, 444, 444, 278, 278, 278, 278,
500, 556, 500, 500, 500, 500, 500, 570, 500, 556, 556, 556, 556, 500, 556,
500
};
/*
* Width table for Helvetica with the iso8859-1 encoding
*/
static int hv_wx[256] = {
278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278,
278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278,
278, 278, 278, 278, 355, 556, 556, 889, 667, 222, 333, 333, 389, 584, 278,
584, 278, 278, 556, 556, 556, 556, 556, 556, 556, 556, 556, 556, 278, 278,
584, 584, 584, 556, 1015, 667, 667, 722, 722, 667, 611, 778, 722, 278, 500,
667, 556, 833, 722, 778, 667, 778, 722, 667, 611, 722, 667, 944, 667, 667,
611, 278, 278, 278, 469, 556, 222, 556, 556, 500, 556, 556, 278, 556, 556,
222, 222, 500, 222, 833, 556, 556, 556, 556, 333, 500, 278, 556, 500, 722,
500, 500, 500, 334, 260, 334, 584, 278, 278, 278, 278, 278, 278, 278, 278,
278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 333, 333, 333, 333, 333,
333, 333, 333, 278, 333, 333, 278, 333, 333, 333, 278, 333, 556, 556, 556,
556, 260, 556, 333, 737, 370, 556, 584, 333, 737, 333, 400, 584, 333, 333,
333, 556, 537, 278, 333, 333, 365, 556, 834, 834, 834, 611, 667, 667, 667,
667, 667, 667, 1000, 722, 667, 667, 667, 667, 278, 278, 278, 278, 722, 722,
778, 778, 778, 778, 778, 584, 778, 722, 722, 722, 722, 667, 667, 611, 556,
556, 556, 556, 556, 556, 889, 500, 556, 556, 556, 556, 278, 278, 278, 278,
556, 556, 556, 556, 556, 556, 556, 584, 611, 556, 556, 556, 556, 500, 556,
500
};
/*
* Width table for Helvetica-Bold with the iso8859-1 encoding
*/
static int hvb_wx[256] = {
278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278,
278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278,
278, 278, 278, 333, 474, 556, 556, 889, 722, 278, 333, 333, 389, 584, 278,
584, 278, 278, 556, 556, 556, 556, 556, 556, 556, 556, 556, 556, 333, 333,
584, 584, 584, 611, 975, 722, 722, 722, 722, 667, 611, 778, 722, 278, 556,
722, 611, 833, 722, 778, 667, 778, 722, 667, 611, 722, 667, 944, 667, 667,
611, 333, 278, 333, 584, 556, 278, 556, 611, 556, 611, 556, 333, 611, 611,
278, 278, 556, 278, 889, 611, 611, 611, 611, 389, 556, 333, 611, 556, 778,
556, 556, 500, 389, 280, 389, 584, 278, 278, 278, 278, 278, 278, 278, 278,
278, 278, 278, 278, 278, 278, 278, 278, 278, 278, 333, 333, 333, 333, 333,
333, 333, 333, 278, 333, 333, 278, 333, 333, 333, 278, 333, 556, 556, 556,
556, 280, 556, 333, 737, 370, 556, 584, 333, 737, 333, 400, 584, 333, 333,
333, 611, 556, 278, 333, 333, 365, 556, 834, 834, 834, 611, 722, 722, 722,
722, 722, 722, 1000, 722, 667, 667, 667, 667, 278, 278, 278, 278, 722, 722,
778, 778, 778, 778, 778, 584, 778, 722, 722, 722, 722, 667, 667, 611, 556,
556, 556, 556, 556, 556, 889, 556, 556, 556, 556, 556, 278, 278, 278, 278,
611, 611, 611, 611, 611, 611, 611, 584, 611, 611, 611, 611, 611, 556, 611,
556
};
/*
* Width table for Courier with the iso8859-1 encoding
*/
static int co_wx[256] = {
600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600,
600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600,
600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600,
600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600,
600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600,
600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600,
600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600,
600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600,
600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600,
600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600,
600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600,
600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600,
600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600,
600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600,
600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600,
600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600,
600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600,
600
};
/*
* Width table for Courier-Bold with the iso8859-1 encoding
*/
static int cob_wx[256] = {
600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600,
600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600,
600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600,
600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600,
600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600,
600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600,
600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600,
600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600,
600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600,
600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600,
600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600,
600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600,
600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600,
600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600,
600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600,
600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600,
600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600, 600,
600
};
/*
* Local functions
*/
static void InitPrintData(Tcl_Interp *interp);
static float GetStringLength(RatFont font, const char *string,
int length);
static void Newpage(Tcl_Interp *interp, Tcl_Channel channel,
const char *subjectArg, MESSAGECACHE *elt);
static void Startpage(Tcl_Interp *interp, Tcl_Channel channel,
const char *subject, MESSAGECACHE *elt, int pagenum);
static void Endpage(Tcl_Channel channel);
static void PsPrintString(Tcl_Interp *interp, Tcl_Channel channel,
RatFont font, Tcl_Encoding enc,
float lm, float hm, const char *string, int length);
static void PrintHeaders(Tcl_Interp *interp, Tcl_Channel channel,
Tcl_Encoding enc, char *hs,
MessageInfo *msgPtr);
static void PrintBody(Tcl_Interp *interp, Tcl_Channel channel,
Tcl_Encoding enc, BodyInfo *bodyInfoPtr);
static int PrintBodyText(Tcl_Interp *interp, Tcl_Channel channel,
Tcl_Encoding enc, BodyInfo *bodyInfoPtr);
static int PrintBodyImage(Tcl_Interp *interp, Tcl_Channel channel,
BodyInfo *bodyInfoPtr);
/*
*----------------------------------------------------------------------
*
* RatPrettyPrintMsg --
*
* Print a message prettily
*
* Results:
* A standard tcl result
*
* Side effects:
* Adds data to the passed channel
*
*----------------------------------------------------------------------
*/
int
RatPrettyPrintMsg(ClientData dummy, Tcl_Interp *interp, int objc,
Tcl_Obj *CONST objv[])
{
MESSAGECACHE *elt;
Tcl_Channel channel;
Tcl_CmdInfo cmdInfo;
MessageInfo *msgPtr;
char *subject, *hs, buf[1024];
Tcl_Obj *oPtr, **bv;
int bc, i;
if (5 != objc) {
Tcl_AppendResult(interp, "wrong # args: should be \"",
Tcl_GetString(objv[0]), " channel header_set msg bodys\"",
(char *) NULL);
return TCL_ERROR;
}
/*
* Get data from options
*/
channel = Tcl_GetChannel(interp, Tcl_GetString(objv[1]), NULL);
hs = Tcl_GetString(objv[2]);
Tcl_GetCommandInfo(interp, Tcl_GetString(objv[3]), &cmdInfo);
msgPtr = (MessageInfo*)cmdInfo.objClientData;
oPtr = RatMsgInfo(interp, msgPtr, RAT_FOLDER_SUBJECT);
subject = Tcl_GetString(oPtr);
elt = RatMessageInternalDate(interp, msgPtr);
/*
* Init print data
*/
InitPrintData(interp);
pagenum = 0;
/* Print prelude & prolog */
Tcl_WriteChars(channel, "%!PS-Adobe-3.0\n"
"%%Createor: TkRat\n"
"%%Pages: (atend)\n"
"%%DOcumentData: Clean7Bit\n", -1);
snprintf(buf, sizeof(buf),
"%%%%Orientation: %s\n"
"%%%%DocumentNeededResources: font %s\n%%%%+ font %s\n",
(portrait ? "Portrait" : "Landscape"), font, boldfont);
Tcl_WriteChars(channel, buf, -1);
Tcl_WriteChars(channel, "%%EndComments\n", -1);
Tcl_WriteChars(channel, prolog, -1);
snprintf(buf, sizeof(buf),
"/smallfont /%s findfont %.2f scalefont ISOEncode def\n",
font, fontsize/2.0);
Tcl_WriteChars(channel, buf, -1);
snprintf(buf, sizeof(buf),
"/textfont /%s findfont %d scalefont ISOEncode def\n",
font, fontsize);
Tcl_WriteChars(channel, buf, -1);
snprintf(buf, sizeof(buf),
"/boldfont /%s findfont %d scalefont ISOEncode def\n",
boldfont, fontsize);
Tcl_WriteChars(channel, buf, -1);
snprintf(buf,sizeof(buf),
"/bigfont /%s findfont %d scalefont ISOEncode def\n",
boldfont, fontsize*2);
Tcl_WriteChars(channel, buf, -1);
Tcl_WriteChars(channel, "%%EndProlog\n", -1);
/* Print page borders etc */
Newpage(interp, channel, subject, elt);
/* Print headers */
PrintHeaders(interp, channel, NULL, hs, msgPtr);
/* Print bodyparts */
Tcl_ListObjGetElements(interp, objv[4], &bc, &bv);
for (i=0; i<bc; i++) {
yPos -= fontsize*1.1;
CHECK_NEWPAGE(interp, channel);
Tcl_GetCommandInfo(interp, Tcl_GetString(bv[i]), &cmdInfo);
PrintBody(interp, channel, NULL, (BodyInfo*)cmdInfo.objClientData);
}
/* Print postludium */
Endpage(channel);
snprintf(buf, sizeof(buf),"%%%%Trailer\n%%%%Pages: %d\n%%%%EOF\n",pagenum);
Tcl_WriteChars(channel, buf, -1);
return TCL_OK;
}
/*
*----------------------------------------------------------------------
*
* InitPrintData --
*
* Initialize printing options
*
* Results:
* None
*
* Side effects:
* Init global data
*
*----------------------------------------------------------------------
*/
static void
InitPrintData(Tcl_Interp *interp)
{
Tcl_Obj *oPtr, *o2Ptr, *o3Ptr;
CONST84 char *s, *f;
int i;
/*
* Papersize
*/
s = Tcl_GetVar2(interp, "option", "print_papersize", TCL_GLOBAL_ONLY);
oPtr = Tcl_GetVar2Ex(interp, "option", "print_papersizes",TCL_GLOBAL_ONLY);
Tcl_ListObjLength(interp, oPtr, &i);
for (i--; i >= 0; i--) {
Tcl_ListObjIndex(interp, oPtr, i, &o2Ptr);
Tcl_ListObjIndex(interp, o2Ptr, 0, &o3Ptr);
if (!strcmp(s, Tcl_GetString(o3Ptr))) {
break;
}
}
Tcl_ListObjIndex(interp, o2Ptr, 1, &o3Ptr);
Tcl_ListObjIndex(interp, o3Ptr, 0, &o2Ptr);
Tcl_GetIntFromObj(interp, o2Ptr, &ps_xsize);
Tcl_ListObjIndex(interp, o3Ptr, 1, &o2Ptr);
Tcl_GetIntFromObj(interp, o2Ptr, &ps_ysize);
ps_xsize -= PS_LEFT_MARGIN + PS_RIGHT_MARGIN;
ps_ysize -= PS_TOP_MARGIN + PS_BOTTOM_MARGIN;
/*
* Orientation
*/
s = Tcl_GetVar2(interp, "option", "print_orientation", TCL_GLOBAL_ONLY);
if (!strcmp("portrait", s)) {
portrait = 1;
} else {
portrait = 0;
i = ps_xsize;
ps_xsize = ps_ysize;
ps_ysize = i;
}
/*
* Fonts
*/
f = Tcl_GetVar2(interp, "option", "print_fontfamily", TCL_GLOBAL_ONLY);
if (!strcasecmp("helvetica", f)) {
font = "Helvetica";
boldfont = "Helvetica-Bold";
font_wx = hv_wx;
boldfont_wx = hvb_wx;
} else if (!strcasecmp("courier", f)) {
font = "Courier";
boldfont = "Courier-Bold";
font_wx = co_wx;
boldfont_wx = cob_wx;
} else {
font = "Times-Roman";
boldfont = "Times-Bold";
font_wx = tir_wx;
boldfont_wx = tib_wx;
}
/*
* Rest of the variables
*/
oPtr = Tcl_GetVar2Ex(interp, "option", "print_fontsize", TCL_GLOBAL_ONLY);
Tcl_GetIntFromObj(interp, oPtr, &fontsize);
oPtr = Tcl_GetVar2Ex(interp, "option", "print_resolution",TCL_GLOBAL_ONLY);
Tcl_GetIntFromObj(interp, oPtr, &resolution);
}
/*
*----------------------------------------------------------------------
*
* GetStringLength --
*
* Get the length of a given string in poscript points
*
* Results:
* the length of the string
*
* Side effects:
* None
*
*----------------------------------------------------------------------
*/
static float
GetStringLength(RatFont font, const char *string, int length)
{
int *wx = tir_wx, i;
float l, size = fontsize;
if (-1 == length) {
length = strlen((char*)string);
}
switch(font) {
case FONT_SMALL: wx = font_wx;
size /= 2;
break;
case FONT_NORMAL: wx = font_wx; break;
case FONT_BOLD: wx = boldfont_wx; break;
case FONT_BIG: wx = boldfont_wx;
size *= 2;
break;
}
for (i=l=0; i<length; i++) {
l += wx[(unsigned char)string[i]];
}
return (l*size)/1000;
}
/*
*----------------------------------------------------------------------
*
* Newpage --
*
* Prepares a new page, finishes off the old one if needed
*
* Results:
* None
*
* Side effects:
* Adds data to the passed channel
*
*----------------------------------------------------------------------
*/
static void
Newpage(Tcl_Interp *interp, Tcl_Channel channel, const char *subjectArg,
MESSAGECACHE *eltArg)
{
static const char *subject;
static MESSAGECACHE *elt;
if (subjectArg) {
subject = subjectArg;
}
if (eltArg) {
elt = eltArg;
}
if (pagenum > 0) {
Endpage(channel);
}
Startpage(interp, channel, subject, elt, ++pagenum);
}
/*
*----------------------------------------------------------------------
*
* Startpage --
*
* Writes start of page data
*
* Results:
* None
*
* Side effects:
* Adds data to the passed channel
*
*----------------------------------------------------------------------
*/
static void
Startpage(Tcl_Interp *interp, Tcl_Channel channel, const char *subject,
MESSAGECACHE *elt, int pagenum)
{
char buf[1024];
const unsigned char *cPtr;
CONST84 char *s;
float x, y, w, h, l;
int objc;
Tcl_Obj **objv, *oPtr;
Tcl_DString ds;
/*
* prepare
*/
snprintf(buf, sizeof(buf), "%%%%Page: %d %d\n"
"save\n"
"%d %d translate\n",
pagenum, pagenum, PS_LEFT_MARGIN, PS_BOTTOM_MARGIN);
Tcl_WriteChars(channel, buf, -1);
/*
* Background
*/
x = SPACE/2;
y = ps_ysize+SPACE/2;
w = ps_xsize-SPACE;
h=fontsize*2.6+SPACE;
snprintf(buf, sizeof(buf), ".8 setgray %.2f %.2f %.2f %.2f rectfill\n"
"0 setgray %.2f %.2f %.2f %.2f rectstroke\n",
x, y-h, w, h, x, y-h, w, h);
Tcl_WriteChars(channel, buf, -1);
/*
* Date
*/
x = SPACE;
y = ps_ysize;
w = fontsize*2.4;
h = fontsize*2.6;
snprintf(buf, sizeof(buf), "%.2f %.2f moveto %.2f 0 rlineto 0 -%.2f "
"rlineto -%.2f 0 rlineto closepath stroke\n", x, y, w, h, w);
Tcl_WriteChars(channel, buf, -1);
sprintf(buf, "%d", elt->day);
l = GetStringLength(FONT_BOLD, buf, -1);
sprintf(buf, "%.2f %.2f moveto boldfont setfont (%d) show\n",
x+w/2-l/2, y-fontsize*1.1, elt->day);
Tcl_WriteChars(channel, buf, -1);
oPtr = Tcl_GetVar2Ex(interp, "t", "months", TCL_GLOBAL_ONLY),
Tcl_ListObjGetElements(interp, oPtr, &objc, &objv);
s = Tcl_GetString(objv[elt->month-1]);
l = GetStringLength(FONT_SMALL, s, -1);
snprintf(buf, sizeof(buf),"%.2f %.2f moveto smallfont setfont (%s) show\n",
x+w/2-l/2, y-fontsize*1.7, s);
Tcl_WriteChars(channel, buf, -1);
sprintf(buf, "%d", elt->year+BASEYEAR);
l = GetStringLength(FONT_SMALL, buf, -1);
sprintf(buf, "%.2f %.2f moveto smallfont setfont (%d) show\n",
x+w/2-l/2, y-fontsize*2.3, elt->year+BASEYEAR);
Tcl_WriteChars(channel, buf, -1);
x += fontsize*3;
y -= fontsize/2;
s = Tcl_GetVar2(interp, "t", "received", TCL_GLOBAL_ONLY);
snprintf(buf, sizeof(buf),"%.2f %.2f moveto smallfont setfont (%s) show\n",
x, y, s);
Tcl_WriteChars(channel, buf, -1);
sprintf(buf, "%.2f %.2f moveto textfont setfont (%02d:%02d) show\n",
x, y-fontsize*1.2, elt->hours, elt->minutes);
Tcl_WriteChars(channel, buf, -1);
/*
* Subject
*/
x = SPACE+DATEWIDTH;
y = ps_ysize-1.7*fontsize;
yPos = ps_ysize-fontsize*4;
Tcl_WriteChars(channel, buf, -1);
s = Tcl_GetVar2(interp, "t", "mail_regarding", TCL_GLOBAL_ONLY);
snprintf(buf, sizeof(buf),"boldfont setfont %.2f %.2f moveto\n(%s) show\n",
x, y, s);
Tcl_WriteChars(channel, buf, -1);
x += GetStringLength(FONT_BOLD, s, -1);
snprintf(buf, sizeof(buf),"textfont setfont %.2f %.2f moveto\n(", x+2, y);
Tcl_WriteChars(channel, buf, -1);
Tcl_UtfToExternalDString(NULL, subject, -1, &ds);
for (cPtr = Tcl_DStringValue(&ds); *cPtr; cPtr++) {
if ('(' == *cPtr || ')' == *cPtr || '\\' == *cPtr) {
Tcl_WriteChars(channel, "\\", 1);
}
if (*cPtr >= 32 && *cPtr < 127) {
Tcl_WriteChars(channel, cPtr, 1);
} else {
snprintf(buf, sizeof(buf), "\\%o", *cPtr);
Tcl_WriteChars(channel, buf, -1);
}
}
Tcl_DStringFree(&ds);
Tcl_WriteChars(channel, ") show\n", -1);
/*
* Page number
*/
s = Tcl_GetVar2(interp, "t", "page", TCL_GLOBAL_ONLY);
x = ps_xsize - PAGENUMWIDTH - GetStringLength(FONT_SMALL, s, -1);
y = ps_ysize-fontsize;
snprintf(buf, sizeof(buf),"%.2f %.2f moveto smallfont setfont (%s) show\n",
x, y, s);
Tcl_WriteChars(channel, buf, -1);
sprintf(buf, "%d", pagenum);
x = ps_xsize-PAGENUMWIDTH/2 - GetStringLength(FONT_BIG, buf, -1)/2;
y = ps_ysize-2*fontsize;
sprintf(buf, "bigfont setfont %.2f %.2f moveto (%d) show\n", x, y,pagenum);
Tcl_WriteChars(channel, buf, -1);
}
/*
*----------------------------------------------------------------------
*
* Endpage --
*
* Writes end of page data
*
* Results:
* None
*
* Side effects:
* Adds data to the passed channel
*
*----------------------------------------------------------------------
*/
static void
Endpage(Tcl_Channel channel)
{
Tcl_WriteChars(channel, "restore\nshowpage\n", -1);
}
/*
*----------------------------------------------------------------------
*
* PsPrintString --
*
* Prints the given string with the given margins. May break the
* line if it does not fit, in that case yPos is updated to point at
* the last line.
*
* Results:
* None
*
* Side effects:
* Adds data to the passed channel and may modify yPos and
* initialize a new page.
*
*----------------------------------------------------------------------
*/
static void
PsPrintString(Tcl_Interp *interp, Tcl_Channel channel, RatFont font,
Tcl_Encoding enc, float lm, float hm,
const char *string, int length)
{
char buf[1024], *fn = "";
const unsigned char *cPtr;
float ll, l;
Tcl_DString ds;
switch (font) {
case FONT_SMALL: fn = "smallfont"; break;
case FONT_NORMAL: fn = "textfont"; break;
case FONT_BOLD: fn = "boldfont"; break;
case FONT_BIG: fn = "bigfont"; break;
}
Tcl_UtfToExternalDString(enc, string, length, &ds);
snprintf(buf, sizeof(buf), "%s setfont %.2f %d moveto\n(", fn, lm, yPos);
Tcl_WriteChars(channel, buf, -1);
for (cPtr = Tcl_DStringValue(&ds), ll=lm; *cPtr; cPtr++) {
ll += (l = GetStringLength(font, cPtr, 1));
if (ll > ps_xsize-hm) {
Tcl_WriteChars(channel, ") show\n", -1);
yPos -= fontsize*1.1;
CHECK_NEWPAGE(interp, channel);
snprintf(buf, sizeof(buf), "%s setfont %.2f %d moveto\n(",
fn, lm, yPos);
Tcl_WriteChars(channel, buf, -1);
ll = lm;
}
if ('(' == *cPtr || ')' == *cPtr || '\\' == *cPtr) {
Tcl_WriteChars(channel, "\\", 1);
}
if (*cPtr >= 32 && *cPtr < 127) {
Tcl_WriteChars(channel, cPtr, 1);
} else {
snprintf(buf, sizeof(buf), "\\%o", *cPtr);
Tcl_WriteChars(channel, buf, -1);
}
}
Tcl_WriteChars(channel, ") show\n", -1);
Tcl_DStringFree(&ds);
}
/*
*----------------------------------------------------------------------
*
* PrintHeaders --
*
* Prints the selected message headers
*
* Results:
* None
*
* Side effects:
* Adds data to the passed channel
*
*----------------------------------------------------------------------
*/
static void
PrintHeaders(Tcl_Interp *interp, Tcl_Channel channel,
Tcl_Encoding enc, char *hs, MessageInfo *msgPtr)
{
Tcl_Obj **fhv, **phv, *oPtr, *o2Ptr;
char buf[1024], *s;
int i, j, l, fhc, phc;
float maxX, *lengths;
if (!strcmp("none", hs)) {
return;
}
/*
* Get headers to print
*/
snprintf(buf, sizeof(buf), "%s headers", msgPtr->name);
Tcl_Eval(interp, buf);
Tcl_ListObjGetElements(interp, Tcl_GetObjResult(interp), &fhc, &fhv);
if (!strcmp("selected", hs)) {
oPtr = Tcl_GetVar2Ex(interp, "option", "show_header_selection",
TCL_GLOBAL_ONLY);
Tcl_ListObjLength(interp, oPtr, &l);
phv = (Tcl_Obj**)ckalloc(sizeof(Tcl_Obj*)*l);
for (i=phc=0; i<l; i++) {
Tcl_ListObjIndex(interp, oPtr, i, &o2Ptr);
s = Tcl_GetString(o2Ptr);
for (j=0; j<fhc; j++) {
Tcl_ListObjIndex(interp, fhv[j], 0, &o2Ptr);
if (!strcasecmp(s, Tcl_GetString(o2Ptr))) {
phv[phc++] = fhv[j];
break;
}
}
}
} else {
phv = fhv;
phc = fhc;
}
/*
* Find maximum length
*/
lengths = (float*)ckalloc(sizeof(float)*phc);
for (i=maxX=0; i<phc; i++) {
Tcl_ListObjIndex(interp, phv[i], 0, &oPtr);
lengths[i] = GetStringLength(FONT_BOLD, Tcl_GetString(oPtr), -1);
if (lengths[i] > maxX) {
maxX = lengths[i];
}
}
maxX += GetStringLength(FONT_BOLD, ": ", 2);
/*
* Print headers
*/
for (i=0; i<phc; i++) {
CHECK_NEWPAGE(interp, channel);
Tcl_ListObjIndex(interp, phv[i], 0, &oPtr);
snprintf(buf, sizeof(buf), "%s:", Tcl_GetString(oPtr));
PsPrintString(interp, channel, FONT_BOLD, enc,
HINDENT + maxX - lengths[i], 0, buf, -1);
Tcl_ListObjIndex(interp, phv[i], 1, &oPtr);
PsPrintString(interp, channel, FONT_NORMAL, enc, HINDENT + maxX + 10,
0, Tcl_GetString(oPtr), -1);
yPos -= fontsize*1.1;
}
/*
* Cleanup
*/
if (!strcmp("selected", hs)) {
ckfree(phv);
}
ckfree(lengths);
}
/*
*----------------------------------------------------------------------
*
* PrintBody --
*
* Prints the given bodypart
*
* Results:
* None
*
* Side effects:
* Adds data to the passed channel
*
*----------------------------------------------------------------------
*/
static void
PrintBody(Tcl_Interp *interp, Tcl_Channel channel, Tcl_Encoding enc,
BodyInfo *bodyInfoPtr)
{
Tcl_Obj *oPtr = RatBodyType(bodyInfoPtr), **objv;
int objc;
char buf[1024], buf2[42];
CONST84 char *m;
Tcl_ListObjGetElements(interp, oPtr, &objc, &objv);
/*
* Try to print it
*/
if (!strcasecmp("TEXT", Tcl_GetString(objv[0]))) {
if (TCL_OK == PrintBodyText(interp, channel, enc, bodyInfoPtr)) {
return;
}
} else if (!strcasecmp("IMAGE", Tcl_GetString(objv[0]))) {
if (TCL_OK == PrintBodyImage(interp, channel, bodyInfoPtr)) {
return;
}
}
/*
* Print failure notice
*/
m = Tcl_GetVar2(interp, "t", "unprintable", TCL_GLOBAL_ONLY);
snprintf(buf2, sizeof(buf2), "%s/%s", Tcl_GetString(objv[0]),
Tcl_GetString(objv[1]));
snprintf(buf, sizeof(buf), m, buf2);
CHECK_NEWPAGE(interp, channel);
PsPrintString(interp, channel, FONT_BOLD, enc, SPACE, SPACE, buf, -1);
yPos -= fontsize*1.1;
}
/*
*----------------------------------------------------------------------
*
* PrintBodyText --
*
* Prints the given text/ bodypart
*
* Results:
* None
*
* Side effects:
* Adds data to the passed channel
*
*----------------------------------------------------------------------
*/
static int
PrintBodyText(Tcl_Interp *interp, Tcl_Channel channel, Tcl_Encoding enc,
BodyInfo *bodyInfoPtr)
{
Tcl_Obj *oPtr;
char *cPtr, *nPtr;
oPtr = RatBodyData(interp, bodyInfoPtr, 0, NULL);
cPtr = Tcl_GetString(oPtr);
while (*cPtr) {
if (NULL == (nPtr = strchr(cPtr, '\n'))) {
nPtr = cPtr + strlen(cPtr)-1;
}
CHECK_NEWPAGE(interp, channel);
PsPrintString(interp, channel, FONT_NORMAL, enc, SPACE, SPACE, cPtr,
nPtr-cPtr);
yPos -= fontsize*1.1;
cPtr = nPtr+1;
}
Tcl_DecrRefCount(oPtr);
return TCL_OK;
}
/*
*----------------------------------------------------------------------
*
* PrintBodyImage --
*
* Prints the given text/ bodypart
*
* Results:
* None
*
* Side effects:
* Adds data to the passed channel
*
*----------------------------------------------------------------------
*/
static int
PrintBodyImage(Tcl_Interp *interp, Tcl_Channel channel, BodyInfo *bodyInfoPtr)
{
Tk_PhotoHandle ph;
Tk_PhotoImageBlock block;
Tcl_Obj *objv[5], *namePtr;
int i, r, l, psw, psh, x, y;
char buf[1024];
unsigned char *p, *bp;
/*
* Get image data
*/
objv[i=0] = Tcl_NewStringObj("image", -1);
objv[++i] = Tcl_NewStringObj("create", -1);
objv[++i] = Tcl_NewStringObj("photo", -1);
objv[++i] = Tcl_NewStringObj("-data", -1);
objv[++i] = RatCode64(RatBodyData(interp, bodyInfoPtr, 0, NULL));
r = Tcl_EvalObjv(interp, i+1, objv, 0);
for (; i>=0; i--) {
Tcl_DecrRefCount(objv[i]);
}
if (TCL_OK != r) {
return TCL_ERROR;
}
namePtr = Tcl_GetObjResult(interp);
Tcl_IncrRefCount(namePtr);
ph = Tk_FindPhoto(interp, Tcl_GetString(namePtr));
Tk_PhotoGetImage(ph, &block);
/*
* Print it
*/
psw = (block.width*72)/resolution;
psh = (block.height*72)/resolution;
if (yPos < psh+SPACE) {
Newpage(interp, channel, NULL, NULL);
}
yPos -= psh;
sprintf(buf, "gsave\n/picstr %d string def\n", block.width*3);
Tcl_WriteChars(channel, buf, -1);
sprintf(buf, "%d %d translate\n", ps_xsize/2-psw/2, yPos);
Tcl_WriteChars(channel, buf, -1);
sprintf(buf, "%d %d scale\n", psw, psh);
Tcl_WriteChars(channel, buf, -1);
sprintf(buf, "%d %d 8 [%d 0 0 -%d 0 %d]\n", block.width, block.height,
block.width, block.height, block.height);
Tcl_WriteChars(channel, buf, -1);
Tcl_WriteChars(channel,
"{currentfile picstr readhexstring pop} false 3 colorimage\n",
-1);
for (l=y=0, p = block.pixelPtr; y < block.height;
y++, p = bp+block.pitch) {
bp = p;
for (x=0; x<block.width; x++, p += block.pixelSize) {
sprintf(buf, "%02x%02x%02x", p[block.offset[0]],
p[block.offset[1]], p[block.offset[2]]);
Tcl_WriteChars(channel, buf, -1);
if (++l == 13) {
Tcl_WriteChars(channel, "\n", -1);
l = 0;
}
}
}
Tcl_WriteChars(channel, "\n", -1);
Tcl_WriteChars(channel, "grestore\n", -1);
/*
* Cleanup
*/
objv[i=0] = Tcl_NewStringObj("image", -1);
objv[++i] = Tcl_NewStringObj("delete", -1);
objv[++i] = namePtr;
r = Tcl_EvalObjv(interp, i+1, objv, 0);
Tcl_DecrRefCount(namePtr);
for (; i>=0; i--) {
Tcl_DecrRefCount(objv[i]);
}
return TCL_OK;
}
syntax highlighted by Code2HTML, v. 0.9.1