Plain TeX and different hyperlink styles in dvi and pdf
You get the same result with the hypertex
driver of the LaTeX package hyperref
.
The hyperTeX
specials define some very basic HTML markup, e.g.:
s:[1/1]:: html:<a name="page.1">
s:[1/1]:: html:</a>
s:[1/1]:: html:<a name="Doc-Start">
s:[1/1]:: html:</a>
s:[1/1]:: html:<a name="section*.1">
s:[1/1]:: html:</a>
s:[1/1]:: html:<a href="#section.1">
s:[1/1]:: html:</a>
s:[1/1]:: html:<a name="section.1">
s:[1/1]:: html:</a>
s:[1/1]:: html:<a href="#section.1">
s:[1/1]:: html:</a>
s:[1/1]:: html:<a href="http://www.example.org/">
s:[1/1]:: html:</a>
This is the output of dvii
, the TeX source file:
\documentclass{article}
\usepackage[hypertex]{hyperref}
\begin{document}
\tableofcontents
\section{Hello World}
\label{sec:hello}
See section \ref{sec:hello}.
\url{http://www.example.org/}
\end{document}
The hyperTeX
specials do not provide configuration options and do not know about PDF.
Thus the link outcome is in control of the DVI program:
xdvi
underlines in blue.dvips -z
uses blue boxes.
Configuration of xdvi
There are three options to configure the link outcome, from the manual page of `xdvi`:
-linkcolor (.linkColor) Color used for unvisited hyperlinks (`Blue2' by default). Hyperlinks are unvisited be fore you click on them, or after the DVI file has been reloaded. The value should be either a valid X color name (such as DarkGoldenrod4) or a hexadec imal color string (such as #8b6508).Seealso -visit edlinkcolor and -linkstyle. -linkstyle (.LinkStyle) Determines the style in which hyper links are displayed. Possible values and their meanings are: 0 No highlighting of links 1 Underline links with link color 2 No underlining, color text with link color 3 Underline and display text colored with link color The values for link color are specified by the op tions/resources -linkcolor and -visitedlinkcolor (which see). -visitedlinkcolor (.visitedLinkColor) Color used for visited hyper links (`Purple4' by default). Hyperlinks become visited once you click on them. As for linkColor, the value should be either a valid X color name or a hexadecimal color string.
Configuration of dvips
The border and color are hardcoded, from hps.c
:
p->color[0] = 0; p->color[1] = 0; /* Blue links */ p->color[2] = 1; p->border[0] = 1; /* horizontal corner radius */ p->border[1] = 1; /* vertical corner radius */ p->border[2] = 1; /* box line width */ p->border[3] = 3; /* dash on size */ p->border[4] = 3; /* dash off size */
As result dvips -z
writes the links of the example above as:
(#section.1) [[134 682 210 694] [1 1 1 [3 3]] [0 0 1]] pdfm
(#section.1) [[185 628 190 640] [1 1 1 [3 3]] [0 0 1]] pdfm
[[197 628 318 640] [1 1 1 [3 3]] [0 0 1]] (http://www.example.com/) pdfm
To get different colors, either the definition of pdfm
needs to be changed in hps.pro
or the redefinition can be done at later time by manipulating the arguments for pdfm
and replacing the arrays for the border and the color.
Or in short, dvips -z
cannot be configured without hacking internals.
This is my solution to the problems of (1) using pdfmark in plain TeX to produce the hyperlinks in PDF outputs, and (2) converting the blue rectangles to underlines.
http://linux.topology.org/pdfmark.html
It's too long to summarize here. But roughly speaking, the answer to (1) was to write some TeX macros to do the job. The answer to (2) was to edit the file "hps.pro" in the current working directory to use the underlines.
PS. Here's my answer to part (1).
How to use PDF hyperlinks in plain TeX.
When I say "plain TeX", I mean completely plain TeX with no 3rd party macro libraries or external scripts or anything like that, except for what I write myself. The basic references for my plain TeX macros are http://arxiv.org/hypertex/ (the section marked "How it is all done internally") and https://www.tug.org/applications/hyperref/ftp/doc/manual.html (the section marked "introduction").
First I had to get the HTML special commands into the DVI file by writing them into my own \chapter
, \section
and various subsection macros. These are far too tedious and irrelevant to write out in full. But the following code snippet gives the basic idea.
% This \PreHatch macro to prefix a text string with a hatch character.
{\catcode`\^=6 \catcode`\#=12 \gdef\PreHatch^1{#^1}}
% Anchor points for cross-reference hyperlinks.
\def\LinkNameText#1#2{%
\special{html:<a name="#1">}#2\special{html:</a>}}
\def\LinkNamePRE#1{\special{html:<a name="#1">}}
\def\LinkNamePOST{\special{html:</a>}}
\def\LinkName#1{\LinkNameText{#1}{}}
% Cross-reference hyperlinks to defined anchor points.
\def\LinkHrefText#1#2{%
\special{html:<a href="\PreHatch{#1}">}#2\special{html:</a>}}
% Pre-text and post-text macros.
\def\LinkHrefPRE#1{\special{html:<a href="\PreHatch{#1}">}}
\def\LinkHrefPOST{\special{html:</a>}}
% External hyperlinks.
\def\LinkHrefExtText#1#2{%
\special{html:<a href="#1">}#2\special{html:</a>}}
\def\LinkHrefExt#1{\LinkHrefExtText{#1}{#1}}
\def\LinkHrefExtTT#1{\LinkHrefExtText{#1}{{\tt#1}}}
That's the actual code which I use for my documents. These macros may be invoked as in this example.
\def\BKidxhyperSECT##1{\LinkHrefText{section.##1}{##1}}
That inserts the text parameter (an integer number of a section) into an A/HREF span. If I need to first output the A/HREF link info, then some text, and then after that add the </a>
, then I invoke the LinkHrefPRE
and LinkHrefPOST
macros.
The above description is the relatively easy part, just getting completely plain TeX to create PDF hyperlinks. The only tricky thing is altering the \catcode
to allow the hash character to be inserted in the a/href parameter.
I'll write up part (2) about how to get the blue link-boxes replaced by underlines in the next answer.
Part (2).
How to change pdfmark blue rectangle hyperlinks to underlines.
By running the strace
command for the linux dvips -z
command, I discovered that dvips -z
used a global file /usr/lib/texmf/dvips/base/hps.pro
by default to prepend to the output Postscript file to make the hyperlinks get used by ps2pdf
. However, dvips -z
also looked in the current working directory for a file hps.pro
first. So all I had to do was copy the global hps.pro
file to the local working directory and hack it to change the blue box for hyperlinks to a blue underline.
First I added a new Postscript dictionary /bsdict
just below where the original hps.pro
file creates a dictionary called /actiondict
.
/actiondict 2 dict dup/Subtype/URI put def
/bsdict 4 dict dup /S /U put dup /W 1 put def
The first line above was in the file already. The second line is what I added.
Second, I added the text /BS bsdict
in the places where indicated in the following text.
[....]
/Color exch /BS bsdict oldstyle{/LNK}{/Subtype/Link/ANN}ifelse gsave initmat
[....]
aload pop/Rect 4 1 roll/Border 3 1 roll/Color exch/BS bsdict/LNK gsave initmat
[....]
The added text must be exactly where indicated (unless you know exactly how to program Postscript so that you know how to put it somewhere else). The above commands have the effect of adding the desired parameters for creating blue underlines. I think that BS
means box style, /S
means "style", /U
means underline, and very importantly, '/W' means "width" of line. If this is not set, then the default is zero, which is invisible.
PS. Just for the record, here is the modified version of the
hps.pro
file which I have been using successfully for the last year. I'm fairly sure there are no copyright issues with this.
%!
/HPSdict 20 dict dup begin/braindeaddistill 50 def/rfch{dup length 1 sub
1 exch getinterval}bind def/splituri{dup(#)search{exch pop}{()exch}
ifelse dup(file:)anchorsearch{pop exch pop 3 -1 roll pop false}{pop 3 -1
roll exch pop true}ifelse}bind def/lookuptarget{exch rfch dup
/TargetAnchors where{pop TargetAnchors dup 3 -1 roll known{exch get true
}{pop(target unknown:)print == false}ifelse}{pop pop
(target dictionary unknown\012)print false}ifelse}bind def/savecount 0
def/stackstopped{count counttomark sub/savecount exch store stopped
count savecount sub 1 sub dup 0 gt{{exch pop}repeat}{pop}ifelse}bind def
/tempstring 128 string def/targetvalidate{1 index dup length 127 gt exch
tempstring cvs dup(/)search{pop pop pop exch pop true exch}{pop}ifelse
token{pop length 0 ne}{true}ifelse or not}bind def/targetdump-hook where
{pop}{/targetdump-hook{dup mark exch gsave initmat setmatrix{{mark/Dest
4 2 roll targetvalidate{aload pop exch pop/Page 3 1 roll/View exch[exch
/FitH exch]/DEST pdfmark}{cleartomark}ifelse}forall}stackstopped pop
grestore}bind def}ifelse/baseurl{mark exch 1 dict dup 3 -1 roll/Base
exch put/URI exch/DOCVIEW{pdfmark}stackstopped pop}bind def
/externalhack systemdict/PDF known def/oldstyle true def/initmat matrix
currentmatrix def
/actiondict 2 dict dup/Subtype/URI put def
/bsdict 4 dict dup /S /U put dup /W 1 put def
/weblinkhandler{dup 3 1 roll mark 4 1 roll/Title 4 1 roll splituri 3 -1
roll dup length 0 gt{cvn/Dest exch 4 2 roll}{pop}ifelse{externalhack{
/HTTPFile exch}{actiondict dup 3 -1 roll/URI exch put/Action exch}
ifelse}{externalhack{/HTTPFile exch}{/File exch/Action/GoToR}ifelse}
ifelse counttomark 2 sub -1 roll aload pop/Rect 4 1 roll/Border 3 1 roll
/Color exch /BS bsdict oldstyle{/LNK}{/Subtype/Link/ANN}ifelse gsave initmat
setmatrix{pdfmark}stackstopped grestore}bind def/externalhandler where{
pop}{/externalhandler{2 copy{weblinkhandler}exec{/externalhack
externalhack not store 2 copy{weblinkhandler}exec{/externalhack
externalhack not store/oldstyle false store 2 copy{weblinkhandler}exec{
(WARNING: external refs disabled\012)print/externalhandler{pop pop}bind
store externalhandler}{pop pop}ifelse}{pop pop/externalhack externalhack
not store}ifelse}{pop pop/externalhandler{weblinkhandler pop}bind store}
ifelse}bind def}ifelse/pdfmnew{dup type/stringtype eq{externalhandler}{
exch dup rfch exch 3 -1 roll lookuptarget{mark 4 1 roll/Title 4 1 roll
aload pop exch pop/Page 3 1 roll/View exch[exch/FitH exch]5 -1 roll
aload pop/Rect 4 1 roll/Border 3 1 roll/Color exch/BS bsdict/LNK gsave initmat
setmatrix pdfmark grestore}{pop pop}ifelse}ifelse}bind def/pdfmold{dup
type/stringtype eq{externalhandler}{exch dup rfch exch 3 -1 roll
lookuptarget{mark 4 1 roll/Title 4 1 roll aload pop exch pop/Page 3 1
roll/View exch[exch/FitH exch]5 -1 roll aload pop pop 0 3 getinterval
/Rect 3 1 roll/Border exch/LNK gsave initmat setmatrix pdfmark grestore}
{pop pop}ifelse}ifelse}bind def/pdfm where{pop}{/pdfm
/currentdistillerparams where{pop currentdistillerparams dup
/CoreDistVersion known{/CoreDistVersion get}{0}ifelse dup
braindeaddistill le{(WARNING: switching to old pdfm because version =)
print ==/pdfmold}{pop/pdfmnew}ifelse load}{/pdfmark where{pop{dup type
/stringtype eq{externalhandler}{2 copy mark 3 1 roll{pdfmnew}
stackstopped{2 copy mark 3 1 roll{pdfmold}stackstopped{
(WARNING: pdfm disabled\012)print/pdfm{pop pop}store}{
(WARNING: new pdfm failed, switching to old pdfm\012)print/pdfm/pdfmold
load store}ifelse}{/pdfm/pdfmnew load store}ifelse pop pop}ifelse}}{{
pop pop}}ifelse}ifelse bind def}ifelse end def
If this file is put in the local directory when dvips -z
is run, then it will be used instead of the global hps.pro
file. It works on dvips version 5.98 anyway.