1

I am very excited about this code by User @egreg which I will be using to display images of the stroke order of a Chinese character whenever you hover over a certain character.

I changed the code a little bit for my use-case, to end-up with something as such (note I used the font HanWangKaiMediumChuIn_wp010-08.ttf, so one needs to download it in case one wishes to use the font):

\documentclass[a6paper,12pt]{scrbook}

\usepackage{fontspec}
\setmainfont{HanWangKaiMediumChuIn_wp010-08}

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
% tooltips with LaTeX
%
% optimized for Adobe Reader (visible on mouse-over)
%     usage: \tooltip[<link colour>]{<link text>}[<tip box colour>]{<tip text>}
%   non-draggable version:
%     usage: \tooltip*[<link colour>]{<link text>}[<tip box colour>]{<tip text>}
%
% for Evince (visible on click, not draggable)
%   usage: \tooltip**[<link colour>]{<link text>}[<tip box colour>]{<tip text>}
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\usepackage{pdfbase}[2017/03/16]
\usepackage{xparse,ocgbase}
\usepackage{xcolor,calc}
\usepackage{tikz}
\usetikzlibrary{calc}

\ExplSyntaxOn
\let\tpPdfLink\pbs_pdflink:nn
\let\tpPdfAnnot\pbs_pdfannot:nnnn\let\tpPdfLastAnn\pbs_pdflastann:
\let\tpAppendToFields\pbs_appendtofields:n
\def\tpPdfXform{\pbs_pdfxform:nnnnn{1}{1}{}{}}
\let\tpPdfLastXform\pbs_pdflastxform:
\ExplSyntaxOff

\makeatletter
\NewDocumentCommand{\tooltip}{ssO{blue}mO{yellow!20}m}{{%
  \leavevmode%
  \IfBooleanT{#1}{%
    \ocgbase@new@ocg{tipOCG.\thetcnt}{%
      /Print<</PrintState/OFF>>/Export<</ExportState/OFF>>%
    }{false}%
    \xdef\tpTipOcg{\ocgbase@last@ocg}%
  }%
  \tpPdfLink{%
    \IfBooleanTF{#2}{%
      /Subtype/Link/Border [0 0 0]/A <</S/SetOCGState/State [/Toggle \tpTipOcg]>>
    }{%
      /Subtype/Screen%
      \IfBooleanTF{#1}{%
        /AA<<%
          /E<</S/SetOCGState/State [/ON \tpTipOcg]>>%
          /X<</S/SetOCGState/State [/OFF \tpTipOcg]>>%
        >>%
      }{
        /AA<<%
          /E<</S/JavaScript/JS(%
            var fd=this.getField('tip.\thetcnt');%
            \IfBooleanF{#1}{%
              if(typeof(click\thetcnt)=='undefined'){%
                var click\thetcnt=false;%
                var fdor\thetcnt=fd.rect;var dragging\thetcnt=false;%
              }%
            }%
            if(fd.display==display.hidden){%
              fd.delay=true;fd.display=display.visible;fd.delay=false;%
            }%
           this.dirty=false;%
          )>>%
          /X<</S/JavaScript/JS(%
            if(!click\thetcnt&&!dragging\thetcnt){fd.display=display.hidden;}%
            if(!dragging\thetcnt){click\thetcnt=false;}%
            this.dirty=false;%
          )>>%
          /U<</S/JavaScript/JS(click\thetcnt=true;this.dirty=false;)>>%
          /PC<</S/JavaScript/JS (%
            var fd=this.getField('tip.\thetcnt');%
            try{fd.rect=fdor\thetcnt;}catch(e){}%
            fd.display=display.hidden;this.dirty=false;%
          )>>%
          /PO<</S/JavaScript/JS(this.dirty=false;)>>%
        >>%
      }
    }%
  }{{\color{#3}#4}}%
  \sbox\tiptext{\fcolorbox{black}{#5}{#6}}%
  \edef\twd{\the\wd\tiptext}%
  \edef\tht{\the\ht\tiptext}%
  \edef\tdp{\the\dp\tiptext}%
  \tpPdfXform{\tiptext}%
  %tip box placed at top left page corner
  \begin{tikzpicture}[remember picture,overlay]
    \node [inner sep=0pt, anchor=base] at (current page.north west) {%
      \raisebox{-\tht}[0pt][0pt]{%
        \tpPdfAnnot{\twd}{\tht}{\tdp}{%
          /Subtype/Widget/FT/Btn/T (tip.\thetcnt)%
          /AP<</N \tpPdfLastXform>>%
          /MK<</TP 1/I \tpPdfLastXform/IF<</S/A/FB true/A [0.0 0.0]>>>>%
          \IfBooleanTF{#1}{%
            /Ff 65537/OC \tpTipOcg%
          }{%
            /Ff 65536/F 3%
            /AA <<%
              /U <<%
                /S/JavaScript/JS(%
                  var fd=event.target;%
                  var mX=this.mouseX;var mY=this.mouseY;%
                  var drag=function(){%
                    var nX=this.mouseX;var nY=this.mouseY;%
                    var dX=nX-mX;var dY=nY-mY;%
                    var fdr=fd.rect;%
                    fdr[0]+=dX;fdr[1]+=dY;fdr[2]+=dX;fdr[3]+=dY;%
                    fd.rect=fdr;mX=nX;mY=nY;%
                  };%
                  if(!dragging\thetcnt){%
                    dragging\thetcnt=true;Int=app.setInterval("drag()",1);%
                  }%
                  else{app.clearInterval(Int);dragging\thetcnt=false;}%
                  this.dirty=false;%
                )%
              >>%
            >>%
          }%
        }%
        \tpAppendToFields{\tpPdfLastAnn}%
      }%
    };
  \end{tikzpicture}
  \stepcounter{tcnt}%
}}
\makeatother
\newsavebox\tiptext\newcounter{tcnt}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

\ExplSyntaxOn
\NewDocumentCommand{\tooltips}{sO{}m+O{}}
 {
  \tl_map_inline:nn { #3 }
   {
    \IfBooleanTF{#1}{\tooltip*}{\tooltip*}{##1}{\includegraphics[#2]{images/##1}}#4
   }
 }
\ExplSyntaxOff

\begin{document}

\tooltips[scale=1]{高一生}

\end{document}

Another note: be sure to compile twice.

The code however, is designed to look for an image of the same name as the character. For example, if one would hover over the character , the code currently looks for the image 高.png (in the folder images).

Now, this is designed to work with a large set of Chinese characters. In fact, I have the required images of thousands of such characters. Unfortunately, the images are not named e.g. 高.png, not even U+9AD8.png (the UCS of that Character), but it is now named B0AA.png, which is the Big-5 encoding of that character.

So, that leaves me with two options to make this code workable.

  1. Either, I have to batch convert all of the names of each and every .png into their respective Chinese characters (for example B0AA.png into 高.png). But I wouldn't know how to do this. Is there a way to do this with Tex?
  2. Alternatively. How could I adapt the code above so that the code will sort of "convert" each character in \tooltips[scale=1]{高一生} into the Big-5 encoding but only when looking for the respective image of each character (so that \tooltips[scale=1]{高一生} will still display 高一生 but look for the images B0AA.png, A440.png and A5CD.png respectively).

Anybody offering a solution please?

Please note that I do have a .txt file available which might be used as a conversion table, as it lists all the necessary characters as such:

UCS Big5
UCS Big5
UCS Big5
... ...

As for the purpose of a M(not yet-)WE example, one could use the following minimal .txt file, which contains the UCS (left) and Big5 (right) codes for 高一生:

U+9AD8  B0AA
U+4E00  A440
U+751F  A5CD

By the way, if you want to use the images I am using, you can find them at

http://stroke-order.learningweb.moe.edu.tw/words/B0AA.png

at http://stroke-order.learningweb.moe.edu.tw/words/A440.png

and at http://stroke-order.learningweb.moe.edu.tw/words/A5CD.png

That is, for the example case of 高一生. Be sure to put them in a folder called "images" next to your main .tex file.

Finally, this code is supposed to produce .pdf files to be viewed with Adobe Acrobat Reader. I don't need to be bound by a specific compiling method, but the only way I have been successfully able to compile this, is using LuaLaTeX. As per this answer by @David Carlisle, XeTeX might be an other option, but I can't figure out what minute changes I would have to make.

  • If your file compiles with lualatex (I haven't tried), then it may be easier to write this encoding conversion as a piece of Lua code. Of course in principle it can be done in TeX macros too, and as you already have a file for use as a conversion table, using that data may make the problem easier. – ShreevatsaR Sep 20 '17 at 2:08
  • @ShreevatsaR Yes, exactly compiling is done with LuaLaTeX, let me just mention that in the OP. Would you have a possible solution? – Vincent Mia Edie Verheyen Sep 20 '17 at 2:10
  • Does your .txt file contain the UCS <-> Big5 mapping for every character that you care about? Can you upload your .txt file somewhere and add the link here? – ShreevatsaR Sep 20 '17 at 2:45
  • @ShreevatsaR Yes. Don't worry about the .txt file. To not have even more download links who might expire, I will propose a MWE code (for the characters 高一生) to be saved as a .txt file in the OP (first line: U+9AD8 B0AA, second line: U+4E00 A440, third line: U+751F A5CD) – Vincent Mia Edie Verheyen Sep 20 '17 at 2:55
  • @cfr The nice thing about renaming the PNGs to Chinese characters is that they are then instantly human readable, even beyond this particular use case (that is of course for people with knowledge of the language). But if you think it's easy to rename the thousands of images to their UCS encoding, I am all ears to learn how. – Vincent Mia Edie Verheyen Sep 20 '17 at 2:59
4

Of the two options, the better solution may be to rename all your files (e.g. rename B0AA.png to 高.png), assuming all your tools that work on these files are capable of handling such filenames, and assuming you are comfortable reading and distinguishing these filenames. But TeX would not be a good tool for such filesystem operations.

The other option is doable in TeX: you can convert each character to its Big5 encoding when you specify the filename in TeX, thus solving the problem. This sort of programming is doable even in TeX macros, but as you're using LuaTeX it becomes significantly easier.

Lua has no native understanding of Unicode; a UTF-8 encoded byte string is just a byte string to Lua. But this is not a problem; below I've written a simple UTF-8 decoder in Lua.

The following Lua and TeX files define a macro \bigfive so that you can type \bigfive{高} and it will expand (be equivalent) to B0AA, the Big5 encoding of that character. The Lua code can be put in the .tex file, but I prefer to put it into a separate file called big5.lua (say):

function ucsFromChar(s)
   -- UTF-8 decoder. Given a valid UTF-8-encoded character s, returns the number (codepoint) it encodes.
   -- A stricter version is at https://gist.github.com/shreevatsa/6aef61aafd4ccfe2149ddacdc5c5855d
   local n = string.byte(s, 1)
   if n < 128 then return n end -- Starts with a 0
   if n < 224 then              -- Starts with 110, so get remaining 5 bits, then 6 bits from next byte
      return (n - 192) * 64 + string.byte(s, 2) - 128
   elseif n < 240 then          -- Starts with 1110, so get remaining 4 bits, then 6 bits each from next 2 bytes
      return ((n - 224) * 64 + string.byte(s, 2) - 128) * 64 + string.byte(s, 3) - 128
   else                         -- Starts with 11110, so get remaining 3 bits, then 6 bits each from next 3 bytes
      return (((n - 240) * 64 + string.byte(s, 2) - 128) * 64 + string.byte(s, 3) - 128) * 64 + string.byte(s, 4) - 128
   end
end

map = nil  -- Populated and used by getMap() below.
function getMap()
   -- Returns map from a file containing a space-separated key-value pair per line.
   if map ~= nil then return map end
   map = {}
   for line in io.open('map.txt', 'r'):lines() do
      local key = nil
      for s in string.gmatch(line, "%S+") do
         if key ~= nil then map[key] = s else key = s end
      end
   end
   return map
end

function big5FromChar(s)
   local u = string.upper(string.format('U+%x', ucsFromChar(s)))
   -- return getMap()[u]
   local v = getMap()[u]
   if v ~= nil then return v else return s end
end

This assumes that (as stated in the question) you have a map.txt file with lines like:

U+9AD8  B0AA
U+4E00  A440
U+751F  A5CD

With this, the .tex file can be just:

\documentclass{article}
\usepackage{fontspec}
\setmainfont{AppleGothic}

\directlua{dofile('big5.lua')}
\newcommand{\bigfive}[1]{\directlua{tex.sprint(big5FromChar('#1'))}}

\begin{document}
Anywhere in my document I can use the \verb+\bigfive+ macro.

For example, 高 is \bigfive{高} in big5.
\end{document}

The above produces:

output

  • Very impressive. Thank you so much. If I change the line \IfBooleanTF{#1}{\tooltip*}{\tooltip*}{##1}{\includegraphics[#2]{images/##1}}#4 into \IfBooleanTF{#1}{\tooltip*}{\tooltip*}{##1}{\includegraphics[#2]{images/\bigfive{##1}}}#4, it does indeed compile when using e.g. \tooltips[scale=1]{高} or \tooltips[scale=1]{國字標準字體筆順} and many many more characters. Strangely, I found it doesn't compile when trying to use the character , even though its UCS and Big5 (namely U+4E00 A440) are in map.txt and I have an image named A440.png ... – Vincent Mia Edie Verheyen Sep 20 '17 at 6:43
  • The same problem occurs when trying your code only (without the OPs code). For example \bigfive{高} compiles, so does \bigfive{生}, but somehow \bigfive{一} doesn't. – Vincent Mia Edie Verheyen Sep 20 '17 at 6:55
  • Something as follows: (./OTHER.aux)(load luc: /Users/User/Library/texlife/2017/texmf-varluatex-cache/generic/fonts/otl/lmono10-regular.luc)big5.lua:4: Continuation byte must start with 10:128, stack traceback:, [C]: in function 'assert', big5.lua:4: in function 'getCBValue', big5.lua:16: in function 'ucsFromChar', big5.lua:45; in function 'big5fromChar', [\directlua]:1: in main chunk., \bigfive ...ctlua {tex.sprint(big5FromChar('#1'))}, l.13 For example, 一 is \bigfive{一}, in big5., ?. – Vincent Mia Edie Verheyen Sep 20 '17 at 7:03
  • @VincentMiaEdieVerheyen Oops it was a bug in my UTF-8 decoder (128 < n should have been 128 <= n). Can you try the version that is in the answer now? – ShreevatsaR Sep 20 '17 at 7:07
  • Congratulations, you fixed it! So is it advisable to continue using your first (longer) revision but change 128 < n into 128 <= n, or continue with the later (shorter) revision? – Vincent Mia Edie Verheyen Sep 20 '17 at 7:12

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.