[Top][All Lists]

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [Groff] Using variables from register within fractions or equations

From: Tadziu Hoffmann
Subject: Re: [Groff] Using variables from register within fractions or equations
Date: Sun, 19 Feb 2012 20:23:03 +0100
User-agent: Mutt/1.5.21 (2010-09-15)

Sorry that this was a bit confusing... I have a habit of using
the old-style roff syntax with one- or two-character names.
Here's a slightly revised version using groff's extended syntax
(which is probably a bit easier to understand for novices)
including a short description of how it's supposed to work:

  .de Z
  .sp 1
  .nr n 1
  .ad l

This begins a new exercise/problem by vertically spacing a bit
and resetting the fill-in blanks counter to 1.  (You could
also increment an exercise counter here and output it at
this point.)  I added left-adjust so that groff doesn't
stretch text that needs to be line-wrapped before a blank.

  .de Q
  .if \\n[m]=1 .X \\$@
  .if \\n[m]=2 .Y \\$@
  .if \\n[m]=3 .Y \\$@

As Ralph already said, this just calls either macro X or
macro Y with all the arguments Q was called with.  This is
convenient because it lets you reconfigure the macros to
use either constant-width blanks or variable-width blanks,
while allowing you to always call macro Q in the actual
exercise part.

  .ds n \s'-2'\\n[n]\s'0'

This defines a string "n" specifying the current blank-number
at a size 2 points smaller.  We define this abbreviation for
convenience because we need this expression twice later on,
once to find the width of the number-text and then again to
print the number itself.

  .if \\n[m]=1 .char _ \v'.2m'\h'.1m'\D'l .8m 0'\h'.1m'\v'-.2m'
  .if \\n[m]=2 .char _ \v'.2m'\D'l 1m 0'\v'-.2m'
  .if \\n[m]=3 .char _ \v'.2m'\D'l 1m 0'\v'-.2m'

Depending on the current mode this defines a character "_"
which is either just a 1 em wide underline (0.2 ems below the
baseline) (which will give a continuous underline if several of
them are output next to each other) or a 0.8 em wide underline
with a 0.1 em wide space at each end (to make it symmetric)
(which will give an underline with gaps if several of the
are output next to each other).  This could be improved.
For example, because of the gaps the apparent width of the
total fill-in blank will be a bit different at the beginning
and end.  Alternatively, we could define a continuous underline
with little upticks between the letters.  Or we might try
letterspacing (track kerning) to obtain the gaps.

(Instead of a number you could also define the mode as a
string, ".ds m easy", and then say ".if '\\*[m]'easy' ..." etc.
Anyhow, always redefining the underline character at every
blank is probably a bit inefficient.  I've done it just to
demonstrate the different modes next to each other, but
I guess it will be more common to print a whole sheet in
the same mode, in which case you would need to define the
character only once when setting the mode.)

  .tr a_b_c_d_e_f_g_h_i_j_k_l_m_n_o_p_q_r_s_t_u_v_w_x_y_z_

This tells groff to replace all letters with the newly-defined
underline character when printing text from now on.  You will
probably want to add uppercase characters, too:

  .tr A_B_C_D_E_F_G_H_I_J_K_L_M_N_O_P_Q_R_S_T_U_V_W_X_Y_Z_

Hyphens, for example, are not translated (unless you add them
to the list as well), so a hyphenated word will be printed
as two blanks with a hyphen inbetween (but it will still get
only one number).

  .nr w (\w'\\$1'-\w'\\*[n]')/2

This defines a number register "w" whose value is half of
the width of the blank to be output (the word itself (\$1)
translated to underline characters) minus half of the width of
the blank-number. This will be used for centering the number.


This now outputs the blank, but pretends it has zero width
(\Z'...') (you could think of groff drawing the stuff and
then moving the current point back to where it began),
and additionally it tells groff that the "word" still isn't
finished (\c) so groff won't start its line-filling/breaking
machinery just yet.

  .tr aabbccddeeffgghhiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz

This tells groff to print all letters as themselves again
from now on.

  .ds x \\$1
  .substring x 0 0

This copies the word to string "x" and then extracts the first

  .if \\n[m]=1 \Z'\o'_\\*[x]''\c
  .if \\n[m]=2 \Z'\o'_\\*[x]''\c

In modes 1 and 2 this prints the first letter (\*[x]) centered
on an underline character (_), using groff's overstrike function
(\o'...') (whose primary function probably is to print accents
on letters, but which is convenient here because it centers
the stuff by itself, so we don't explicitly have to calculate
how much to move the letter before printing it), again in zero
width and without terminating the word.


This now prints the blank-number centered below the blank:
it moves the necessary horizontal distance (calculated
as register "w" above) and moves 1 em below the baseline,
prints the number, moves 1 em up again and horizontally the
same distance as before, which should leave us at the end
of the blank (if we haven't miscalculated).  Then it outputs
any supplied punctuation (\$2) (if there is none, this simply
resolves to nothing) and tells groff that the line contains
overlarge items (the number below the blank (\x'1m'), and a
bit of extra space above (\x'-1m') to give the student room
to fill in the word).  (You can see the effect of these when
you create exercises spanning multiple lines.)  Now the word
is finished, and groff can start linebreaking if necessary.

The macro with fixed-width blanks works almost the same but
is a bit simpler.  It defines the blank as string "W"
(we could have defined a character instead)

  .ds W \v'.2m'\D'l 3.5c 0'\v'-.2m'

that will draw a 3.5 cm long line 0.2 ems below the baseline,
and computes the centering-distance for the number similarly
to what we had before,

  .nr w (\w'\\*[W]'-\w'\\*[n]')/2

Since we have a continuous-underline blank we don't need to
worry about centering the first letter within one "letter
blank", so we just space a short width (\|) into the blank
and print the letter,

  .if \\n[m]=1 \Z'\|\\*[x]'\c
  .if \\n[m]=2 \Z'\|\\*[x]'\c

then we print the blank and the number as before,


As Holger correctly pointed out, many groff request that
work with numbers have implied default units that will be
used unless a unit is explicitly given.  For example, the
default unit for horizontal movement is "ems", but the width
function \w returns values in basic units ("u"), so we need to
specify "u" when moving horizontally a distance computed using
the width function, as in the above "\h'\\n[w]u'".

reply via email to

[Prev in Thread] Current Thread [Next in Thread]