summaryrefslogtreecommitdiff
path: root/macros/latex/contrib/truncate/truncate.sty
blob: a4f0ed840cbdbbe7031e4559f1e454b4ba521412 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
% truncate.sty  ver 3.6   20-Aug-2001    Donald Arseneau
% This LaTeX program is released to the public domain.
%
% Truncate text to a specified width:  \truncate [marker]{width}{text}
%
% If the text is too wide to fit in the specified width, then it is
% truncated, and a continuation marker is shown at the end. The default
% marker, used when the optional "[marker]" parameter is omitted, is
% "\,\dots".  You can change this default by redefining "\TruncateMarker"
% ("\renewcommand{\TruncateMarker}{}").
%
% Normally, the text (whether truncated or not) is printed flush-left
% in a box with exactly the width specified. The package option "[fit]"
% ("\usepackage[fit]{truncate}") causes the output text to have its
% natural width, up to a maximum of the specified width.
%
% The text will not normally be truncated in the middle of a word,
% nor at a space specified by the tie "~".  For example:
%
%     "\truncate{122pt}{This text has been~truncated}"   
% gives
%     "This text has...      "
%
% You can give one of the package options "[hyphenate]", "[breakwords]",
% or "[breakall]" to allow breaking in the middle of words. The first 
% two only truncate at hyphenation points; with the difference being that 
% "breakwords" suppresses the hyphen character.  On the other hand, 
% "breakall" allows truncation at any character.  For example: 
%
%     "\truncate{122pt}{This text has been~truncated}" 
% gives
%     "This text has been trun-..."   (hyphenate)
%     "This text has been trun... "   (breakwords)
%     "This text has been trunc..."   (breakall)
%
% (All of these options work through TeX's hyphenation mechanism.)
%
\ProvidesPackage{truncate}[2001/08/20 \space ver 3.6]
%
% [breakwords] allows hyphenation but with invisible hyphen characters
\DeclareOption{breakwords}{%
   \let\@Trunc@DoSelect\relax
   \expandafter\chardef\csname \string\@TruncHyph T1\endcsname 23
   \expandafter\chardef\csname \string\@TruncHyph OT1\endcsname 223
   \expandafter\chardef\csname \string\@TruncHyph LY1\endcsname 0
   \def\@Trunc@Post{\hyphenpenalty\tw@ \exhyphenpenalty\tw@
     \doublehyphendemerits\z@ \finalhyphendemerits\z@
     \the\@Trunc@RestHyph % set invisible hyphenchars for all fonts used
  }}
%
% [breakall] is like breakwords, but all characters are treated like
% the letter "c"; the default language 0 patterns allow hyphenation
% c-c-c-c-c-c-c-c-c.  This means we don't need special patterns!
\DeclareOption{breakall}{\ExecuteOptions{breakwords}%
    \expandafter\def\expandafter\@Trunc@Post\expandafter{\@Trunc@Post
      \@tempcnta\z@ \chardef\c`\c % in knuth patterns, c allows hyphens
      \loop \lccode\@tempcnta\c \advance\@tempcnta\@ne
      \ifnum\@tempcnta<\@cclvi \repeat}%
    \def\@Trunc@Pre{% force US english
      \lefthyphenmin\@ne \righthyphenmin\@ne
      \doublehyphendemerits\language
      \language\z@ % default English language
      \let\language\doublehyphendemerits
      \uchyph\@ne
    }}
%
% [hyphenate] ordinary hyphenation for breakpoints
\DeclareOption{hyphenate}{% takes precedence over breakwords
   \def\@Trunc@Post{\hyphenpenalty\sixt@@n}\let\@Trunc@Pre\relax}
%
% [fit] use true size of truncated text, not requested size.
\DeclareOption{fit}{\def\@Trunc@Rebox{%
   \setbox\z@\hbox{\unhbox\z@\unskip\unskip\@tempa}%
   \ifdim\wd\z@>\hsize \hbox to\hsize{\unhbox\z@}\else \box\z@ \fi}}
%
% Defaults:
\let\@Trunc@Pre\@empty
\def\@Trunc@Post{\hyphenpenalty\@M}
\def\@Trunc@Rebox{\hbox to\hsize{\unhbox\z@\unskip\unskip\@tempa\hfil}}
\let\@Trunc@DoSelect\@empty

\ProcessOptions\relax

\providecommand{\TruncateMarker}{\,\dots}

\newcommand{\truncate}[3][\TruncateMarker]{% #1=marker #2=width #3=text
  \parbox[b]{#2}{\tolerance9999 \emergencystretch .216\hsize
   \vbadness\maxdimen \vfuzz\vsize % no overfull warnings
   \hbadness\maxdimen \hfuzz\vsize
   %  set up hyphenchar switching for all fonts used.
   \edef\@tempb{\the\@Trunc@RestHyph}% save whole stack
   \global\@Trunc@RestHyph{}%
   \ifx\@Trunc@DoSelect\relax
     \let\@Trunc@DoSelect\selectfont
     \def\selectfont{\@Trunc@DoSelect\@Trunc@SaveHChar}%
   \fi
   \@Trunc@SaveHChar % do it for current font
   \setbox\z@\vbox\bgroup
    % First set text in a hbox to see if it fits.
    \setbox\z@\@@line{\ignorespaces #3\unskip\hfil}%
    \ifnum\badness>\@M  % text does not fit
      \settowidth\rightskip{#1}%
      \advance\rightskip \z@\@plus\p@
      \parfillskip-\@ne\rightskip \@plus\@ne fil 
      \parshape \tw@ \z@\hsize \z@\maxdimen \linepenalty60
      \tracinglostchars\z@
      %% \let\par\relax -- don't do this because \vskip causes infinite loop
      \@Trunc@Pre % must set language before paragraph
      \noindent \vadjust{\penalty-\@MM}% for splitting off first line
      \nobreak\hskip\z@skip % allow first word hyphenation
      \ignorespaces #3% the text
      \pretolerance\hyphenpenalty \@Trunc@Post % set up `hyphenation'
      \@@par
      \ifnum\prevgraf>\@ne
       \egroup \def\@tempa{#1}% use truncation marker
      \else
       \egroup \let\@tempa\@empty% no linebreak, so all text fits; no marker
      \fi
    \else
      \box\z@ \egroup \let\@tempa\@empty
    \fi
   \setbox\@tempboxa\vsplit\z@ to\z@ \unvbox\@tempboxa
   \unskip\unpenalty\unpenalty
   \setbox\z@\lastbox
   \nointerlineskip
   \hfuzz\p@
   \@Trunc@Rebox
   \let\@Trunc@HC\@firstofone \the\@Trunc@RestHyph % restore hyphenchars
   \global\@Trunc@RestHyph\expandafter{\@tempb}% restore the stack itself
 }}
% (Note that the text may be proessed twice.  I could avoid that
% if there are problems with global counters etc.)

% we save (implicit global) hyphenchar settings for each font on a 
% manual stack -- the token register \@Trunc@RestHyph -- before changing
% them.  We execute \@Trunc@RestHyph to set them to the invisible 
% characters and then again to restore them.  (I can't imagine this
% will ever be required.)
\newtoks\@Trunc@RestHyph
\global\@Trunc@RestHyph{}
%
\def\@Trunc@SaveHChar{\begingroup
  \edef\@tempa{%
    \hyphenchar\the\font=\noexpand\@Trunc@HC{\the\hyphenchar\font}\relax 
    \the\@Trunc@RestHyph}%
  \global\@Trunc@RestHyph\expandafter{\@tempa}%
%  \hyphenchar\font\z@
  \endgroup}
%
% select an invisible character for hyphenation.  Use \textcompwordmark, if
% any; use selected numbers for certain encodings (OT1 uses a *missing*
% character); use a space otherwise.
\def\@Trunc@HC#1{%
  \@ifundefined{\f@encoding\string\textcompwordmark}{%
    \@ifundefined{\string\@TruncHyph\f@encoding}%
      {32}% If no appropriate character, guess space character may be blank
      {\csname \string\@TruncHyph\f@encoding\endcsname}%
  }{\csname\f@encoding\string\textcompwordmark\endcsname}}%