#!/bin/sh -
#
# $Id$
#
# Experimental awk-script for conversion of tpic \specials as produced
# by (groff-)pic into pdfTeX \pdfliteral sections for further processing
# by pdftex.
#
# Usage:
# $ pic -t somefile.pic | tpic2pdftex > somefile.tex
#
# Process somefile.tex by pdftex/pdflatex.
#
# tpic \special desciption see e. g.:
# Goossens, Rahtz, Mittelbach: The LaTeX Graphics Companion,
# Addison-Wesley, 1997, pp. 464.
#
# Bugs:
# Spline curve shapes not fully authentic (unknown algorithm).
# Bounding box does not care for line thickness (groff pic feature).
# Splines might be outside bounding box.
#
# Copyright (C) 2002--2011 by Hartmut Henkel
#
# 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, see .
#
# The author may be contacted via the e-mail address
#
# hartmut_henkel@gmx.de
#
# NEWS:
# 11 Jun. 2011 - sh construct portability
# (patch from Nelson Beebe)
# 24 Dec. 2009 - use gawk for default awk only if it exists
# (patch by Karl Berry).
# 14 Jan. 2007 - make it executable shell script, calling awk
# 16 Dec. 2004 - Replaced // {print} by {print} (some AWKs did choke)
# 09 Apr. 2004 - Locale check: Decimal point in float numbers?
# 30 Oct. 2003 - Replaced print statements by printf to avoid
# underflow numbers like 1e-14 in \pdfliterals. Remove trailing
# zeroes of floating point numbers.
# 02 May 2003 - Lines starting with \ allow TeX insertions,
# e. g. of pdfTeX \pdfliteral{}
# 29 Apr. 2003 - Changed for pic of groff 1.19
# 16 Mar. 2003 - Bug corrected: Dashed lines shorter than minimum
# dash-pause length now drawn solid.
# 11 Nov. 2002 - Spline drawing improved: First half of first and last
# half of last spline segments are drawn by straight lines.
# 28 Nov. 2002 - Arc and circle drawing cleaned up. Full circle is now
# drawn by 4 Bezier curves, as is common use. Arcs split evenly into
# Bezier curves, to minimize max. error.
# 02 Dec. 2002 - Experimental pic (groff > 1.18.1) with improved
# vertical picture positioning supported.
# 04 Dec. 2002 - Experiment with modified pic (\vtop -> \vbox),
# Formula for Bezier constant c reduced.
#
########################################################################
# In case someone pedantic insists on using grep -E.
: ${EGREP=egrep}}
# Systems which define $COMSPEC or $ComSpec use semicolons to separate
# directories in TEXINPUTS -- except for Cygwin et al., where COMSPEC
# might be inherited, but : is used.
if test -n "$COMSPEC$ComSpec" \
&& uname | $EGREP -iv 'cygwin|mingw|djgpp' >/dev/null; then
path_sep=";"
else
path_sep=":"
fi
# findprog PROG
# -------------
# Return true if PROG is somewhere in PATH, else false.
findprog ()
{
local saveIFS="$IFS"
IFS=$path_sep # break path components at the path separator
for dir in $PATH; do
IFS=$saveIFS
# The basic test for an executable is `test -f $f && test -x $f'.
# (`test -x' is not enough, because it can also be true for directories.)
# We have to try this both for $1 and $1.exe.
#
# Note: On Cygwin and DJGPP, `test -x' also looks for .exe. On Cygwin,
# also `test -f' has this enhancement, bot not on DJGPP. (Both are
# design decisions, so there is little chance to make them consistent.)
# Thusly, it seems to be difficult to make use of these enhancements.
#
if { test -f "$dir/$1" && test -x "$dir/$1"; } ||
{ test -f "$dir/$1.exe" && test -x "$dir/$1.exe"; }; then
return 0
fi
done
return 1
}
if test -z "$AWK"; then # if set by user, leave it.
if findprog gawk; then
AWK=gawk
else
AWK=awk
fi
fi
LANG="C"; export LANG
AWKPROG='
# begin of awk input file
function qprintf(a) {
gsub(/0* /," ", a); # trailing zeroes in %f
gsub(/\. /," ", a); # orphaned decimal dots
gsub(/0*]/,"]", a); # trailing zeroes in brackets
gsub(/0X/,"0", a); # guard integer zeroes
gsub(/-0 /,"0 ", a); # correct -0 to 0
print a;
}
function startpdfliteral() {
if (pdfliteral == 0) {
print "\\pdfliteral{";
printf("q [] 0 d %d J %d j\n", linecap, linejoin); # no qprintf!
qprintf(sprintf("%f w", linethickness * wscale));
}
pdfliteral = 1;
}
function stoppdfliteral() {
if (pdfliteral == 1) {
print "Q";
print "}%";
}
pdfliteral = 0;
}
########################################################################
BEGIN{
wscale = 72.0 / 1000;
tpicmode = 0;
pdfliteral = 0;
pointbuf = 0;
filled = 0;
fillval = 0;
linecap = 1;
linejoin = 1;
defaultlinethickness = 8;
drawarc = 0;
pi = atan2(0, -1);
if (match(sprintf("%f", 0.5), /\./) == 0) {
print "ERROR: Floating point numbers miss decimal point. Do"
print " LANG=\"C\"; export LANG"
print "before calling awk."
print "ERROR: Floating point numbers miss decimal point. Do" > "/dev/stderr"
print " LANG=\"C\"; export LANG" > "/dev/stderr"
print "before calling awk." > "/dev/stderr"
exit 1;
}
}
########################################################################
# the following expression triggers tpic processing for pic <= 1.18.1
/^\\setbox\\graph=\\vtop{/ {
pdfliteral = 0;
tpicmode = 1;
linethickness = defaultlinethickness;
}
# the following expression triggers tpic processing for pic = 1.19
/^\\expandafter\\setbox\\csname graph\\endcsname/ {
pdfliteral = 0;
tpicmode = 1;
linethickness = defaultlinethickness;
}
# TeX parts end \pdfliteral, and also TeX parts embedded in .PS ... .PE
# section end \pdfliteral
/^ *\\graphtemp|^ *\\rlap|^ *\\advance|^\\|^ *\\hbox/ {
if(tpicmode == 1)
stoppdfliteral();
}
/^}%/ {
if(tpicmode == 1)
tpicmode = 0;
}
########################################################################
# all specials handling
/^ *\\special/ {
if(tpicmode == 1)
startpdfliteral();
}
# set pen size
/^ *\\special{pn/ {
gsub(/[{}]/, " ");
linethickness = $3 + 0;
qprintf(sprintf("%f w", linethickness * wscale));
next;
}
# add point to path
/^ *\\special{pa/ {
gsub(/[{}]/, " ");
x[pointbuf] = $3 + 0;
y[pointbuf] = $4 + 0;
pointbuf++;
next;
}
# print path as straight lines
/^ *\\special{fp/ {
if (filled == 1)
qprintf(sprintf("q %f g", 1 - fillval));
qprintf(sprintf("%f %f m", x[0] * wscale, -y[0] * wscale));
for (i = 1; i < pointbuf; i++)
qprintf(sprintf("%f %f l", x[i] * wscale, -y[i] * wscale));
if (filled == 1)
print "B Q";
else
print "S";
pointbuf = 0;
filled = 0;
next;
}
# print path as straight dashed lines
/^ *\\special{da/ {
gsub(/[{}]/, " ");
don = ($3 + 0) * 1000;
if (filled == 1) {
qprintf(sprintf("q %f g", 1 - fillval));
qprintf(sprintf("%f %f m", x[0] * wscale, -y[0] * wscale));
for (i = 1; i < pointbuf; i++)
qprintf(sprintf("%f %f l", x[i] * wscale, -y[i] * wscale));
print "f Q";
}
for (i = 1; i < pointbuf; i++) {
dx = x[i] - x[i - 1];
dy = y[i] - y[i - 1];
len = sqrt(dx * dx + dy * dy);
non = int(0.5 * len / don + 0.75);
noff = non - 1;
lon = don * non;
loff = len - lon;
if(noff > 0) {
doff = loff / noff;
qprintf(sprintf("q [%f %f] 0X d", don * wscale, doff * wscale));
} else {
print "q [] 0 d";
}
qprintf(sprintf("%f %f m", x[i - 1] * wscale, -y[i - 1] * wscale));
qprintf(sprintf("%f %f l", x[i] * wscale, -y[i] * wscale));
print "S Q";
}
pointbuf = 0;
filled = 0;
next;
}
# print path as straight dotted lines
/^ *\\special{dt/ {
gsub(/[{}]/, " ");
dt = ($3 + 0) * 1000;
if (filled == 1) {
qprintf(sprintf("q %f g", 1 - fillval));
qprintf(sprintf("%f %f m", x[0] * wscale, -y[0] * wscale));
for (i = 1; i < pointbuf; i++)
qprintf(sprintf("%f %f l", x[i] * wscale, -y[i] * wscale));
print "f Q";
}
for (i = 1; i < pointbuf; i++) {
dx = x[i] - x[i - 1];
dy = y[i] - y[i - 1];
len = sqrt(dx * dx + dy * dy);
dl = int (len / dt + 0.5);
if (!dl)
dtl = len;
else
dtl = len / dl;
qprintf(sprintf("q [0X %f] 0X d", dtl * wscale));
qprintf(sprintf("%f %f m", x[i - 1] * wscale, -y[i - 1] * wscale));
qprintf(sprintf("%f %f l", x[i] * wscale, -y[i] * wscale));
print "S Q";
}
pointbuf = 0;
filled = 0;
next;
}
# like , but path actually not drawn
/^ *\\special{ip/ {
if (filled == 1)
qprintf(sprintf("q %f g", 1 - fillval));
qprintf(sprintf("%f %f m", x[0] * wscale, -y[0] * wscale));
for (i = 1; i < pointbuf; i++)
qprintf(sprintf("%f %f l", x[i] * wscale, -y[i] * wscale));
if (filled == 1)
print "f Q";
else
print "f";
pointbuf = 0;
filled = 0;
next;
}
# like , but path printed as splines
/^ *\\special{sp/ {
gsub(/[{}]/, " ");
don = ($3 + 0) * 1000;
a = 0.68; # fudge, visually optimized
x[pointbuf] = x[pointbuf - 1];
y[pointbuf] = y[pointbuf - 1];
if (don > 0)
qprintf(sprintf("q [%f] 0X d", don * wscale));
if (don < 0)
qprintf(sprintf("q [0X %f] 0X d", -don * wscale));
qprintf(sprintf("%f %f m", x[0] * wscale, -y[0] * wscale));
if(pointbuf < 3)
qprintf(sprintf("%f %f l", x[pointbuf - 1] * wscale, -y[pointbuf - 1] * wscale));
else {
qprintf(sprintf("%f %f l", 0.5 * (x[0] + x[1]) * wscale, \
-0.5 * (y[0] + y[1]) * wscale)); # start straight, see cstr116.ps
for (i = 1; i < pointbuf - 1; i++)
qprintf(sprintf("%f %f %f %f %f %f c", \
(a * x[i] + (1 - a) * 0.5 * (x[i] + x[i - 1])) * wscale, \
-(a * y[i] + (1 - a) * 0.5 * (y[i] + y[i - 1])) * wscale, \
(a * x[i] + (1 - a) * 0.5 * (x[i] + x[i + 1])) * wscale, \
-(a * y[i] + (1 - a) * 0.5 * (y[i] + y[i + 1])) * wscale, \
0.5 * (x[i] + x[i + 1]) * wscale, -0.5 * (y[i] + y[i + 1]) * wscale));
qprintf(sprintf("%f %f l", x[pointbuf - 1] * wscale, -y[pointbuf - 1] * wscale));
}
if (filled == 1) {
qprintf(sprintf("q %f g", 1 - fillval));
print "B Q";
}
else
print "S";
if (don != 0)
print "Q";
pointbuf = 0;
filled = 0;
next;
}
# prepare shading of object interior
/^ *\\special{sh/ {
gsub(/[{}]/, " ");
fillval = $3 + 0;
filled = 1;
next;
}
# draw arc
# like , but arc actually not drawn
/^ *\\special{ar/ {
drawarc = 1;
}
/^ *\\special{ar|^ *\\special{ia/ {
gsub(/[{}]/, " ");
xc = $3 + 0;
yc = $4 + 0;
rx = $5 + 0;
ry = $6 + 0;
s = $7 + 0;
e = $8 + 0;
if (e - s > 2 * pi) e = s + 2 * pi;
if (s - e > 2 * pi) e = s - 2 * pi;
curvespercircle = 4; # max. number Bezier curves per circle
phi_max = 1.001 * 2 * pi / curvespercircle;
if (e > s)
imax = int ((e - s) / phi_max) + 1;
else
imax = int ((s - e) / phi_max) + 1;
phi = (e - s) / imax;
# parameter for Bezier control vectors, c(90 deg.) = 0.55228...:
c = 4 * (1 - cos(0.5 * phi)) / (3 * sin(0.5 * phi));
x0 = rx * cos(s) + xc;
y0 = ry * sin(s) + yc;
qprintf(sprintf("%f %f m", x0 * wscale, -y0 * wscale));
for (i = 0; i < imax; i++) {
x1 = x0 - rx * c * sin(s + i * phi);
y1 = y0 + ry * c * cos(s + i * phi);
x3 = rx * cos(s + (i + 1) * phi) + xc;
y3 = ry * sin(s + (i + 1) * phi) + yc;
x2 = x3 + rx * c * sin(s + (i + 1) * phi);
y2 = y3 - ry * c * cos(s + (i + 1) * phi);
qprintf(sprintf("%f %f %f %f %f %f c", x1 * wscale, -y1 * wscale, \
x2 * wscale, -y2 * wscale, x3 * wscale, -y3 * wscale));
x0 = x3;
y0 = y3;
}
if(drawarc == 1) {
if (filled == 1) {
qprintf(sprintf("h q %f g", 1 - fillval));
print "B Q";
}
else
print "S";
} else {
if (filled == 1) {
qprintf(sprintf("h q %f g", 1 - fillval));
print "f Q";
}
else
print "f";
}
filled = 0;
drawarc = 0;
next;
}
########################################################################
{print}
########################################################################
'
# end of awk input file
$AWK "$AWKPROG" "$@"