Return-Path: Received: from thumper.bellcore.com by greenbush.bellcore.com (4.1/4.7) id for nsb; Thu, 9 Jan 92 20:46:13 EST Received: from wilma.cs.utk.edu by thumper.bellcore.com (4.1/4.7) id for nsb@greenbush; Thu, 9 Jan 92 20:46:10 EST Received: from LOCALHOST by wilma.cs.utk.edu with SMTP (5.61++/2.7c-UTK) id AA02750; Thu, 9 Jan 92 20:45:55 -0500 Message-Id: <9201100145.AA02750@wilma.cs.utk.edu> From: Keith Moore To: Nathaniel Borenstein Cc: moore@cs.utk.edu Subject: comments on mime draft, esp. richtext In-Reply-To: Your message of "Tue, 07 Jan 92 15:14:49 EST." Content-Type: multipart/mixed; boundary="xyzzy" Date: Thu, 09 Jan 92 20:45:54 EST Sender: moore@cs.utk.edu --xyzzy content-type: text/richtext content-transfer-encoding: quoted-printable A few random things: (a) it looks like t= here are strange font changes in the PostScript version. it switches from t= imes-roman to helvetica to helvetica bold to courier at random places. (b) on page 25 of the postscript version, near the end of the se= cond paragraph, "but when soft line breaks immediately follow a newline= > or /paragraph>...", I assume you mean "a nl> or /paragraph>..."? (This rule seems a bit odd; I would prefe= r that either newlines are always ignored, or always ignored whenever they = immediately follow a command.) (c) I implemented a richtext-to-groff converter. Using gxditvie= w, I now have the abiltity to read richtext messages on my X window display= . I noticed some oddities with some of the messages that you sent to the li= st. In particular, your mail composer generates things like: excerpt>random textnewline>/excerpt>excerpt><= italic>more textnewline>/excerpt>...where every = line of an excerpt is put in its own excerpt>...<= lt>/excerpt> block. My initial implementation set off each excerpt= from the surrounding text with vertical white space ... which would be rea= sonable if all of the excerpted text appeared together in a single <= lt>excerpt>.../excerpt> block. So this seems a = bit odd. In general it's not clear whether certain commands should cause = a "break" in running text. I assume the following: These caus= e a break: center flushleft flushright indent indentright outdent outdentright samepage excerpt paragraph signature nl np These do not cause a break: bold italic fixed smaller bigger underline subscript superscript heading footing iso-8859-x us-ascii comment no-op lt Also, what is the initial state of the richtext interpreter? Is= text to be wrapped and filled? Justified left, right, or both? what is t= he default font? (fixed or ordinary roman). My richtext-to-groff converter is just a one-evening hack, but l= ots of questions cropped up, and I had only the messages you sent to the li= st as examples. (I have enclosed a copy.) So the question is, is the spec= in RFC-MIME precise enough to ensure good interoperability? This message = looks pretty good on my screen. How does it look on yours? -Keith --xyzzy content-type: text/plain; charset="us-ascii" x-content-type: application/octet-stream; file="richtogroff.c" content-description: richtext-to-groff converter content-transfer-encoding: quoted-printable /* * text/richtext to groff converter * Copyright =A9 January 1992 by Keith Moore * * This program converts mail message bodies of content-type: text/richtex= t * to documents suitable for input to groff. The output can be either * printed or viewed on an X window system display using gxditview. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * = * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include #include int AtBOL =3D 0;=09=09=09/* true if at start of output line */ int Broken =3D 0;=09=09=09/* true if we have emitted a break */ int lineNumber =3D 1;=09=09/* current source line # */ int PageWidth =3D ((5*72)+36);=09/* (in points) 5.5 inches by default */ int PageHeight =3D 8*72; int LeftMargin =3D 0; int RightMargin =3D 0; int PageOffset =3D 0; void forceNewLine () { if (!AtBOL) =09printf ("\n"); AtBOL =3D 1; } void forceBreak () { if (Broken) =09return; forceNewLine (); printf (".br\n"); Broken =3D 1; } struct state { int font; #define FontPlain=0900 #define FontBold=0901 #define FontItalic=0902 #define FontFixed=0904 int adjust; #define AdjustBoth=0900 #define AdjustLeft=0901 #define AdjustRight=0902 #define AdjustCenter=0903 int leftMargin; int rightMargin; }; struct state currentState =3D { FontPlain, AdjustBoth, 0, 0 }; /* * font names for groff, assuming PostScript or X11 drivers */ char *fontnames[] =3D { =09=09=09=09/* FIB */ "TR",=09=09=09/* 000 (Times-Roman) */ "TB",=09=09=09/* 001 (Times-Bold) */ "TI",=09=09=09/* 010 (Times-Italic) */ "TBI",=09=09=09/* 011 (Times-BoldItalic) */ "CR",=09=09=09/* 100 (Courier-Roman) */ "CB",=09=09=09/* 101 (Courier-Bold) */ "CI",=09=09=09/* 110 (Courier-Italic) */ "CBI",=09=09=09/* 111 (Courier-BoldItalic) */ }; char *adjust[] =3D { ".ad b\n.fi\n",=09=09/* AdjustBoth */ ".ad l\n.fi\n",=09=09/* AdjustLeft */ ".ad r\n.fi\n",=09=09/* AdjustRight */ ".nf\n.ce 10000\n",=09=09/* AdjustCenter */ }; void changeState (oldState, newState, undo) struct state *oldState, *newState; int undo; { if (undo =3D=3D 0) { =09if (oldState->font !=3D newState->font) { =09 printf ("\\f[%s]", fontnames[newState->font]); =09 AtBOL =3D 0; =09 Broken =3D 0; =09} } if (oldState->leftMargin !=3D newState->leftMargin) { =09forceBreak (); =09printf (".in %4.2fi\n", (float) newState->leftMargin / 72.0); =09AtBOL =3D 1; } if (oldState->rightMargin !=3D newState->rightMargin) { =09forceBreak (); =09printf (".ll %4.2fi\n", =09=09(float) (PageWidth - newState->rightMargin) / 72.0); =09AtBOL =3D 1; } if (oldState->adjust !=3D newState->adjust) { =09forceBreak (); =09if (oldState->adjust =3D=3D AdjustCenter) =09 printf (".ce 0\n"); =09printf ("%s", adjust[newState->adjust]); =09AtBOL =3D 1; } if (undo =3D=3D 1) { =09if (oldState->font !=3D newState->font) { =09 printf ("\\f[%s]", fontnames[newState->font]); =09 AtBOL =3D 0; =09 Broken =3D 0; =09} } currentState =3D *newState; } void changeFont (font) { struct state newState; newState =3D currentState; newState.font |=3D font; changeState (¤tState, &newState, 0); } void smaller (size) { printf ("\\s-%d", size); AtBOL =3D 0; Broken =3D 0; } void bigger (size) { printf ("\\s+%d", size); AtBOL =3D 0; Broken =3D 0; } void beginBlock (adjust) { struct state newState; newState =3D currentState; = forceBreak (); printf (".ne 3\n"); AtBOL =3D 1; newState.adjust =3D adjust; changeState (¤tState, &newState, 0); } void beginExcerpt (x) int x; { struct state newState; newState =3D currentState; newState.font =3D FontItalic; changeState (¤tState, &newState, 0); } void beginExample (x) int x; { struct state newState; newState =3D currentState; newState.font =3D FontFixed; newState.adjust =3D AdjustLeft; newState.rightMargin =3D 0; changeState (¤tState, &newState, 0); } void indentLeft (x) int x; { struct state newState; newState =3D currentState; newState.leftMargin =3D currentState.leftMargin + x; if (newState.leftMargin < 0) =09newState.leftMargin =3D 0; changeState (¤tState, &newState, 0); } void indentRight (x) int x; { struct state newState; newState =3D currentState; newState.rightMargin =3D currentState.rightMargin + x; if (newState.rightMargin < 0) =09newState.rightMargin =3D 0; changeState (¤tState, &newState, 0); } void beginUnderline () { forceNewLine (); printf (".ul 10000\n"); } void endUnderline () { forceNewLine (); printf (".ul 0\n"); } void beginSub () { printf ("\\d\\s-2"); } void endSub () { printf ("\\s+2\\u"); } void beginSuper () { printf ("\\u\\s-2"); } void endSuper () { printf ("\\s+2\\d"); } struct stk { char *cmdName; int (*proc)(); int arg; struct state savedState; struct stk *next; }; struct stk *stack =3D NULL; char * strsave (s) char *s; { char *p =3D (char *) malloc (strlen (s) + 1); strcpy (p, s); return p; } void pushStack (cmdName, proc, arg, state) char *cmdName; int (*proc)(); int arg; struct state *state; { struct stk *ptr =3D (struct stk *) malloc (sizeof (struct stk)); ptr->cmdName =3D strsave (cmdName); ptr->proc =3D proc; ptr->arg =3D arg; ptr->savedState =3D *state; ptr->next =3D stack; stack =3D ptr; } void popStack () { struct stk *ptr; if (stack) { =09ptr =3D stack->next; =09free (stack->cmdName); =09free (stack); =09stack =3D ptr; } } struct cmd { char *cmdName; void (*enter)(); void (*leave)(); int arg; } cmds[] =3D { { "bold", changeFont, NULL, FontBold }, { "italic", changeFont, NULL, FontItalic }, { "fixed", changeFont, NULL, FontFixed }, { "smaller", smaller, bigger, 2 }, { "bigger", bigger, smaller, 2 }, { "example", beginExample, NULL, 0 }, { "underline", beginUnderline, endUnderline, 0 }, { "center", beginBlock, NULL, AdjustCenter }, { "flushleft", beginBlock, NULL, AdjustLeft }, { "flushright", beginBlock, NULL, AdjustRight }, = { "indent", indentLeft, NULL, 18 }, { "indentright", indentRight, NULL, 18 }, { "outdent", indentLeft, NULL, -18 }, { "outdentright", indentRight, NULL, -18 }, /* { "samepage", noOp, NULL, 0 }, */ { "subscript", beginSub, endSub, 0 }, { "superscript", beginSuper, endSuper, 0 }, /* { "heading", noOp, NULL, 0 }, */ /* { "footing", noOp, NULL, 0 }, */ { "excerpt", beginExcerpt, NULL, 0 }, { "paragraph", beginBlock, NULL, AdjustBoth }, /* { "signature", noOp, NULL, 0 }, */ }; void doCommand (s) char *s; { int i; if (*s =3D=3D '/') {=09=09/* leave command */ =09++s; =09if (stack =3D=3D NULL) =09 fprintf (stderr, "%d:stack underflow\n", lineNumber); =09else if (strcmp (s, stack->cmdName) =3D=3D 0) { =09 if (stack->proc) =09=09(*(stack->proc))(stack->arg); =09 changeState (¤tState, &(stack->savedState), 1); =09 popStack (); =09} =09else { =09 fprintf (stderr, "%d: incorrect nesting: found %s expected %s\n", =09=09 lineNumber, s, stack->cmdName); =09} } else {=09=09=09/* enter command */ =09struct cmd *p; =09for (i =3D 0; i < sizeof cmds / sizeof *cmds; ++i) { =09 p =3D &(cmds[i]); =09 if (strcmp (s, p->cmdName) =3D=3D 0) { =09=09pushStack (p->cmdName, p->leave, p->arg, ¤tState); =09=09if (p->enter) =09=09 (*(p->enter))(p->arg); =09=09return; =09 } =09} =09pushStack (s, NULL, 0, ¤tState); } } char * getCmd () { static char buf[52]; char *ptr; int c; ptr =3D buf; while ((c =3D getchar ()) !=3D '>') { =09if (c =3D=3D EOF) { =09 fprintf (stderr, "%d: premature EOF\n", lineNumber); =09 exit (1); =09} =09if (ptr >=3D buf + 51) { =09 fprintf (stderr, "%d: no closing '>'\n", lineNumber); =09 exit (1); =09} =09if (isupper (c)) =09 c =3D tolower (c); =09*ptr++ =3D c; } *ptr =3D '\0'; return buf; } init () { currentState.font =3D FontPlain; currentState.adjust =3D AdjustBoth; currentState.leftMargin =3D LeftMargin; currentState.rightMargin =3D RightMargin; printf (".pl %4.2fi\n", PageHeight / 72.0);=09/* 8 inches tall */ printf (".po %4.2fi\n", PageOffset / 72.0);=09/* 0 page offset */ printf (".ll %4.2fi\n", =09 (float) (PageWidth - currentState.rightMargin) / 72.0); printf (".lt %4.2fi\n", =09 (float) (PageWidth - currentState.rightMargin) / 72.0); printf (".de NP\n");=09/* do page breaks */ printf ("'sp .3i\n"); printf ("'bp\n"); printf ("'sp .3i\n"); printf ("..\n"); printf (".wh -.4i NP\n"); printf (".ft TR\n");=09/* Times-Roman font */ printf (".nh\n");=09=09/* no hyphenation */ printf (".ad l\n");=09=09/* justify both sides ? */ printf (".fi\n");=09=09/* fill lines */ AtBOL =3D 1; Broken =3D 1; } main(argc, argv) char **argv; { int c, i; char *command; double atof(); int nestedComments =3D 0; /* * XXX add command-line parsing for page-width and page-length options */ for (i =3D 1; i < argc; ++i) { =09if (strncmp (argv[i], "width=3D", 6) =3D=3D 0) =09 PageWidth =3D (int) (atof (argv[i] + 6) * 72.0); =09else if (strncmp (argv[i], "height=3D", 7) =3D=3D 0) =09 PageHeight =3D (int) (atof (argv[i] + 7) * 72.0); =09else if (strncmp (argv[i], "left=3D", 5) =3D=3D 0) =09 LeftMargin =3D (int) (atof (argv[i] + 5) * 72.0); =09else if (strncmp (argv[i], "right=3D", 6) =3D=3D 0) =09 RightMargin =3D (int) (atof (argv[i] + 6) * 72.0); =09else if (strncmp (argv[i], "offset=3D", 7) =3D=3D 0) =09 PageOffset =3D (int) (atof (argv[i] + 7) * 72.0); } init (); while ((c =3D getchar ()) !=3D EOF) { =09switch (c) { =09case '<': =09 command =3D getCmd (); =09 /* =09 * hack to make sure a newline following or =09 * is ignored. =09 */ =09 if (strcmp (command, "nl") =3D=3D 0 || =09=09strcmp (command, "/paragraph") =3D=3D 0) { =09=09if ((c =3D getchar ()) =3D=3D EOF) =09=09 break; =09=09else if (c !=3D '\n') =09=09 ungetc (c, stdin); =09 } =09 if (strcmp (command, "comment") =3D=3D 0) { =09=09nestedComments++; =09=09do { while ((c =3D getchar ()) !=3D '<') =09=09=09if (c =3D=3D EOF) { =09=09=09 fprintf (stderr, "%d: premature EOF in comment\n", =09=09=09=09 lineNumber); =09=09=09 exit (1); =09=09=09} =09=09 command =3D getCmd (); =09=09 if (strcmp (command, "comment") =3D=3D 0) =09=09=09++nestedComments; =09=09 else if (strcmp (command, "/comment") =3D=3D 0) =09=09=09--nestedComments; } while (nestedComments > 0); =09=09continue; =09 } else if (strcmp (command, "lt") =3D=3D 0) { putchar ('<'); AtBOL =3D 0; } =09 else if (strcmp (command, "nl") =3D=3D 0) { =09=09if (Broken) { =09=09 printf ("\n"); =09=09 AtBOL =3D 1; =09=09} =09=09else =09=09 forceBreak (); } =09 else if (strcmp (command, "np") =3D=3D 0) { =09=09forceNewLine (); =09=09printf (".NP\n"); AtBOL =3D 1; } =09 else =09=09doCommand (command); =09 break; =09case '\n':=09=09/* special for newline */ =09 lineNumber++; =09 if (!AtBOL) { =09=09printf (" "); =09=09AtBOL =3D 0; =09=09Broken =3D 0; =09 } =09 break; =09case '\\':=09=09/* special for backslash */ =09 printf ("\\e"); =09 AtBOL =3D 0; =09 Broken =3D 0; =09 break; =09case '.':=09=09/* special for '.' at start of line */ =09 if (AtBOL) =09=09printf ("\\."); =09 else =09=09printf ("."); =09 AtBOL =3D 0; =09 Broken =3D 0; =09 break; =09default: putchar (c); AtBOL =3D 0; =09 Broken =3D 0; =09 break; =09} } } --xyzzy--