Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]
Groups > comp.lang.postscript > #1838 > unrolled thread
| Started by | jdaw1 <jdawiseman@gmail.com> |
|---|---|
| First post | 2014-03-29 10:27 -0700 |
| Last post | 2014-04-06 16:00 -0700 |
| Articles | 8 — 2 participants |
Back to article view | Back to comp.lang.postscript
How 'thick' is a glyph? jdaw1 <jdawiseman@gmail.com> - 2014-03-29 10:27 -0700
Re: How 'thick' is a glyph? Mark Carroll <mtbc@bcs.org> - 2014-03-29 17:44 +0000
Re: How 'thick' is a glyph? jdaw1 <jdawiseman@gmail.com> - 2014-03-29 13:07 -0700
Re: How 'thick' is a glyph? jdaw1 <jdawiseman@gmail.com> - 2014-03-31 02:38 -0700
Re: How 'thick' is a glyph? jdaw1 <jdawiseman@gmail.com> - 2014-03-31 16:36 -0700
Re: How 'thick' is a glyph? jdaw1 <jdawiseman@gmail.com> - 2015-04-07 04:25 -0700
Re: How 'thick' is a glyph? jdaw1 <jdawiseman@gmail.com> - 2015-04-07 04:27 -0700
Re: How 'thick' is a glyph? jdaw1 <jdawiseman@gmail.com> - 2014-04-06 16:00 -0700
| From | jdaw1 <jdawiseman@gmail.com> |
|---|---|
| Date | 2014-03-29 10:27 -0700 |
| Subject | How 'thick' is a glyph? |
| Message-ID | <e6b8d0d6-4596-46fc-a95a-7b779e46b4b5@googlegroups.com> |
This question asks how a PostScript program can know how thick is a glyph.
What do I mean by "thick"?
Start with
http://www.jdawiseman.com/2013/20131011_champagne_ahb.pdf
On the first page observe the styling of the large letters ("MC", "H", "ED"). This is made with a charpath, a clip, and then repeated gsave stroke grestore: very thick black; slightly less thick white; less thick black; etc.
Works well. Looks good. Also see
http://www.jdawiseman.com/2014/20140402_g80.pdf
http://www.jdawiseman.com/2014/20140422_1966.pdf
How big does the first setlinewidth need to be, to cover the whole character? That is, how thick is the font?
Currently my code needs the user to choose this. But can it be chosen by the PostScript? Can the PostScript know, for any given glyph (or even at the level of a font), what is the greatest distance between an interior point and the edge?
For those interested, this question is about the InlineTitles parameters in
http://www.jdawiseman.com/papers/placemat/placemat.html#inlinetitles
Thank you.
[toc] | [next] | [standalone]
| From | Mark Carroll <mtbc@bcs.org> |
|---|---|
| Date | 2014-03-29 17:44 +0000 |
| Message-ID | <87lhvsdhmz.fsf@ixod.org> |
| In reply to | #1838 |
jdaw1 <jdawiseman@gmail.com> writes: > How big does the first setlinewidth need to be, to cover the whole > character? That is, how thick is the font? If I'm understanding you correctly at all, then one thing you /could/ do, is use charpath to get the outline of a capital I, flattenpath to turn that to line segments, sort the segments by height, and for the segments that intersect the vertical midpoint, find the leftmost and rightmost points. Hopefully there's a better solution coming from somebody else though. -- Mark
[toc] | [prev] | [next] | [standalone]
| From | jdaw1 <jdawiseman@gmail.com> |
|---|---|
| Date | 2014-03-29 13:07 -0700 |
| Message-ID | <9d97d353-efe7-4ca3-bccf-71c16029fa36@googlegroups.com> |
| In reply to | #1839 |
That is at least three things: interesting; a kludge; and sometimes wrong. The first two are obvious. Third, part I. It might be that other glyphs contain wider strokes than the 'I'. For example, the bottom two corners in a 'W' can contain points further from the edge than happens in the middle of a straight pen stroke. Third, part II. What if the 'I' bows outward. That is, what if the narrowest part of the vertical stroke is at the vertical middle? That would cause my code to do too few 'gsave stroke grestore's.
[toc] | [prev] | [next] | [standalone]
| From | jdaw1 <jdawiseman@gmail.com> |
|---|---|
| Date | 2014-03-31 02:38 -0700 |
| Message-ID | <2989eb23-75da-4333-b6d0-0545f45c2f3d@googlegroups.com> |
| In reply to | #1838 |
Here's a horrible way to do it, imperfectly. There is a string to be 'inlined'. Compute its path. Compute bounding box. Divide the height by 64, and take a grid of points with that spacing. So if the string is long, there will be many more columns than rows. Test each point in the grid with infill, and keep in an array those points that are in the interior of the path. Set WidthMin to be tiny. Set Width to be WidthMin 2 mul. Then strokepath. Forall the stored interior points, infill. If there are any unpainted points, keep only those points, /WidthMin Width def, and repeat. If there are no unpainted points, /WidthMax Width def, and henceforth /Width WidthMin WidthMax add 2 div def. Repeat until WidthMax sufficiently close to WidthMin. This succeeds only if the grid of points is sufficiently dense. But infill is slow, and 64^2 infills, some done many times, would be very slow. Yuck!
[toc] | [prev] | [next] | [standalone]
| From | jdaw1 <jdawiseman@gmail.com> |
|---|---|
| Date | 2014-03-31 16:36 -0700 |
| Message-ID | <db985b7a-bbe2-47d1-a82f-9cbed3dbe9bf@googlegroups.com> |
| In reply to | #1841 |
On testing, it isn't that slow. The string "GC60", in /Optima-Bold, with 128 rows of dots, and the number of columns being a modest multiple of this, using Distiller XI on a 2.7GHz Mac, takes about 1.3 seconds. That's not immediate, but not terrible. Nonetheless, the algorithm is still horrible.
[toc] | [prev] | [next] | [standalone]
| From | jdaw1 <jdawiseman@gmail.com> |
|---|---|
| Date | 2015-04-07 04:25 -0700 |
| Message-ID | <1e8d3aa0-d9f3-4142-ac44-45c61fda0346@googlegroups.com> |
| In reply to | #1846 |
> On testing, it isn't that slow. The string "GC60", in /Optima-Bold, with 128 rows of dots, and the number of columns being a modest multiple of this, using Distiller XI on a 2.7GHz Mac, takes about 1.3 seconds. That's not immediate, but not terrible. When I said that it "isn't that slow", that is a feature of insufficient testing. It's quick enough in Distiller, but painfully slow in Ghostscript, as has recently been reported. http://bugs.ghostscript.com/show_bug.cgi?id=695906 My OP's report: http://www.theportforum.com/viewtopic.php?t=175&start=913
[toc] | [prev] | [next] | [standalone]
| From | jdaw1 <jdawiseman@gmail.com> |
|---|---|
| Date | 2015-04-07 04:27 -0700 |
| Message-ID | <5b2de170-28a1-42b3-84f3-efe899ce4fd2@googlegroups.com> |
| In reply to | #2209 |
I'll also post the code of LineWidthThatCoversPath, in case it changes in a later version. But be careful with line breaks that you can copy but that weren't in my original code.
% TargetAccuracy LineWidthThatCoversPath WidthMax WidthMin
% Calculates, for the current path, the greatest distance from an interior point to the edge.
% WidthMin is known to be less than this distance. WidthMax will be greater.
% https://groups.google.com/forum/#!topic/comp.lang.postscript/86b7Sg8v7B0
/LineWidthThatCoversPath
{
DeBugLevel 25 le {(+LineWidthThatCoversPath) OutputToLog} if
20 dict begin
//PrinterEpsilon 2 copy lt {exch} if pop /TargetAccuracy exch def
% Target accuracy is split into two equal parts. The grid separation is half. And then the WidthMax-WidthMin separation is allowed the other half, and sometimes less than that.
PathBBox /ury exch def /urx exch def /lly exch def /llx exch def
urx llx sub //PrinterEpsilon gt ury lly sub //PrinterEpsilon gt and
{
% For an equilateral triangle the point furthest from the three corners is 2/3 of the height from the corners, which is Sqrt3/3 of the base.
/NumRows ury lly sub TargetAccuracy 0.75 mul div def % Error to be <= TargetAccuracy/2, which is 2/3 of row height. So row height = 0.75*TargetAccuracy.
/NumCols urx llx sub TargetAccuracy 1.5 mul Sqrt3 div div def % Error to be <= TargetAccuracy/2, which is Sqrt3/3 of the width. So width = TargetAccuracy * 1.5 / Sqrt3.
30000 NumRows ceiling 1 add NumCols ceiling 1 add mul div dup 1 gt % 30k => max stack of about 60k
{sqrt dup NumRows mul ceiling cvi 1 add /NumRows exch def NumCols mul ceiling cvi 1 add /NumCols exch def}
{pop /NumRows NumRows ceiling cvi 1 add def /NumCols NumCols ceiling cvi 1 add def}
ifelse % NumRows*NumCols small
/HalfGapRows ury lly sub NumRows 2 mul div def
/HalfGapCols urx llx sub NumCols 2 mul div def
/MaxResolutionError HalfGapRows dup 4 mul HalfGapCols dup mul exch div add def
DeBugLevel 15 le {mark ( LineWidthThatCoversPath: NumRows = ) NumRows (; NumCols = ) NumCols (; HalfGapRows = ) HalfGapRows (; HalfGapCols = ) HalfGapCols (; MaxResolutionError = ) MaxResolutionError ConcatenateToMark OutputToLog} if
count currentsystemparams /MaxOpStack 2 copy known {get} {pop pop 65535} ifelse exch sub /StackSpaceRemaining exch def
/PointsMostDistanceFromEdge
[
1 1 NumCols 2 mul 1 sub
{
/ColNum exch def /X ColNum HalfGapCols mul llx add def
1 2 NumRows 2 mul
{
/RowNum exch def /Y RowNum HalfGapRows mul lly add def
RowNum 4 mod 2 idiv ColNum 2 mod eq {X Y infill {X Y /StackSpaceRemaining StackSpaceRemaining 2 sub def} if StackSpaceRemaining 2 lt {exit} if} if
} for % Y
StackSpaceRemaining 2 lt {exit} if
} for % X
] def % /PointsMostDistanceFromEdge
StackSpaceRemaining 2 lt {(Warning: LineWidthThatCoversPath has incomplete calculation because there might be too little stack space. Increasing TargetAccuracy would help.) OutputToLog} if
DeBugLevel 15 le {mark ( LineWidthThatCoversPath: PointsMostDistanceFromEdge length = ) PointsMostDistanceFromEdge length ConcatenateToMark OutputToLog} if
/WidthMin null def /WidthMax null def
/Width ury lly sub 6 div PrinterEpsilon 2 copy lt {exch} if pop def
{
gsave
Width setlinewidth strokepath
/NewArrayLength 0 def
0 2 PointsMostDistanceFromEdge length 2 sub
{
dup PointsMostDistanceFromEdge exch get exch 1 add PointsMostDistanceFromEdge exch get 2 copy
infill not
{
PointsMostDistanceFromEdge NewArrayLength 1 add 3 -1 roll put PointsMostDistanceFromEdge NewArrayLength 3 -1 roll put
/NewArrayLength NewArrayLength 2 add def
} {pop pop} ifelse % infill not
} for % PointsMostDistanceFromEdge
NewArrayLength 0 gt {/WidthMin Width def /PointsMostDistanceFromEdge PointsMostDistanceFromEdge 0 NewArrayLength getinterval def} {/WidthMax Width def} ifelse
grestore
DeBugLevel 15 le {mark ( LineWidthThatCoversPath: WidthMin = ) WidthMin (; WidthMax = ) WidthMax (; PointsMostDistanceFromEdge length = ) PointsMostDistanceFromEdge length ConcatenateToMark OutputToLog} if
WidthMax null ne
{
WidthMin null ne
{
% Complicated exit conditions. Generally by the time this becomes close, PointsMostDistanceFromEdge is short, so a a few extra rounds are fast.
WidthMax WidthMin sub MaxResolutionError lt WidthMin TargetAccuracy div cvi WidthMax MaxResolutionError add TargetAccuracy div cvi eq and {exit} if
WidthMax WidthMin sub dup TargetAccuracy 64 div lt exch //PrinterEpsilon le or {exit} if
} {WidthMax TargetAccuracy 2 div le WidthMax PrinterEpsilon le or {/WidthMin 0 def exit} if} ifelse % WidthMin null ne
} if % WidthMax null ne
/Width WidthMax null eq {WidthMin 2 mul} {WidthMin null eq {WidthMax 2 div} {WidthMin WidthMax add 2 div} ifelse} ifelse def
} loop
WidthMax MaxResolutionError add WidthMin
} {//PrinterEpsilon 0} ifelse end % Non-zero area
DeBugLevel 25 le {(-LineWidthThatCoversPath) OutputToLog} if
} def % /LineWidthThatCoversPath
[toc] | [prev] | [next] | [standalone]
| From | jdaw1 <jdawiseman@gmail.com> |
|---|---|
| Date | 2014-04-06 16:00 -0700 |
| Message-ID | <f52e23f1-349a-4a94-a8d2-c93f478e87a3@googlegroups.com> |
| In reply to | #1838 |
The 'answer', horrible algorithm and all, is now implemented as the function LineWidthThatCoversPath, which can be found in http://www.jdawiseman.com/papers/placemat/placemat.ps Share and enjoy.
[toc] | [prev] | [standalone]
Back to top | Article view | comp.lang.postscript
csiph-web