Just for fun, here is another way. While not specified in meaning, I took your length in argument #1 to be the length that remains, rather than the length that is clipped.
THIS ANSWER HAS TWO PARTS: the first answer clips the string mid-character to the specified length; and the second answer clips to the nearest character boundary inside the specified length (while allowing font declarations in the text).
PART 1
The solution relies on the fact that \ldots
is wider than any single character. The approach is to recursively strip off letters from the beginning of the sentence, until the result is less wide than the target width. Then, after an \ldots
, one takes the prior result (which is just slightly larger than the target width, and sets it in a right-aligned box of the target width. This will make the extra portion of the left-most character overlap the right end of the \ldots
. To take care of this small mismatch, the \ldots
are again lapped to the left in a white colorbox, to get rid of the overhang.
Finally, the result is re-kerned to the right to bring you to the end of the printed result (this last phase was an EDIT, upon realizing that I left the current position too far to the left.
EDITED to streamline code and remove a pathological case if null argument was passed to \clip
.
\documentclass{article}
\usepackage{xcolor}
\newlength\cliplength
\newcommand\clip[2]{\setlength\cliplength{#1}\cliphelper#2\relax\relax}
\def\cliphelper#1#2\relax{%
\fboxsep 0pt%
\setbox0=\hbox{#2}%
\ifdim\wd0>\cliplength%
\cliphelper#2\relax%
\else%
\setbox0=\hbox{#1#2}%
\ldots\makebox[\cliplength][r]{\box0}%
\kern-\cliplength\llap{\colorbox{white}{\strut\ldots}}%
\kern\cliplength%
\fi%
}
\parindent 0in
\begin{document}
\ldots\rule{1cm}{1pt}
\clip{1cm}{An example sentence.}
\ldots\rule{2cm}{1pt}
\clip{2cm}{An much longer example sentence.}
\ldots\rule{3cm}{1pt}
\clip{3cm}{An much longer example sentence.}
\end{document}
PART 2
The OP later wondered if the clipping could occur at letter boundaries. That problem is actually easier, can be done without xcolor
, and requires but a small change in my routine \cliphelper
. In this case, once the test string becomes less wide than the target clip width, I just set \ldots
and the test string. Here is the MWE which will clip to the closest letter boundary that is at or slightly smaller than the target clipping width:
REEDITED to support font size/shape declaration macros (that don't take arguments).
\documentclass{article}
\newlength\cliplength
\newcommand\clip[2]{%
\def\savedmacro{}\setlength\cliplength{#1}\cliphelper#2\relax\relax}
\def\cliphelper#1#2\relax{%
\setbox0=\hbox{\savedmacro#2}%
\ifdim\wd0>\cliplength% Is length larger than \cliplength?
\savethemacros{#1}{#2}% If #1 is a macro, save it
\cliphelper#2\relax% Recursively clip next letter
\else%
\ldots\savedmacro#2% Final typeset, once string length below \cliplength
\fi%
}
\makeatletter
\def\savethemacros#1#2{%
\ifcat A\noexpand#1\else\ifcat 1\noexpand#1\else% Skip cat11 and cat12
\protected@edef\savedmacro{\savedmacro#1}% Save the macro
\fi\fi%
}
\makeatother
\begin{document}
\ldots\rule{1cm}{1pt}\par
\clip{1cm}{An example sentence.}
\ldots\rule{2cm}{1pt}\par
\clip{2cm}{An much longer example sentence.}
\ldots\rule{3cm}{1pt}\par
\clip{3cm}{An much longer example sentence.}
\ldots\rule{1cm}{1pt}\par
\clip{1cm}{An \bfseries\itshape example sentence.\normalfont}
\ldots\rule{2cm}{1pt}\par
\clip{2cm}{An much longer example \bfseries sentence\normalfont.}
\ldots\rule{3cm}{1pt}\par
\clip{3cm}{\large An much \itshape longer example \bfseries\tiny sentence\normalfont.}
\end{document}
Use the truncate
package
\documentclass{article}
\usepackage[breakwords]{truncate}
\begin{document}
\truncate{3cm}{This text has been~truncated}
\end{document}
The package uses a clever stategy based on hyphenation. But a word of warning, if you just insert long nonsense letter sequences it may have a problem finding a breakpoint. Use sensible text and works very well. Do read the docs. Use texdoc truncate
from the command line, is the quickest way.
Best Answer
You've stated that you "may want to do things like replace all occurrences of
=
with>
" and also that "[c]oding should ideally by done in the preamble".I'm going to keep my fingers crossed that you'll reconsider the decision not to use LuaLaTeX. Lua (the programming language) has a very flexible and powerful string library, and LuaTeX offers several ways to assign Lua-coded functions to various "callbacks" -- meeting your requirement that the coding should be all done in the preamble. In the following example, the function
eq2gt
(which, as its name suggests, replaces all instances of=
with>
) is assigned to theprocess_input_buffer
callback, which operates at a very early stage of processing, viz., before TeX's "eyes" start their processing. That way, theeq2gt
function can act as a pre-processor, modifying parts of the input file "on the fly" before the typesetting job itself commences.