Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]


Groups > comp.lang.postscript > #1838 > unrolled thread

How 'thick' is a glyph?

Started byjdaw1 <jdawiseman@gmail.com>
First post2014-03-29 10:27 -0700
Last post2014-04-06 16:00 -0700
Articles 8 — 2 participants

Back to article view | Back to comp.lang.postscript


Contents

  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

#1838 — How 'thick' is a glyph?

Fromjdaw1 <jdawiseman@gmail.com>
Date2014-03-29 10:27 -0700
SubjectHow '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]


#1839

FromMark Carroll <mtbc@bcs.org>
Date2014-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]


#1840

Fromjdaw1 <jdawiseman@gmail.com>
Date2014-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]


#1841

Fromjdaw1 <jdawiseman@gmail.com>
Date2014-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]


#1846

Fromjdaw1 <jdawiseman@gmail.com>
Date2014-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]


#2209

Fromjdaw1 <jdawiseman@gmail.com>
Date2015-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]


#2210

Fromjdaw1 <jdawiseman@gmail.com>
Date2015-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]


#1849

Fromjdaw1 <jdawiseman@gmail.com>
Date2014-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