16

I've looked at the demo of Beeline reader and would like to test it out on a physical version of my books, which are done in LaTeX. I'm wondering if it would even be possible.

Beeline reader takes online test a puts a slight gradient on the letters for a single line of text. On the next line it reverses the direction of the gradient. The idea being that it helps tracking text and improves speed of reading.

Any hints or suggestions as to how to do this in LaTeX?

What follows is an example taken from http://www.beelinereader.com/

enter image description here

jub0bs
  • 58,916

2 Answers2

15

with thanks to

How to put color gradient to "Desired Text Only"

enter image description here

\documentclass{article}
\usepackage{lipsum}


\usepackage{tikz}
\usetikzlibrary{fadings,patterns}
\begin{document}

\newcommand\tikzsection[1]{%
  \begin{tikzfadingfrompicture}[name=tikzsection]
    \node [text=white] {#1};
  \end{tikzfadingfrompicture}
    \begin{tikzpicture}
      \node [text=white,inner sep=0pt,outer sep=0pt] (textnode) {#1};
      \shade[path fading=tikzsection,fit fading=false,left color=\lcolor,right color=\rcolor]
      (textnode.south west) rectangle (textnode.north east);
    \end{tikzpicture}%
}

\makeatletter
\setbox1=\vbox{}


\def\zcolor{red}

\setbox2\vbox{\def\write#1#2{}%
\section{Grr dasadsa}
\lipsum\par


% at this point we are at the end of box 2 now
% loop backwards up the vertical list copying glue and penalties
% but modifying boxesto add the prefix.
\loop
%
% an e-tex primitive that reports the last item in the vertical list.
% type 1 is a box, so remove the box (a lien of text) and then
% put into box 1 a box that is this box together with the prefix which
% was saved in box 0
\ifnum\lastnodetype=1
\ifx\rcolor\zcolor
\gdef\lcolor{red}\gdef\rcolor{blue}%
\else
\gdef\lcolor{blue}\gdef\rcolor{red}%
\fi
\global\setbox3\lastbox
\global\setbox1\vbox{%
\hbox{\tikzsection{\copy\thr@@}}%
\unvbox1}%
\fi
%
% type 11 is glue so remove it from this list and add
% equivalent glue to box 1
\ifnum\lastnodetype=11
\skip0\lastskip\unskip
\global\setbox1\vbox{\vskip\skip0\unvbox1}%
\fi
%
% same for penalty
\ifnum\lastnodetype=13
\count0\lastpenalty\unpenalty
\global\setbox1\vbox{\penalty\count0 \unvbox1}%
\fi
%
% other node types are not handled here: either they won't happen
% or they can't be removed anyway so would break the loop \special
% for example would be bad.
%
% -1 means the vertical list is empty: we have reached the top of the box.
\ifnum\lastnodetype=-1
\else
\repeat}%


% Tip the modified box back onto the main list for the page, unbox
% it so that page breaking may still happen (if it could happen in the
% original context).
\unvbox1




\end{document}

A modified colour cycle closer to the image added to the question could be to add

\newcount\colorcycle

to the preamble, then modify the box nodetype code to look like:

\ifnum\lastnodetype=1
\ifcase\colorcycle
\gdef\lcolor{red}\gdef\rcolor{black}\or
\gdef\lcolor{black}\gdef\rcolor{red}\or
\gdef\lcolor{blue}\gdef\rcolor{black}\or
\gdef\lcolor{black}\gdef\rcolor{blue}\global\colorcycle\m@ne\fi
\global\advance\colorcycle\@ne
\global\setbox3\lastbox
\global\setbox1\vbox{%
\hbox{\tikzsection{\copy\thr@@}}%
\unvbox1}%
\fi

enter image description here

David Carlisle
  • 757,742
  • That's great, David. However, beeline seems to use three colours. I think the rationale may be that, if only two colours are used, there is a greater risk of skipping a line when reading the middle of a block of text (because the colour of two successive lines is the same in the middle). That risk is somewhat reduced with three colours. – jub0bs Sep 06 '13 at 16:30
  • @Jubobs ah you mean each row just has two colours but you cycle a-b b-c c-a ? that is easily done just modify the \ifx block in the niddle to cycle the two colour macros. – David Carlisle Sep 06 '13 at 16:37
  • @Jubobs oh no the image added to the question goes black-red red-black black-blue blue-black but again it's just how you switch lcolor and rcolor macros – David Carlisle Sep 06 '13 at 16:41
  • Thanks for your answer, I tried to apply this to my book and pegged the CPU for a couple hours before I killed it... – Matt Harrison Sep 11 '13 at 04:11
  • 1
    @mattharrison It might take a fraction longer than normal but if it takes a lot longer then it is in an infinite loop almost certainly because there is a non-removable item such as a \special or a write on the main vertical list. (Note I disabled \write in the above) You can apply the system more locally on a per-paragraph basis, and also put in a test to check for non-removable items (you could not recover cleanly but at least you could abort the loop) or to avoid the restriction on not being able to remove unremovable items you need to leave tex, hence the luatex version in the other answer. – David Carlisle Sep 11 '13 at 08:17
  • @DavidCarlisle I actually really like this answer. I think it would make a great package, especially if it comes from a color guru... – jub0bs Nov 21 '13 at 23:55
  • @DavidCarlisle I wanted to try this out, but with 3 colors. So I tried modifying the \ifx block, adding \else% \gdef\lcolor{black}\gdef\rcolor{red}% But that gave the error Extra \else. \repeat It produced a pdf with black, red, blue gradient, but was the same for each line without randomness. I probably just am doing it wrong, would you please kindly post an example with 3 color gradient? – A Feldman Jul 17 '16 at 11:34
  • The picture at the end of the answer is what it should look like. The first version doesn't do the job and even makes reading harder. How do I actually "put to the preamble, then modify the box nodetype code to look like"? Which parts of the code should I replace with the parts provided in the additional listings? I have not used LaTeX before and don't really understand the code :-] Just seek to render some plaintext into a PDF coloured this way. – Ivan Jan 03 '19 at 19:01
12

That’s a wonderful use case for Luatex! (In fact, when I read the about the bee colors yesterday I was hoping for this question to pop up on TeX-SE.) So, without further ado, here is the necessary code. This requires Luaotfload, which Latex users do not have to load explicitly if they are using the fontspec package.

The beecolors code is split into a TeX and a Lua file. The Lua part (beegradients.lua) implements a node processor for the post_linebreak_filter callback. The principle is quite simple: It scans the horizontal lists that constitute a paragraph for glyphs and ligatures which it surrounds with the appropriate PDF color whatsits, recursing into any vlists an hlists it encounters along the way.

The second file (beegradients.tex) contains wrapper macros for defining gradients and toggling the callback. A gradient group is a list of color expressions and can be defined like this:

\definegradientgroup [<name>][<col1>,<col2>, ... ,<coln>]

E.g.

\definegradientgroup [blackwhite][0x000000, 0xFFFFFF]

installs a gradient list consisting of two colors, black and white. Likewise,

\definegradientgroup [red-green-blue][255*0*0, 0*255*0, 0*0*255]

defines a list of three colors, red, green and blue.

These gradient groups can afterwards be referred to by their names. The callback can be activated by means of an environment beegradients:

\startbeegradients [<name>]
  ... ... ...
\stopbeegradients

Where <name> refers to a previously defined gradient group. E.g., in order to use the groups blackwhite and red-green-blue we defined above:

\startbeegradients [blackwhite]
  \input knuth
\stopbeegradients

\startbeegradients [red-green-blue]
  \input knuth
\stopbeegradients

Without the optional argument, \startbeegradients will choose the last active group.

\startbeegradients [blackwhite]
  \input knuth
\stopbeegradients

foo bar baz       %% <- no gradient

\startbeegradients
  \input knuth    %% <- black and white again
\stopbeegradients

Here is a complete example for the Plain format that shows a couple definitions:

\input luaotfload.sty
\input beegradients.tex

\font \mainfont = file:Iwona-Regular.otf at 10pt
\mainfont

\definegradientgroup [mygradients][
  42*11*242,        %% decimal notation, separated by “*”
  83*242*55,
  0xf00ba7,         %% hex notation
  0x1ec001,
  g:23*b:42*r:133,  %% rgb notation, also separated by “*”
  b:53*g:184*r:10,
]

\definegradientgroup     [blackwhite][0x000000, 0xFFFFFF]
\definegradientgroup [red-green-blue][255*0*0, 0*255*0, 0*0*255]
\definegradientgroup            [red][255*0*0, 0*0*0]
\definegradientgroup          [green][0*100*0, 0*255*0]
\definegradientgroup           [blue][0*0*20, 0*0*210]

\input knuth

\startbeegradients [mygradients]    \input knuth \stopbeegradients
\startbeegradients [blackwhite]     \input knuth \stopbeegradients
\startbeegradients [red-green-blue] \input knuth \stopbeegradients
\startbeegradients [red]            \input knuth \stopbeegradients
\startbeegradients [green]          \input knuth \stopbeegradients
\startbeegradients [blue]           \input knuth \stopbeegradients

\bye

Result:

Colorful Plain TeX document

Of course the code is compatible with Latex, you can load it directly:

\documentclass {scrartcl}
\usepackage {fontspec} %% this loads luaotfload as well
\setmainfont {Antykwa Poltawskiego}
\input beegradients.tex

\definegradientgroup [red-green-blue][255*0*0, 0*255*0, 0*0*255]

\begin {document}
  \startbeegradients [red-green-blue]
    \input knuth
  \stopbeegradients
\end {document}

Which leads to this colorful document:

Colorful Latex document

For texlive 2016 see this post: https://tex.stackexchange.com/a/321962/90087

  • Thanks for your answer. I'm a LuaTex newbie, but am getting the following error:`! LuaTeX error ./beegradients.lua:32: attempt to index local 'nodecodes' (a nil value) stack traceback: ./beegradients.lua:32: in main chunk [C]: in function 'require' ...exlive/texmf-dist/tex/luatex/luatexbase/modutils.lua:52: in function 'requi re_module' <\directlua >:1: in main chunk. \lltxb@requirelua ...xluaescapestring {#2}" \fi )}

    l.2` I'm at a loss on how to deal with this...

    – Matt Harrison Sep 07 '13 at 21:13
  • @mattharrison your Luaotfload is probably too old. What TeX distribution are you using? – Philipp Gesang Sep 07 '13 at 21:18
  • luatex --version This is LuaTeX, Version beta-0.70.2-2012091206 (TeX Live 2012/Debian) – Matt Harrison Sep 07 '13 at 21:27
  • @mattharrison Yes, definitely too old. You need at least texlive 2013 (you cannot update only luaotfload since it won’t work with Luatex < 0.75). – Philipp Gesang Sep 07 '13 at 21:35
  • There's no way to write the code for older versions? – Matt Harrison Sep 07 '13 at 21:43
  • @mattharrison It’s possible, but far less convenient. I had to dig out my dusty texlive 2012 installation for testing: the updated gist should work with older versions. – Philipp Gesang Sep 08 '13 at 00:04
  • This works very well! Thank you. I have been trying to get this working in a shell script using lualatex http://pastebin.com/bbSPLLn1 but have been getting this error: Please type another input file name ! Emergency stop.l.7 \input beegradients.tex Any idea what I need to do to get it working within the shell script? – A Feldman Jan 23 '16 at 23:31
  • @a-feldman This appears to be a Latex document, so AFAIR you need \input{beegradients.tex}. If that fails too, check the path and post a complete error log. – Philipp Gesang Jan 25 '16 at 06:49
  • Thanks, I'll try that out. Thanks again for the wonderful answer. – A Feldman Jan 25 '16 at 23:56
  • I removed the temp directory code. Also, I needed links to beegradients.tex and beegradients.lua in the same directory I ran the scripts in. I ended up using these 4 scripts. Two are for "beelines" black, red and blue, as my draft mode, and two scripts are for final output (so I would not have to fool around with changing headers on my google doc (I found out that the google doc header had to be completely empty for this to work well). Color: http://pastebin.com/bmjG1xMd http://pastebin.com/ZVzYxzjm Black: http://pastebin.com/vupjQVbU http://pastebin.com/M6qfvzeL – A Feldman Jan 27 '16 at 01:48
  • 1
    I'm having a bit of a problem with this and texlive 2016, testing using the lualatex example. This error is popping up Undefined control sequence \RequireLuaModule Missing \begin{document}. \RequireLuaModule {b Undefined control sequence \startbeegradients Use of \startbeegradientsindeed doesn't match its definition \startbeegradients Too many }'s \startbeegradients Should I give up and install texlive 2015 instead? – A Feldman Jul 16 '16 at 03:56
  • @AFeldman Try \RequirePackage{luatex85.sty} before \documentclass. Remember that LuaTeX is Beta and backwards compatibility should not be expected. – cfr Jul 17 '16 at 19:41
  • Thanks much @cfr I will try that. Yes I am aware that LuaTeX is in flux. Thanks for taking the time to help. – A Feldman Jul 17 '16 at 23:11
  • @cfr unfortunately that did not work. But as you said, backwards compatibility is not to be expected. – A Feldman Jul 18 '16 at 20:41
  • @AFeldman You could always ask a new question if you can't find the same issue. – cfr Jul 18 '16 at 21:30
  • I upgraded to texlive 2017 and having problems with getting it working again. https://tex.stackexchange.com/questions/407348/beelines-solution-texlive-2017 – A Feldman Dec 23 '17 at 16:10