groff
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [Groff] Using pic/groff/gs to produce gif/jpeg


From: joerg van den hoff
Subject: Re: [Groff] Using pic/groff/gs to produce gif/jpeg
Date: Wed, 11 May 2005 16:44:39 +0200
User-agent: Mozilla Thunderbird 1.0 (Macintosh/20041206)

David Griffiths wrote:
Hi, I'm new to pic/groff and I've been having some problems producing either gif or jpeg images using them. The basic problem seems to be that pic/groff/postscript all have the concept of a page that they are drawing on whereas I want the resulting image to have all the whitespace cropped from the margins. This is to go in a web page so the idea of a page doesn't make sense.

Anyway I started out following some instructions here: http://www.spinellis.gr/sw/umlgraph/doc/faq.html#antialias but the resulting command was very slow. Here are my findings as pasted from my internal blog. I wondered if they made sense or if I'm missing some simpler approach to the whole thing (btw I need the gs stage because using -T gif in groff produces very poor text, no antialiasing).

Thanks!

Dave

 From my blog:

I missed the hint in the FAQ and after a bit of messing around got the following command to work:

pic test | groff | ps2eps | gs -dDEVICEHEIGHTPOINTS=1000 -q -r360 -dNOPAUSE -sDEVICE=pnm -sOutputFile=- - -c quit | pnmcrop | pnmscale 0.5 | ppmtogif > test.gif

I had to add the -dDEVICEHEIGHTPOINTS=1000 option because without that it chopped off the top of the picture.

Anyway, I think I prefer this approach if only because the pnmscale command gives me the option to scale the size of the image. Plus ppmtogif gives me control over transparency.

Posted by D. H. Griffiths on May 04, 2005 at 07:02 AM EDT
I've been playing around with this some more because the command above was just so slow (eg 17 seconds on my T40 for a trivial diagram). Turns out that most of that time is spent in pnmcrop. The image is far larger than it needs to be (as specified by the resolution argument -r360) and is then shrunk back down again with pnmscale (for my tests I'm using pnmscale 0.25). Why is that? Well if you use -r90 and get rid of the pnmscale, the text looks ragged as though it's missing antialiasing. But I found that you could overcome that by specifying -dTextAlphaBits=4 in the gs command. So you end up with a command like this:

pic test | groff | ps2eps -q | gs -dTextAlphaBits=4 -dDEVICEHEIGHTPOINTS=1000 -q -r90 -dNOPAUSE -sDEVICE=pnm -sOutputFile=- - -c quit | pnmcrop | ppmtogif > test.gif

now that's about four times faster because the intermediate pnm image is four times smaller. This command took about 4 seconds on my Thinkpad. But I found a still faster way.

Why bother to go to pnm format in the first place, why can't the image be created directly by ghostscript? Well the answer is that can, but ghostscript can only produce jpeg images, not gif. Which is fine for me. The next issue relates to the pnmcrop command. What that does is chop blank space from around your image. The problem is that a lot of what groff and postscript do revolves round the concept of writing to a page (typically letter size). Pic for instance has its coordinate origin at the bottom left but positions your image at the top of the page. If you play around you run into all sorts of issues with the image having white space above or below or the image top gets chopped off because it's above the top of the imaginary page and so on. After a lot of messing around I came up with this version of the command:

pic test | groff | ps2eps -q -g | fixbox | gs -dTextAlphaBits=4 -q -r90 -dNOPAUSE -sDEVICE=jpeg -sOutputFile=- - -c quit > test.jpg

the -g option to ps2eps is important because that tells it to use the "internal bbox device of ghostscript" (whatever that means!), but anyway it prevents ps2eps from setting the top of the bounding box to the top of the default page. It's important because without it the diagram goes above the bounding box set by ps2eps and the image gets cropped.

fixbox is a small python script I wrote that is a much simplified version of epstopdf. What this does is to move the image to the bottom left corner and set the page size to just include the bounding box. Using this means that there is no longer a need for cropping. The script is here:

#!/usr/bin/python
# This script converts the BoundingBox of a postscript file so that the origin is at # the bottom left corner (0, 0). It also sets the pagesize which avoids the need for
# cropping blank space from the image.

import sys
import os
import re

inlines = sys.stdin.readlines()

for line in inlines:
    if line.find("%%BoundingBox: ") != -1:
        tokens = line.split()
        minx = int(tokens[1])
        miny = int(tokens[2])
        maxx = int(tokens[3])
        maxy = int(tokens[4])
        new_maxx = maxx - minx
        new_maxy = maxy - miny
        print "%%BoundingBox: 0 0 " + str(new_maxx) + " " + str(new_maxy)
print "<< /PageSize [" + str(new_maxx + 10) + " " + str(new_maxy + 10) + "] >> setpagedevice"
        print "gsave -" + str(minx) + " -" + str(miny - 1) + " translate"
    else:
        print line,

print "grestore"

And finally, the best news is that this takes just 1 second to run!



_______________________________________________
Groff mailing list
address@hidden
http://lists.gnu.org/mailman/listinfo/groff



I usually convert the postscript output from groff to EPS with `ps2epsi` (George Cameron), a script which drives `gs' and most of the time makes the bounding box really fit the picture. don't now if this is in the standard distribution together with `gs' (see attachment). the eps-output more often - in my experience - has the correct bounding box
than obtained with `ps2eps' (Roland Bless)
and something (ps2epsi does not like pipes ...) like

groff -p file.pic > file.ps
ps2epsi file.ps
convert file.epsi file.jpeg

(`convert' from the ImageMagick package)

produces the desired result.

joerg
#!/bin/sh
# $Id: ps2epsi,v 1.9 2002/02/21 21:49:28 giles Exp $

tmpfile=/tmp/ps2epsi$$

export outfile

if [ $# -lt 1 -o $# -gt 2 ]; then
        echo "Usage: `basename $0` file.ps [file.epsi]" 1>&2
        exit 1
fi

infile=$1;

if [ $# -eq 1 ]
then
        case "${infile}" in
          *.ps)         base=`basename "${infile}" .ps` ;;
          *.cps)        base=`basename "${infile}" .cps` ;;
          *.eps)        base=`basename "${infile}" .eps` ;;
          *.epsf)       base=`basename "${infile}" .epsf` ;;
          *)            base=`basename "${infile}"` ;;
        esac
        outfile=${base}.epsi
else
        outfile=$2
fi

ls -l "${infile}" |
awk 'F==1       {
                cd="%%CreationDate: " $6 " " $7 " " $8;
                t="%%Title: " $9;
                f="%%For:" U " " $3;
                c="%%Creator: Ghostscript ps2epsi from " $9;
                next;
                }
        /^%!/   {next;}
        /^%%Title:/     {t=$0; next;}
        /^%%Creator:/   {c=$0; next;}
        /^%%CreationDate:/      {cd=$0; next;}
        /^%%For:/       {f=$0; next;}
        !/^%/   {
                print "/ps2edict 30 dict def";
                print "ps2edict begin";
                print "/epsititle (" t "\\n) def";
                print "/epsicreator (" c "\\n) def";
                print "/epsicrdt (" cd "\\n) def";
                print "/epsifor (" f "\\n) def";
                print "end";
                exit(0);
                }
        ' U="$USERNAME$LOGNAME"  F=1 - F=2 "${infile}" >$tmpfile

gs -q -dNOPAUSE -dSAFER -dDELAYSAFER -r72 -sDEVICE=bit -sOutputFile=/dev/null 
$tmpfile ps2epsi.ps $tmpfile <"${infile}" 1>&2
rm -f $tmpfile

(
cat << BEGINEPS
save countdictstack mark newpath /showpage {} def /setpagedevice {pop} def
%%EndProlog
%%Page 1 1
BEGINEPS

cat "${infile}" |
sed -e '/^%%BeginPreview:/,/^%%EndPreview[^!-~]*$/d' -e '/^%!PS-Adobe/d'\
        -e '/^%%[A-Za-z][A-Za-z]*[^!-~]*$/d' -e '/^%%[A-Za-z][A-Za-z]*: /d'

cat << ENDEPS
%%Trailer
cleartomark countdictstack exch sub { end } repeat restore
%%EOF
ENDEPS

) >> "${outfile}"

exit 0

reply via email to

[Prev in Thread] Current Thread [Next in Thread]