Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

pathbbox

50 views
Skip to first unread message

jdaw1

unread,
Mar 19, 2008, 10:28:33 PM3/19/08
to
Below is code, measuring the height above the baseline of a piece of
text, in user space, which should therefore be independent of
rotation.


%!

/TimesNewRomanPS-BoldMT 15 selectfont

90 5 div 1 add cvi
{
gsave newpath 0 0 moveto
(ABEGHKLNTVWXZ1fhijklt) true charpath flattenpath pathbbox 4 1 roll
pop pop pop =
grestore
5 rotate
} repeat


Mac Distiller 8.1.0, 10/23/06, outputs to the log file:

10.125
25.8882
41.3663
55.9918
69.0431
80.1131
89.0882
95.9074
100.125
101.601
100.273
96.2059
89.5666
80.5308
69.3089
56.2502
41.7149
26.2031
10.17


Please, why aren't these nineteen numbers identical? I'm obviously
making a simple error, but am now blind to it. Thank you.

Jeffrey H. Coffield

unread,
Mar 20, 2008, 12:02:14 AM3/20/08
to
Page 632 in the Postscipt Language Manual, Third edition says:

"If the user coordinate system is rotated (other than a multiple of 90
degrees) or skewed, the bounding box returned may be larger than expected."

Jeff Coffield

jdaw1

unread,
Mar 20, 2008, 9:54:00 AM3/20/08
to
Oh, it's that simple, thank you. So I'm going to use the following,
not quite fully debugged.

%!

/TimesNewRomanPS-BoldMT 15 selectfont

% - PathBBox llx lly urx ury
% assesses only parts that will show visibly
/PathBBox
{
21 dict begin

/TrivialDistance 0.0001 def % inconveniently in user space
/MinMaxDefinedX false def
/MinMaxDefinedY false def
/x0y0Defined false def
/MinX /undef def /MaxX /undef def
/MinY /undef def /MaxY /undef def

/SetMinMaxX % takes one parameter
{
1 dict begin
/x exch def
MinMaxDefinedX
{MaxX x lt {/MaxX x store} if MinX x gt {/MinX x store} if}
{/MaxX x store /MinX x store /MinMaxDefinedX true store}
ifelse
end
} def % SetMinMaxX

/SetMinMaxY % takes one parameter
{
1 dict begin
/y exch def
MinMaxDefinedY
{MaxY y lt {/MaxY y store} if MinY y gt {/MinY y store} if}
{/MaxY y store /MinY y store /MinMaxDefinedY true store}
ifelse
end
} def % SetMinMaxY


{ % start move
/y0 exch def /x0 exch def
/x0y0Defined true def
} % end of move
{ % start line
/y1 exch def /x1 exch def
x0 x1 sub abs TrivialDistance ge y0 y1 sub abs TrivialDistance ge
or
{
x0 SetMinMaxX x1 SetMinMaxX
y0 SetMinMaxY y1 SetMinMaxY
} if % non-trivial line segment
/y0 y1 def /x0 x1 def
} % end of line
{ % start curve
/y3 exch def /x3 exch def /y2 exch def /x2 exch def /y1 exch
def /x1 exch def

x0 x1 sub abs TrivialDistance ge x0 x2 sub abs TrivialDistance ge
x0 x3 sub abs TrivialDistance ge or or {true} {
y0 y1 sub abs TrivialDistance ge y0 y2 sub abs TrivialDistance ge
y0 y3 sub abs TrivialDistance ge or or} ifelse
{
x0 SetMinMaxX x3 SetMinMaxX
x1 MaxX gt x1 MinX lt x2 MaxX gt x2 MinX lt or or or
{
% Cubic: a t^3 + b t^2 + c t + x0
/a x0 neg x1 3 mul add x2 3 mul sub x3 add def
/b x0 x1 2 mul sub x2 add 3 mul def
/c x1 x0 sub 3 mul def

a abs 0 gt
{
% Solve first differential for zero
/Discriminant 4 b b mul mul 12 a c mul mul sub def
Discriminant 0 ge
{
/Discriminant Discriminant sqrt def
[ {add} {sub} ]
{
/t -2 b mul Discriminant 4 -1 roll exec 6 a mul div
def
t 0 gt t 1 lt and {a t mul b add t mul c add t mul x0 add
SetMinMaxX} if % if turning point between endpoints
} forall % add, sub
} if % has turning point
} if % a abs 0 gt
} if % x bounds outside MinX to MaxX
y0 SetMinMaxY y3 SetMinMaxY
y1 MaxY gt y1 MinY lt y2 MaxY gt y2 MinY lt or or or
{
/a y0 neg y1 3 mul add y2 3 mul sub y3 add def
/b y0 y1 2 mul sub y2 add 3 mul def
/c y1 y0 sub 3 mul def

a abs 0 gt
{
/Discriminant 4 b b mul mul 12 a c mul mul sub def
Discriminant 0 ge
{
/Discriminant Discriminant sqrt def
[ {add} {sub} ]
{
/t -2 b mul Discriminant 4 -1 roll exec 6 a mul div
def
t 0 gt t 1 lt and {a t mul b add t mul c add t mul y0 add
SetMinMaxY} if % if turning point between endpoints
} forall % add, sub
} if % has turning point
} if % a abs 0 gt
} if % y bounds outside MinY to MaxY
} if % non-trivial curve segment
/y0 y3 def /x0 x3 def
} % end of curve
{ % start close
} % end of close
pathforall

MinMaxDefinedX MinMaxDefinedY and {MinX MinY MaxX MaxY} {x0y0Defined
{x0 y0 2 copy} {PathBBoxEmptyPathError} ifelse} ifelse

end
} def % PathBBox

90 5 div 1 add cvi
{
gsave newpath 0 0 moveto

(ABEGHKLNTVWXZ1fhijklt) true charpath PathBBox 4 1 roll pop pop pop


=
grestore
5 rotate
} repeat


This returns the good enough (but still imperfect)

10.125
10.1755
10.1735
10.1672
10.1696
10.1842
10.1932
10.1873
10.1648
10.1867
10.1876
10.2004
10.1733
10.1746
10.1734
10.1774
10.1771
10.1789
10.17

Jeffrey H. Coffield

unread,
Mar 20, 2008, 10:17:00 AM3/20/08
to
Without doing a lengthy analysis of your code, it looks like the tests
for TrivialDistance could introduce errors that are large enough to
cause the difference in your output.

Jeff Coffield
www.digitalsynergyinc.com

jdaw1

unread,
Mar 20, 2008, 5:43:13 PM3/20/08
to
Thank you.

TrivialDistance deleted.
Fixed possible error if cubic was actually a quadratic.

Latest version of PathBBox available within www.jdawiseman.com/papers/placemat/placemat.ps
if you should want it.

jdaw1

unread,
Mar 20, 2008, 7:23:03 PM3/20/08
to
Code far below outputs stuff immediately below. Still slightly
imperfect: if anyone is willing to wade through the detail,
corrections welcome.

[0 10.125]
[5 10.1755]
[10 10.1735]
[15 10.1672]
[20 10.1696]
[25 10.1842]
[30 10.1931]
[35 10.1872]
[40 10.1648]
[45 10.1867]
[50 10.1876]
[55 10.2004]
[60 10.1733]
[65 10.1746]
[70 10.1733]
[75 10.1774]
[80 10.177]
[85 10.1789]
[90 10.14]

Max - Min =
0.0790844

%!

/TimesNewRomanPS-BoldMT 15 selectfont

% - PathBBox llx lly urx ury
% assesses only parts that will show visibly
/PathBBox
{

22 dict begin

/MinMaxDefinedX false def
/MinMaxDefinedY false def
/x0y0Defined false def
/MinX /undef def /MaxX /undef def
/MinY /undef def /MaxY /undef def

/SetMinMaxX % takes one parameter
{

/z exch def
MinMaxDefinedX
{MaxX z lt {/MaxX z store} {MinX z gt {/MinX z store} if} ifelse}
{/MaxX z store /MinX z store /MinMaxDefinedX true store}
ifelse


} def % SetMinMaxX
/SetMinMaxY % takes one parameter
{

/z exch def
MinMaxDefinedY
{MaxY z lt {/MaxY z store} {MinY z gt {/MinY z store} if} ifelse}
{/MaxY z store /MinY z store /MinMaxDefinedY true store}
ifelse
} def % SetMinMaxY

% First rough bounds from a quick run-through, as doing so can save
doing needless arithmetic in curve routine.


{ % start move
/y0 exch def /x0 exch def
/x0y0Defined true def
} % end of move
{ % start line
/y1 exch def /x1 exch def

x0 SetMinMaxX x1 SetMinMaxX
y0 SetMinMaxY y1 SetMinMaxY

} % end of line
{ % start curve

/y3 exch def /x3 exch def pop pop pop pop
x0 SetMinMaxX x3 SetMinMaxX
y0 SetMinMaxY y3 SetMinMaxY


} % end of curve
{ % start close
} % end of close
pathforall

% Second a slower run-through, not redoing the above, but, where
necessary, thinking hard about curves


{ % start move
/y0 exch def /x0 exch def

} % end of move
{ % start line

/y0 exch def /x0 exch def

} % end of line
{ % start curve
/y3 exch def /x3 exch def /y2 exch def /x2 exch def /y1 exch
def /x1 exch def

x1 MaxX gt x1 MinX lt x2 MaxX gt x2 MinX lt or or or


{
% Cubic: a t^3 + b t^2 + c t + x0
/a x0 neg x1 3 mul add x2 3 mul sub x3 add def
/b x0 x1 2 mul sub x2 add 3 mul def
/c x1 x0 sub 3 mul def

% Solve first differential for zero


a abs 0 gt
{
/Discriminant 4 b b mul mul 12 a c mul mul sub def

Discriminant 0 gt


{
/Discriminant Discriminant sqrt def
[ {add} {sub} ]
{
/t -2 b mul Discriminant 4 -1 roll exec 6 a mul div def
t 0 gt t 1 lt and {a t mul b add t mul c add t mul x0 add
SetMinMaxX} if % if turning point between endpoints
} forall % add, sub

} if % Discriminant 0 gt
}
{
b abs 0 gt
{
/t c -2 div b div def


t 0 gt t 1 lt and {a t mul b add t mul c add t mul x0 add
SetMinMaxX} if % if turning point between endpoints

} if % b abs 0 gt
} ifelse % a abs 0 gt


} if % x bounds outside MinX to MaxX

y1 MaxY gt y1 MinY lt y2 MaxY gt y2 MinY lt or or or


{
/a y0 neg y1 3 mul add y2 3 mul sub y3 add def
/b y0 y1 2 mul sub y2 add 3 mul def
/c y1 y0 sub 3 mul def

a abs 0 gt
{
/Discriminant 4 b b mul mul 12 a c mul mul sub def

Discriminant 0 gt


{
/Discriminant Discriminant sqrt def
[ {add} {sub} ]
{
/t -2 b mul Discriminant 4 -1 roll exec 6 a mul div def
t 0 gt t 1 lt and {a t mul b add t mul c add t mul y0 add
SetMinMaxY} if % if turning point between endpoints
} forall % add, sub

} if % Discriminant 0 gt
}
{
b abs 0 gt
{
/t c -2 div b div def


t 0 gt t 1 lt and {a t mul b add t mul c add t mul y0 add
SetMinMaxY} if % if turning point between endpoints

} if % b abs 0 gt
} ifelse % a abs 0 gt


} if % y bounds outside MinY to MaxY

/y0 y3 def /x0 x3 def
} % end of curve
{ % start close
} % end of close
pathforall

MinMaxDefinedX MinMaxDefinedY and {MinX MinY MaxX MaxY} {x0y0Defined

{x0 y0 2 copy} {nocurrentpoint} ifelse} ifelse

end
} bind def % PathBBox

/sMax 0 def
/sMin 9999999 def

0 1 90
{
/a exch def
gsave a rotate newpath 0 0 moveto


(ABEGHKLNTVWXZ1fhijklt) true charpath PathBBox 4 1 roll pop pop pop

grestore
/s exch def sMax s lt {/sMax s def} if sMin s gt {/sMin s def} if
a 5 mod 0 eq {[a s] ==} if
} for
() = (Max - Min =) = sMax sMin sub ==

0 new messages