meta-imp-txt.mkiv /size: 12 Kb    last modification: 2021-10-28 13:50
1%D \module
2%D   [       file=meta-txt,
3%D        version=2000.07.06,
4%D          title=\METAPOST\ Graphics,
5%D       subtitle=Text Tricks,
6%D         author=Hans Hagen,
7%D           date=\currentdate,
8%D      copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
9%C
10%C This module is part of the \CONTEXT\ macro||package and is
11%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
12%C details.
13
14%D In this library some handy text manipulations are defined. Some can and will be
15%D improved as soon as the \TEX||\METAPOST\ interface is stable. Some of the
16%D solutions may look weird, which is entirely my fault, since I implemented them in
17%D the process of getting grip on this kind of manipulations. Undoubtly better
18%D \METAPOST\ code is possible, but my way of learning this kind of trickery happens
19%D to be by \quote {trial and error} and \quote {look and feel} (as well as
20%D identifying tricks in Hobby's code).
21
22% textext ipv btex ... etex
23
24% we need a proper prefix here
25
26\unprotect
27
28\ifdefined\??sh \else \definesystemvariable {sh}  \fi % ShapedText .. todo: commandhandler
29
30\unexpanded\def\setupshapetexts
31  {\dodoubleempty\getparameters[\??sh]}
32
33\setupshapetexts
34  [\c!bodyfont=]
35
36\startMPextensions
37    loadmodule "text" ;
38\stopMPextensions
39
40\ifdefined\parwidth \else
41    \newdimen\parwidth
42    \newdimen\parheight
43    \newdimen\parvoffset
44    \newdimen\parhoffset
45    \newcount\parlines
46    \newtoks \partoks
47    \newbox  \shapetextbox
48    \newcount\parfirst
49\fi
50
51\unexpanded\def\startshapetext[#1]%
52  {\global\newcounter\currentshapetext
53   \global\setbox\shapetextbox\vbox\bgroup
54     \switchtobodyfont[\@@shbodyfont]%
55     \dontcomplain
56     \hsize\parwidth
57     \setuptolerance[\v!verytolerant,\v!stretch]%
58     \scratchcounter\zerocount
59     \scratchtoks\emptytoks
60     \def\docommand##1%
61       {\setbox\scratchbox\hpack{\useMPgraphic{##1}}%
62        \global\parfirst\zerocount
63        \getMPdata
64        \setshapecharacteristics
65        \advance\scratchcounter by \parlines
66        \expandafter\appendtoks\the\partoks\to\scratchtoks}%
67     \processcommalist[#1]\docommand
68     \xdef\totalparlines{\the\scratchcounter}%
69     \global\partoks\scratchtoks
70     \parshape \the\scratchcounter \the\scratchtoks\relax
71     \setshapecharacteristics % extra dummy
72     \def\par{\endgraf\adaptparshape}%
73     \everypar{\begstrut}}
74
75\unexpanded\def\stopshapetext
76  {\endstrut
77   \egroup
78   \global\newcounter\currentshapetext
79   \getshapecharacteristics}
80
81\unexpanded\def\adaptparshape
82  {\def\docommand##1%
83     {\doifsomething{##1}%
84        {\ifcase\scratchcounter\relax
85           \expandafter\appendtoks\space##1 \to\scratchtoks
86         \else
87           \advance\scratchcounter\minusone
88         \fi}}%
89   \scratchcounter\prevgraf
90   \doglobal\decrement(\totalparlines,\scratchcounter)%
91   \multiply\scratchcounter\plustwo
92   \scratchtoks\emptytoks
93   \expanded{\processseparatedlist[\the\partoks][\space]}\docommand % we can have two spaces
94   \global\partoks\scratchtoks
95   \parshape\totalparlines\the\partoks\relax}
96
97\unexpanded\def\getshapecharacteristics
98  {\doglobal\increment\currentshapetext
99   \doifelsedefined{parlines:\currentshapetext}
100     {\getvalue{parlines:\currentshapetext}}
101     {\global\parlines  \plusone
102      \global\parfirst  \zerocount
103      \global\parvoffset\zeropoint
104      \global\parhoffset\zeropoint
105      \global\parwidth  \hsize
106      \global\parheight \vsize}}
107
108\unexpanded\def\setshapecharacteristics
109  {\doglobal\increment\currentshapetext
110   \setxvalue{parlines:\currentshapetext}%
111     {\global\parlines  \the\parlines
112      \global\parfirst  \the\parfirst
113      \global\parvoffset\the\parvoffset
114      \global\parhoffset\the\parhoffset
115      \global\parwidth  \the\parwidth
116      \global\parheight \the\parheight}}
117
118\unexpanded\def\getshapetext % option: unvbox
119  {\vbox\bgroup
120   \forgetall
121   \dontcomplain
122   \setbox\scratchbox\vbox to \parheight
123     {\switchtobodyfont[\@@shbodyfont]%
124      \splittopskip\strutheight
125      \vskip\parvoffset
126      \ifcase\parfirst\else\vskip\lineheight\fi
127      \hskip\parhoffset
128      \hbox{\vsplit\shapetextbox to \parlines\lineheight}}%
129   \wd\scratchbox\parwidth
130   \ht\scratchbox\parheight
131   \dp\scratchbox\zeropoint
132   \box\scratchbox
133   \getshapecharacteristics
134   \egroup}
135
136\doifundefined{RotFont}{\definefont[RotFont][RegularBold*default]}
137
138\unexpanded\def\getfollowtoken#1%
139 {\hbox\bgroup
140    \strut
141    \ctxlua{mp.follow_text(#1)}%
142  \egroup}
143
144\definefontfeature[mp:tp][liga=no]
145
146\startMPdefinitions
147    def mfun_follow_draw (expr alternative) =
148        if unknown RotPath  : path    RotPath  ; RotPath  := origin ; fi ;
149      % if unknown RotColor : color   RotColor ; RotColor := black  ; fi ;
150        if unknown TraceRot : boolean TraceRot ; TraceRot := false  ; fi ;
151        if unknown ExtraRot : numeric ExtraRot ; ExtraRot := 0      ; fi ;
152        picture pic[] ;
153        numeric len[] ; len[0] := 0 ;
154        numeric n ; n := lua.mp.follow_size() ;
155        for i=1 upto n :
156            pic[i] := lua.mp.follow_slot(i) ;
157            pic[i] := pic[i] shifted - llcorner pic[i] ;
158            len[i] := len[i-1] + lua.mp.follow_width(i) ;
159        endfor ;
160        numeric al, at, pl, pc, wid, pos ; pair ap, ad ;
161        al := arclength RotPath ;
162        if al = 0 :
163            al := len[n] + ExtraRot ;
164            RotPath := origin -- (al,0) ;
165        fi ;
166        if al < len[n]:
167           RotPath := RotPath scaled ((len[n]+ExtraRot)/al) ;
168           al := arclength RotPath ;
169        fi ;
170        if alternative = 1 :
171           pl := (al-len[n])/(if n>1 : (n-1) else : 1 fi) ;
172           pc := 0 ;
173        else : % centered / MP
174           pl := 0 ;
175           pc := arclength RotPath/2 - len[n]/2 ;
176        fi ;
177        if TraceRot :
178           draw RotPath withpen pencircle scaled 1pt withcolor blue ;
179        fi ;
180        for i=1 upto n :
181          % wid := abs(xpart urcorner pic[i] - xpart llcorner pic[i]) ;
182            wid := lua.mp.follow_width(i) ;
183            pos := len[i]-wid/2 + (i-1)*pl + pc ;
184            at := arctime   pos of RotPath ;
185            ap := point     at  of RotPath ;
186            ad := direction at  of RotPath ;
187            if mfun_trial_run :
188                % skip (ok, somewhat inefficient as we can consider a
189                % dedicated store and textext variant (todo)
190            else :
191                pic[i] := pic[i] shifted (-wid/2,0) rotated(angle(ad)) shifted ap ;
192                draw pic[i] ; % withcolor RotColor ;
193                if TraceRot :
194                    draw boundingbox pic[i] withpen pencircle scaled .25pt withcolor red ;
195                    draw ap withpen pencircle scaled .50pt withcolor green ;
196                fi ;
197            fi ;
198        endfor ;
199        if TraceRot :
200            draw boundingbox currentpicture withpen pencircle scaled .25pt withcolor blue ;
201        fi ;
202    enddef ;
203\stopMPdefinitions
204
205\startluacode
206    local context   = context
207
208    local nodecodes = nodes.nodecodes
209    local kerncodes = nodes.kerncodes
210
211    local visible_code = {
212        [nodecodes.glyph] = true,
213        [nodecodes.glue]  = true,
214        [nodecodes.hlist] = true,
215        [nodecodes.vlist] = true,
216        [nodecodes.rule]  = true,
217    }
218
219    local kern_code  = nodecodes.kern
220    local c_userkern = kerncodes.userkern
221    local a_fontkern = attributes.private("fontkern")
222
223    local copynode = nodes.copy
224    local freenode = nodes.free
225
226    local topoints = number.topoints
227    local mpprint  = mp.print
228
229    local n = nil
230    local s = 0
231
232    function mp.follow_reset()
233        for i=1,#n do
234            freenode(n[i])
235        end
236        n = nil
237        s = 0
238    end
239
240    function mp.follow_initialize(b)
241        if not n then
242            local head = tex.takebox(b).list
243            if head then
244                n = { }
245                s = 0
246                head = nodes.flattendiscretionaries(head)
247                local current = head
248                while current do
249                    local id = current.id
250                    if visible_code[id] then
251                        s = s + 1
252                        head, current, n[s] = nodes.remove(head,current)
253                    elseif id == kern_code and current.subtype == c_userkern and not current[a_fontkern] then
254                        s = s + 1
255                        head, current, n[s] = nodes.remove(head,current)
256                    else
257                        current = current.next
258                    end
259                end
260                nodes.flush_list(head)
261            end
262        end
263    end
264
265    function mp.follow_size()
266        mpprint(s)
267    end
268
269    function mp.follow_slot(i)
270        mpprint('textext("\\getfollowtoken{' .. i .. '}")')
271    end
272
273    function mp.follow_text(s)
274        context(copynode(n[s]))
275    end
276
277    function mp.follow_width(i)
278        mpprint(topoints(n[i].width))
279    end
280\stopluacode
281
282\unexpanded\def\dofollowtokens#1#2%
283  {\vbox\bgroup
284   \forgetall
285   \dontcomplain
286   \setbox\scratchbox\hbox{\addff{mp:tp}#2}%
287   \ctxlua{mp.follow_initialize(\number\scratchbox)}%
288   \startMPcode
289     \includeMPgraphic{followtokens} ;
290     mfun_follow_draw(\number#1) ;
291   \stopMPcode
292   \ctxlua{mp.follow_reset()}%
293   \egroup}
294
295\unexpanded\def\followtokens        {\dofollowtokens\plusone}
296\unexpanded\def\followtokenscentered{\dofollowtokens\zerocount}
297
298% stretched variant:
299%
300% \followtokens
301%   {This is just a dummy text, kerned by T{\kern
302%    -.1667em\lower .5ex\hbox {E}}{\kern -.125emX} and typeset
303%    in a circle using {\setMFPfont M}{\setMFPfont
304%    E}{\setMFPfont T}{\setMFPfont A}{\setMFPfont
305%    P}{\setMFPfont O}{\setMFPfont S}{\setMFPfont T}.\quad}
306
307% centered variant:
308%
309% \def\followtokengraphicscale#1{%%
310%   \startuseMPgraphic {followtokens}
311%     path RotPath; RotPath :=  reverse halfcircle scaled #1 ;
312%     draw RotPath ;
313%     setbounds currentpicture to boundingbox fullcircle scaled 12cm ;
314%   \stopuseMPgraphic}
315%
316% \startoverlay
317%   {\followtokengraphicscale{12cm}%%
318%    \followtokenscentered{There was question on the list about this kind of graphics.}}
319%   {\followtokengraphicscale{10cm}%%
320%    \followtokenscentered{And Marco patched followingtokens to handle a centered text.}}
321%   {\followtokengraphicscale{8cm}%%
322%    \followtokenscentered{That ended up as variant branch in the main macro.}}
323%   {\followtokengraphicscale{6cm}%%
324%    \followtokenscentered{So now we have two commands.}}
325% \stopoverlay
326
327% \followtokengraphicscale{6cm}
328% \followtokens{Hans Hagen uses {\darkred\TeX}, {\darkgreen\Lua}, {\darkblue \MetaPost} and friends.}
329
330\startuseMPgraphic{fuzzycount}
331   begingroup
332   save height, span, drift, d, cp ;
333   height := 3/ 5 * \baselinedistance ;
334   span   := 1/ 3 * height ;
335   drift  := 1/10 * height ;
336   pickup pencircle scaled (1/12 * height) ;
337   def d = (uniformdeviate drift) enddef ;
338   for i := 1 upto \MPvar{n} :
339     draw
340       if (i mod 5)=0 : ((-d-4.5span,d)--(+d-0.5span,height-d))
341       else           : ((-d,+d)--(+d,height-d)) fi
342       shifted (span*i,d-drift) ;
343   endfor;
344   picture cp ; cp := currentpicture ; % for readability
345   setbounds currentpicture to
346     (llcorner cp shifted (0,-ypart llcorner cp) --
347      lrcorner cp shifted (0,-ypart lrcorner cp) --
348      urcorner cp -- ulcorner cp -- cycle) ;
349   endgroup ;
350\stopuseMPgraphic
351
352\setupMPvariables
353  [fuzzycount]
354  [n=10]
355
356\unexpanded\def\fuzzycount#1%
357  {{\tx\useMPgraphic{fuzzycount}{n=#1}}}
358
359\defineconversion[fuzzy][\fuzzycount]
360
361%%%%%%%
362
363\setupMPvariables
364  [EnglishRule]
365  [height=1ex,
366   width=\the\localhsize, % without \the, problems in non e-tex
367   color=darkgray]
368
369\defineblank
370  [EnglishRule]
371  [medium]
372
373\startuniqueMPgraphic{EnglishRule}{height,width,color}
374    x1 = 0 ; x3 = \MPvar{width} ; x2 = x4 = .5x3 ;
375    y1 = y3 = 0 ; y2 = -y4 = \MPvar{height}/2 ;
376    fill z1..z2..z3 & z3..z4..z1 & cycle withcolor \MPvar{color} ;
377\stopuniqueMPgraphic
378
379\unexpanded\def\EnglishRule
380  {\startlinecorrection[EnglishRule]
381   \setlocalhsize \noindent \reuseMPgraphic{EnglishRule}
382   \stoplinecorrection}
383
384%D The following macro returns a tight bound character sequence.
385%D
386%D \useMPlibrary[txt]
387%D
388%D \startlinecorrection
389%D \TightText{\ss\bf 123}{0cm}{3cm}{red}
390%D \stoplinecorrection
391
392\unexpanded\def\TightText#1#2#3#4%
393  {\hpack
394     {\startMPcode
395        picture p ; p := image (graphictext "#1" withfillcolor red) ;
396        draw p xsized #2 ysized #3 withcolor \MPcolor{#4} ;
397      \stopMPcode}}
398
399\protect \endinput
400