diff --git a/contrib/mom/om.tmac b/contrib/mom/om.tmac
index adb7508ce..f5c6637ef 100644
--- a/contrib/mom/om.tmac
+++ b/contrib/mom/om.tmac
@@ -4918,7 +4918,7 @@ y\R'#DESCENDER \\n[.cdp]'
 .    ds $AUTHOR \\*[$AUTHOR_1]
 .    substring $AUTHORS 0 -2
 .    ds PDF_AUTHORS \\*[$AUTHORS]
-.    pdfmomclean PDF_AUTHORS
+.    if '\\*[.T]'ps' .pdfmomclean PDF_AUTHORS
 .    nop \!x X ps:exec [/Author (\\*[PDF_AUTHORS]) /DOCINFO pdfmark
 .END
 .
@@ -23452,13 +23452,13 @@ No room to start \\*[MN-pos] margin note #\\n[MN-curr] on page \\n[#P].
 .          el .nr LEVEL_REQ \\n[CURRENT_LEVEL]
 .       \}
 .       ds PDF_TX \\$*
-.       pdfmomclean PDF_TX
 .       nr PDF_LEV (\\n[LEVEL_REQ]*\\n[#PDF_BOOKMARKS_OPEN])
 .       ie '\\*[.T]'ps' \{\
 .           if !'\\*[PDF_NM]'' \{\
 .              pdfhref M -N \\*[PDF_NM2] -- \\*[PDF_TX]
 .              if !dpdf:href.map .tm gropdf-info:href \\*[PDF_NM2] \\*[PDF_TX]
 .           \}
+.           pdfmomclean PDF_TX
 .           pdfbookmark \\n[PDF_LEV] \\*[PDF_TX]
 .       \}
 .       el .pdfbookmark \\*[PDF_NM] \\n[PDF_LEV] \\$*
@@ -23479,7 +23479,7 @@ No room to start \\*[MN-pos] margin note #\\n[MN-curr] on page \\n[#P].
 \#
 .MAC PDF_TITLE END
 .    ds pdftitle \\$*
-.    pdfmomclean pdftitle
+.    if '\\*[.T]'ps' .pdfmomclean pdftitle
 .    nop \!x X ps:exec [/Title (\\*[pdftitle]) /DOCINFO pdfmark
 .END
 \#
@@ -23552,8 +23552,10 @@ No room to start \\*[MN-pos] margin note #\\n[MN-curr] on page \\n[#P].
 .    if '\\*[PDF_AST]'*' \{\
 .        chop PDF_TXT
 .        ie '\\*[.T]'pdf' \{\
-.           ie d pdf:look(\\*[PDF_NM]) \
-.               as PDF_TXT \&\\*[PDF_AST_Q]\\*[pdf:look(\\*[PDF_NM])]\\*[PDF_AST_Q]
+.           ds PDF_NM_HEX \\*[PDF_NM]
+.           stringhex PDF_NM_HEX
+.           ie d pdf:look(\\*[PDF_NM_HEX]) \
+.               as PDF_TXT \&\\*[PDF_AST_Q]\\*[pdf:look(\\*[PDF_NM_HEX])]\\*[PDF_AST_Q]
 .           el \{\
 .               as PDF_TXT Unknown
 .               if !rPDF_UNKNOWN .tm \

============================================================

The calls to pdfmomclean are no longer required for pdf output
but should be retained for postscript.

The pdf:look array is now keyed on a stringhexed key.

============================================================

diff --git a/contrib/sboxes/msboxes.ms.in b/contrib/sboxes/msboxes.ms.in
index a1700dfbf..8b509572c 100644
--- a/contrib/sboxes/msboxes.ms.in
+++ b/contrib/sboxes/msboxes.ms.in
@@ -81,12 +81,12 @@ Including
 .Qq fill
 in the command will paint the rectangle with the current fill colour (as
 with
-.Lt \[rs]M[] )
+.Lt \[rs]M[\|] )
 and including
 .Qq box
 will give the rectangle a border in the current stroke colour
 (as with
-.Lt \[rs]m[] ).
+.Lt \[rs]m[\|] ).
 .sp \n[PD]u
 .I cmd
 may also be

============================================================

Looks better!

============================================================

diff --git a/doc/GMPfront.t b/doc/GMPfront.t
new file mode 100644
index 000000000..fd14511a5

============================================================

A cover page for the groff-man-pages.pdf book.

============================================================

diff --git a/doc/doc.am b/doc/doc.am
index cddc51907..c139452c1 100644
--- a/doc/doc.am
+++ b/doc/doc.am
@@ -38,6 +38,11 @@ DOC_GROFF = \
   GROFF_BIN_PATH="$(GROFF_BIN_PATH)" \
   $(GROFFBIN) -M $(doc_srcdir) $(MFLAG) $(FFLAG) -ww -b
 
+DOC_PDFMOM = \
+  GROFF_COMMAND_PREFIX= \
+  GROFF_BIN_PATH="$(GROFF_BIN_PATH)" \
+  $(GROFF_BIN_PATH)/pdfmom -M $(doc_srcdir) $(MFLAG) $(FFLAG) -ww -b --roff
+
 # This image file is used by several documents in the groff source tree.
 DOC_GNU_EPS = doc/gnu.eps
 
@@ -256,8 +261,9 @@ man-clean:
 # feature of gropdf.
 doc/groff-man-pages.pdf: $(GROFF_MAN_PAGES_ALL) eqn pic tbl \
   $(TMAC_PACKAGE_MAN) $(TMAC_PACKAGE_MDOC) font/devps/freeeuro.pfa
-	$(GROFF_V)$(DOC_GROFF) -pet -mandoc -dHF=HB -rC1 \
-	  -rCHECKSTYLE=3 -Tpdf -P-e \
+	$(GROFF_V)$(DOC_PDFMOM) -pet -mandoc -manmark -dHF=HB -rC1 \
+	  -rCHECKSTYLE=3 -rU=1 -rIN=7.2n -Tpdf -P-e \
+	  $(top_srcdir)/doc/GMPfront.t \
 	  $(GROFF_MAN_PAGES1) \
 	  $(tmac_srcdir)/sv.tmac $(GROFF_MAN_PAGES2) \
 	  $(tmac_srcdir)/en.tmac $(GROFF_MAN_PAGES3) > $@

============================================================

The book should be produced by pdfmom --roff so any forward
.MR calls are correctly handled.

Include new file anmark.tmac this is only required for the
build and should not be installed.

============================================================

diff --git a/font/devpdf/DESC.in b/font/devpdf/DESC.in
index 5cb254f37..ba90d26ee 100644
--- a/font/devpdf/DESC.in
+++ b/font/devpdf/DESC.in
@@ -6,6 +6,6 @@ unitwidth 1000
 sizes 1000-10000000 0
 styles R I B BI
 family T
-fonts 8 0 0 0 0 0 0 S ZD
+fonts 8 0 0 0 0 0 SS S ZD
 tcommand
 postpro gropdf

============================================================

New font SS allows eqn to accurately format slanted greek.

============================================================

diff --git a/font/devpdf/SS b/font/devpdf/SS
new file mode 100644
index 000000000..a61fa8b7a

============================================================

Groff font built from URW StandardSymbol.pfb font with
transformations to exactly match the type 3 font used for postscript.

============================================================

diff --git a/font/devpdf/StandardSymSL.pfb b/font/devpdf/StandardSymSL.pfb
new file mode 100644
index 000000000..94efbab40

============================================================

The .pfb file produced by Fontforge after transformations.

============================================================

diff --git a/font/devps/AB b/font/devps/AB
diff --git a/font/devps/ABI b/font/devps/ABI
diff --git a/font/devps/AI b/font/devps/AI
diff --git a/font/devps/AR b/font/devps/AR
...
diff --git a/font/devps/ZD b/font/devps/ZD

============================================================

All the standard postscript fonts have had an extra column
added - the glyphs unicode number.

Perusal of groff code shows this extra column is ignored and
does not exceed the line buffer size used.

If this is proved to cause an issue with other parts of groff
I could add code to BuildFoundry which adds the column as part of copying from devps.

============================================================

diff --git a/src/devices/gropdf/gropdf.1.man b/src/devices/gropdf/gropdf.1.man
index d1d39bbe0..cc80367bc 100644
--- a/src/devices/gropdf/gropdf.1.man
+++ b/src/devices/gropdf/gropdf.1.man
@@ -352,9 +352,7 @@ in the PostScript font.
 .LP
 Note that
 .I gropdf
-is currently only able to display the first 256 glyphs in any font.
-This restriction will be lifted in a later version.
-.
+Can now access all the glyphs available in a font.
 .
 .\" .LP
 .\" Note that
@@ -395,8 +393,9 @@ is the foundry name or blank for the default foundry.
 is the PostScript name of the font,
 and
 .I filename
-is the name of the file containing the font;
-lines beginning with
+is the name of the file containing the font.
+It can also contain a pathname to the font.
+Any lines beginning with
 .B #
 and blank lines are ignored;
 fields must be separated by tabs
@@ -407,7 +406,7 @@ allowed);
 is searched for using the same mechanism that is used
 for
 .I groff
-font metric files.
+font metric files, unless a path is also present.
 .
 The
 .I download
@@ -1339,6 +1338,64 @@ see
 .MR groff_tmac @MAN5EXT@ .
 .RE
 .
+.TP
+.BI "\[rs]X\[aq]pdf: pagenumbering\~" "type prefix start" \[aq]
+This is used to control the page numbering shown in the pdf reader\[aq]s
+outline pane which contains your bookmarks. Normally the page numbers
+shown against the bookmark is the physical page number in the file, But
+this may not match the different page number styles within the document.
+.RS
+.LP
+In a single document there may be a cover sheet (which has no page
+number), a TOC (which uses lower case roman numbers), and the main body
+of the document (which has decimal page numbers). Use this command
+somewhere on the page where the numbering system changes, once changed
+the numbers will automatically increment until the number system changes
+again, so don\[aq]t call for every page, just when you want to change
+the numbering.
+.LP
+The parameters are:-
+.TP
+.I type
+This specifies the type of numbering to use for this page onward. It
+should be one of
+.RB \[lq] "Decimal | Roman | roman | Alpha | alpha" \[rq].
+Only the initial letter is relevant. The alphabetic number systems use
+A-Z (then AA-AZ ... ZA-ZZ).
+The
+.I type
+may also be
+.RB \[lq] . \[rq]
+which means no numbering system is chosen, but you may still provide a
+.I prefix
+to have a custom name (such as "Cover");
+.TP
+.I prefix
+Provides a string to insert before the number.
+If the document has an Appendix with page numbers in the form
+.RI A- n ,
+the
+.I prefix
+would be set to \[lq]A-\[rq] and the
+.I type
+would be
+.BR Decimal .
+.
+.
+.TP
+.I start
+Gives the start number for the incrementing page numbers in the outline
+pane. If no value is given for
+.I start
+it will default to 1, which is usually correct.
+.LP
+The convenience macro for this command is \[lq]
+.BI ".pdfpagenumbering " "type prefix start"
+\[rq] using '.' for preceding missing values, or just \[lq]
+.BR .pdfpagenumbering \[rq]
+on its own to have no page numbers shown in the outline pane.
+.RE
+.
 .
 .\" ====================================================================
 .SS Macros
@@ -1673,18 +1730,18 @@ A timestamp
 to use as the output creation timestamp in place of the current time.
 .
 The time is converted to human-readable form using Perl's
-.I \%localtime()
+.I \%gmtime()
 function and recorded in a PDF comment.
 .
 .
 .TP
 .I TZ
-The time zone to use when converting the current time
-(or value of
-.IR SOURCE_DATE_EPOCH )
-to human-readable form;
+The time zone to use when converting the current time to human-readable form;
 see
 .MR tzset 3 .
+If
+.I SOURCE_DATE_EPOCH
+is used, it is always converted to human-readable form using UTC.
 .
 .
 .\" ====================================================================

============================================================

New device control \X'pdf: pagenumbering ...'

============================================================

diff --git a/src/devices/gropdf/pdfmom.1.man b/src/devices/gropdf/pdfmom.1.man
index 08d789c54..cea77b557 100644
--- a/src/devices/gropdf/pdfmom.1.man
+++ b/src/devices/gropdf/pdfmom.1.man
@@ -10,7 +10,7 @@ macro package for
 .\" Legal Terms
 .\" ====================================================================
 .\"
-.\" Copyright (C) 2012-2020 Free Software Foundation, Inc.
+.\" Copyright (C) 2012-2023 Free Software Foundation, Inc.
 .\"
 .\" Permission is granted to make and distribute verbatim copies of this
 .\" manual provided the copyright notice and this permission notice are
@@ -53,16 +53,18 @@ macro package for
 .\" ====================================================================
 .
 .SY pdfmom
+.RB [ \-\-roff ]
 .RB [ \-Tpdf ]
-.RI [ groff-options ]
+.RI [ groff-option\~ .\|.\|.\&]
 .RI [ file\~ .\|.\|.]
 .YS
 .
 .
 .SY pdfmom
+.RB [ \-\-roff ]
 .B \-Tps
-.RI [ pdfroff-options ]
-.RI [ groff-options ]
+.RI [ pdfroff-option\~ .\|.\|.\&]
+.RI [ groff-option\~ .\|.\|.\&]
 .RI [ file\~ .\|.\|.]
 .YS
 .
@@ -89,6 +91,25 @@ macros.
 .
 .
 .P
+If the
+.B \-\-roff
+option is used,
+the wrapper can be used with macro packages other than
+.MR groff_mom @MAN7EXT@ .
+.
+This is also true if the wrapper is renamed or linked as a
+pseudonym;
+for example,
+creating a
+.I pdfms
+link pointing to the
+.I pdfmom
+executable makes a wrapper for producing PDFs with the
+.I ms
+package.
+.
+.
+.P
 .I pdfmom
 prints to the standard output,
 so output must usually be redirected to a destination file.
@@ -173,6 +194,10 @@ options,
 .I pdfmom
 displays its version information and exits.
 .
+Using the
+.B \-\-help
+option displays a usage message and exits.
+.
 .
 .\" ====================================================================
 .SH Authors

============================================================

Document --roff and aliases.

============================================================

diff --git a/src/devices/gropdf/pdfmom.pl b/src/devices/gropdf/pdfmom.pl
index 89977d496..1c24d85b9 100644
--- a/src/devices/gropdf/pdfmom.pl
+++ b/src/devices/gropdf/pdfmom.pl
@@ -1,6 +1,6 @@
 #!@PERL@
 #
-#	pdfmom		: Frontend to run groff -mom to produce PDFs
+#	pdfmom		: Frontend to run groff to produce PDFs
 #	Deri James	: Friday 16 Mar 2012
 #
 
@@ -29,6 +29,20 @@ my @cmd;
 my $dev='pdf';
 my $preconv='';
 my $readstdin=1;
+my $mom='-mom';
+my $zflg='';
+if ($0=~m/pdf(\w+)$/)
+{
+    my $m=$1;
+    if ($m=~m/^(mom|mm|ms|me|man|mandoc)$/)
+    {
+        $mom="-".$m;
+    }
+    else
+    {
+        $mom='';
+    }
+}
 my $RT_SEP='@RT_SEP@';
 
 $ENV{PATH}=$ENV{GROFF_BIN_PATH}.$RT_SEP.$ENV{PATH} if exists($ENV{GROFF_BIN_PATH});
@@ -68,11 +82,30 @@ while (my $c=shift)
 	$preconv=$c;
 	next;
     }
-    elsif ($c eq '-z' or $c eq '-Z')
+    elsif ($c eq '-Z')
     {
-	$dev=$c;
+	$zflg=$c;
 	next;
     }
+    elsif ($c eq '-z')
+    {
+	$zflg="$c -dPDF.EXPORT=1";
+	next;
+    }
+    elsif ($c eq '--roff')
+    {
+        $mom='';
+    }
+    elsif ($c eq '--help')
+    {
+	print "usage: pdfmom [--roff] [-Tpdf] [groff-option ...] [file ...]\n";
+	print "usage: pdfmom [--roff] -Tps [pdfroff-option ...] [groff-option ...] [file ...]\n";
+	print "usage: pdfmom {-v | --version}\n";
+	print "usage: pdfmom --help\n";
+	print "\nHandle forward references in PDF documents.\n" .
+	      "See the pdfmom(1) manual page.\n";
+	exit;
+    }
     elsif ($c eq '-v' or $c eq '--version')
     {
 	print "GNU pdfmom (groff) version @VERSION@\n";
@@ -123,19 +156,22 @@ if ($readstdin)
 
 if ($dev eq 'pdf')
 {
-    system("groff -Tpdf -dLABEL.REFS=1 -mom -z $cmdstring 2>&1 | LC_ALL=C grep '^\\. *ds' | groff -Tpdf -dPDF.EXPORT=1 -dLABEL.REFS=1 -mom -z - $cmdstring 2>&1 | LC_ALL=C grep '^\\. *ds' | groff -Tpdf -mom $preconv - $cmdstring");
+    if ($mom)
+    {
+        system("groff -Tpdf -dLABEL.REFS=1 $mom -z $cmdstring 2>&1 | LC_ALL=C grep '^\\. *ds' | groff -Tpdf -dPDF.EXPORT=1 -dLABEL.REFS=1 $mom -z - $cmdstring 2>&1 | LC_ALL=C grep '^\\. *ds' | groff -Tpdf $mom $preconv - $cmdstring $zflg");
+    }
+    else
+    {
+        system("groff -Tpdf -dPDF.EXPORT=1 -z $cmdstring 2>&1 | LC_ALL=C grep '^\\. *ds' | groff -Tpdf $preconv - $cmdstring $zflg");
+    }
 }
 elsif ($dev eq 'ps')
 {
-    system("groff -Tpdf -dLABEL.REFS=1 -mom -z $cmdstring 2>&1 | LC_ALL=C grep '^\\. *ds' | pdfroff -mpdfmark -mom --no-toc - $preconv $cmdstring");
+    system("groff -Tpdf -dLABEL.REFS=1 $mom -z $cmdstring 2>&1 | LC_ALL=C grep '^\\. *ds' | pdfroff -mpdfmark $mom --no-toc - $preconv $cmdstring");
 }
 elsif ($dev eq '-z') # pseudo dev - just compile for warnings
 {
-    system("groff -Tpdf -mom -z $cmdstring");
-}
-elsif ($dev eq '-Z') # pseudo dev - produce troff output
-{
-    system("groff -Tpdf -mom -Z $cmdstring");
+    system("groff -Tpdf $mom -z $cmdstring");
 }
 else
 {

============================================================

New --roff option, aliases and new (undocumented - useful
for debugging!) -z option.

============================================================


diff --git a/src/utils/afmtodit/afmtodit.pl b/src/utils/afmtodit/afmtodit.pl
index c6b67cc45..3d38d2f80 100644
--- a/src/utils/afmtodit/afmtodit.pl
+++ b/src/utils/afmtodit/afmtodit.pl
@@ -621,7 +621,7 @@ for (my $i = 0; $i <= $#encoding; $i++) {
 	    printf(",%d", conv($h));
 	}
 	printf("\t%d", $type);
-	printf("\t%d\t%s\n", $i, $ch);
+	printf("\t%d\t%s\t%s\n", $i, $ch, $AGL_to_unicode{$ch}||'');
 	if (defined $nmap{$ch}) {
 	    for (my $j = 1; $j < $nmap{$ch}; $j++) {
 		printf("%s\t\"\n", $map{$ch, $j});

============================================================

New unicode column

============================================================

diff --git a/tmac/papersize.tmac b/tmac/papersize.tmac
index 6d5ad14c2..ffba019f1 100644
--- a/tmac/papersize.tmac
+++ b/tmac/papersize.tmac
@@ -123,14 +123,28 @@
 .  nr paper-w 0
 .
 .  ie d paper-\*[paper-p]-length \{\
+.\" .    if '\*[.T]'dvi' \{\
+.\" .       ds paper-last \*[paper-\*[paper-p]-width]
+.\" .       substring paper-last -1
+.\" .       ie '\*[paper-last]'c' \{\
+.\" .          as paper-\*[paper-p]-width m
+.\" .          as paper-\*[paper-p]-length m
+.\" .       \}
+.\" .       el \{\
+.\" .          as paper-\*[paper-p]-width n
+.\" .          as paper-\*[paper-p]-length n
+.\" .       \}
+.\" .    \}
 .    ie '\*[paper-l]'l' \{\
 .      pl \*[paper-\*[paper-p]-width]
 .      ll (\*[paper-\*[paper-p]-length] - 2i)
+.      if '\*[.T]'pdf' .device papersize=\*[paper-\*[paper-p]-length],\*[paper-\*[paper-p]-width] tmac
 .    \}
 .    el \{\
 .      ie '\*[paper-l]'' \{\
 .        pl \*[paper-\*[paper-p]-length]
 .        ll (\*[paper-\*[paper-p]-width] - 2i)
+.        if '\*[.T]'pdf' .device papersize=\*[paper-\*[paper-p]-width],\*[paper-\*[paper-p]-length] tmac
 .      \}
 .      el \
 .        nr paper-w 1

============================================================

Pass papersz to gropdf (and grodvi if code uncommented)

============================================================

diff --git a/src/roff/troff/input.cpp b/src/roff/troff/input.cpp
index 292ee7389..11ea87ee9 100644
--- a/src/roff/troff/input.cpp
+++ b/src/roff/troff/input.cpp
@@ -4752,8 +4752,46 @@ void chop_macro()
 }

 enum case_xform_mode { STRING_UPCASE, STRING_DOWNCASE };
+const char * hex = "0123456789ABCDEF";

 // Case-transform each byte of the string argument's contents.
+void stringhex_request()
+{
+  symbol s = get_name(true /* required */);
+  if (s.is_null()) {
+    skip_line();
+    return;
+  }
+  request_or_macro *p = lookup_request(s);
+  macro *m = p->to_macro();
+  if (!m) {
+    error("cannot apply stringhex to a request ('%1')",
+	  s.contents());
+    skip_line();
+    return;
+  }
+  string_iterator iter1(*m);
+  macro *mac = new macro;
+  int len = m->macro::length();
+  for (int l = 0; l < len; l++) {
+    int nc, c = iter1.get(0);
+    if (c == PUSH_GROFF_MODE
+	|| c == PUSH_COMP_MODE
+	|| c == POP_GROFFCOMP_MODE)
+      mac->append(c);
+    else if (c == EOF)
+      break;
+    else {
+      nc = (int)hex[(c >> 4) & 0x0f];
+      mac->append(nc);
+      nc = (int)hex[c & 0x0f];
+      mac->append(nc);
+    }
+  }
+  request_dictionary.define(s, mac);
+  tok.next();
+}
+
 void do_string_case_transform(case_xform_mode mode)
 {
   assert((mode == STRING_DOWNCASE) || (mode == STRING_UPCASE));

============================================================

Convert each byte in string to two char hex.

============================================================

diff --git a/tmac/pdf.tmac b/tmac/pdf.tmac
index 6a2fa7bba..f300d612d 100644
--- a/tmac/pdf.tmac
+++ b/tmac/pdf.tmac
@@ -35,39 +35,9 @@ am solely responsible for any bugs I may have introduced into this file.
 .
 .mso ps.tmac
 .
-.de pdf:SS
-.      fchar \\$1 \\S'16'\\$1\\S'0'
-..
-.pdf:SS \[+h]
-.pdf:SS \[ts]
-.pdf:SS \[*a]
-.pdf:SS \[*b]
-.pdf:SS \[*x]
-.pdf:SS \[*d]
-.pdf:SS \[*e]
-.pdf:SS \[*f]
-.pdf:SS \[*g]
-.pdf:SS \[*y]
-.pdf:SS \[*i]
-.pdf:SS \[+f]
-.pdf:SS \[*k]
-.pdf:SS \[*l]
-.pdf:SS \[*m]
-.pdf:SS \[*n]
-.pdf:SS \[*o]
-.pdf:SS \[*p]
-.pdf:SS \[*h]
-.pdf:SS \[*r]
-.pdf:SS \[*s]
-.pdf:SS \[*t]
-.pdf:SS \[*u]
-.pdf:SS \[+p]
-.pdf:SS \[*w]
-.pdf:SS \[*c]
-.pdf:SS \[*q]
-.pdf:SS \[*z]
 .char \[lh] \X'pdf: xrev'\[rh]\X'pdf: xrev'
 .nr pdf:bm.nl 1
+.gcolor black     \" Until bug #64592 is addressed
 .de pdfmark
 . nop \!x X ps:exec [\\$* pdfmark
 ..
@@ -213,10 +183,23 @@ am solely responsible for any bugs I may have introduced into this file.
 .   \" then parse any specified options, to set a "tag", if required
 .   \"
 .      ds pdf:href-T
-.      while dpdf:href.opt\\$1 \{\
-.         pdf:href.opt\\$1 \\$@
-.         shift \\n[pdf:href.argc]
+.      ds pdf:hrefhex-T
+.      length pdf:l \\$1
+.      ds pdf:char1 \\$1
+.      substring pdf:char1 0 0
+.      if !'\\*[pdf:char1]'-' .nr pdf:l 99
+.      while (\\n[pdf:l]<4) \{\
+.         ie dpdf:href.opt\\$1 \{\
+.            pdf:href.opt\\$1 \\$@
+.            shift \\n[pdf:href.argc]
+.            length pdf:l \\$1
+.            if '\\$1'--' .break
+.            ds pdf:char1 \\$1
+.            substring pdf:char1 0 0
+.            if !'\\*[pdf:char1]'-' .nr pdf:l 99
 .         \}
+.         el .break
+.      \}
 .      rr pdf:href.argc
 .   \"
 .   \" If we found "--" to mark the end of the options, discard it
@@ -245,28 +228,35 @@ am solely responsible for any bugs I may have introduced into this file.
 .   \" ( which we return in the string "PDFBOOKMARK.NAME" ),
 .   \"
 .      nr pdf:bm.nr +1
-.      ie '\\*[pdf:href-T]'' .ds PDFBOOKMARK.NAME pdf:bm\\n[pdf:bm.nr]
-.      el .ds PDFBOOKMARK.NAME \\*[pdf:href-T]
+.      ie '\\*[pdf:hrefhex-T]'' \{\
+.         ds PDFBOOKMARK.NAME pdf:bm\\n[pdf:bm.nr]
+.         ds PDFBOOKMARK.NAME.HEX \\*[PDFBOOKMARK.NAME]
+.         stringhex PDFBOOKMARK.NAME.HEX
+.      \}
+.      el \{\
+.         ds PDFBOOKMARK.NAME \\*[pdf:href-T]
+.         ds PDFBOOKMARK.NAME.HEX \\*[pdf:hrefhex-T]
+.      \}
 .      pdf:href.sety
-.         ds pdf:cleaned \\$*
-.         ev pdfcln
-.         tr \[em]-
-.         nf
-.         box pdf:clean
-.         nop \\$*
-.         fl
-.         box
-.         chop pdf:clean
-.         asciify pdf:clean
-.         length pdf:clean:len \\*[pdf:clean]
-.         ds pdf:cleaned \\*[pdf:clean]
-.         rm pdf:clean
-.         ev
-.         tr \[em]\[em]
-.      ds pdf:look(\\*[PDFBOOKMARK.NAME]) \\*[pdf:cleaned]
-.      if dPDF.EXPORT .tm .ds pdf:look(\\*[PDFBOOKMARK.NAME]) \\*[pdf:cleaned]
-.      pdfmark /Dest /\\*[PDFBOOKMARK.NAME] /View [\\*[PDFBOOKMARK.VIEW]] /DEST
-.      nop \!x X ps:exec [/Dest /\\*[PDFBOOKMARK.NAME] /Title (\\*[pdf:cleaned]) /Level \\n[pdf:bm.lev] /OUT pdfmark
+.\" .         ds pdf:cleaned \\$*
+.\" .         ev pdfcln
+.\" .         tr \[em]-
+.\" .         nf
+.\" .         box pdf:clean
+.\" .         nop \\$*
+.\" .         fl
+.\" .         box
+.\" .         chop pdf:clean
+.\" .         asciify pdf:clean
+.\" .         length pdf:clean:len \\*[pdf:clean]
+.\" .         ds pdf:cleaned \\*[pdf:clean]
+.\" .         rm pdf:clean
+.\" .         ev
+.\" .         tr \[em]\[em]
+.      ds pdf:look(\\*[PDFBOOKMARK.NAME.HEX]) \\$*
+.      if dPDF.EXPORT .tm .ds pdf:look(\\*[PDFBOOKMARK.NAME.HEX]) \\$*
+.      pdfmark /Dest /\\*[PDFBOOKMARK.NAME.HEX] /View [\\*[PDFBOOKMARK.VIEW]] /DEST
+.      nop \!x X ps:exec [/Dest /\\*[PDFBOOKMARK.NAME.HEX] /Title (\\$*) /Level \\n[pdf:bm.lev] /OUT pdfmark
 .\".      pdfmark /Dest /\\*[PDFBOOKMARK.NAME] /Title "(\\*[pdf:cleaned])" /Level \\n[pdf:bm.lev] /OUT
 .      pdf:href.options.clear
 .      rr PDFPAGE.Y
@@ -383,7 +373,7 @@ am solely responsible for any bugs I may have introduced into this file.
 .\" blue components of the colour specification in the RGB colour
 .\" domain, which is shared by "groff" and the PDF readers.
 .\"
-.ds PDFHREF.COLOUR   0.35 0.00 0.60
+.ds PDFHREF.COLOUR   0.00 0.35 0.60
 .defcolor pdf:href.colour rgb \*[PDFHREF.COLOUR]
 .\"
 .\" "PDFHREF.TEXT.COLOUR", on the other hand, is simply defined
@@ -492,18 +482,27 @@ am solely responsible for any bugs I may have introduced into this file.
 .ds pdf:href\\$1 \\$2
 .nr pdf:href.argc 2
 ..
+.de pdf:href.option-hex
+.\" ----------------------------------------------------------------------
+.\" ----------------------------------------------------------------------
+.ds pdf:hex \\$2
+.stringhex pdf:hex
+.ds pdf:href\\$1 \\$2
+.ds pdf:hrefhex\\$1 \\*[pdf:hex]
+.nr pdf:href.argc 2
+..
 .\"
 .\" Valid PDFHREF options are simply declared
 .\" by aliasing option handlers to "pdf:href.option",
 .\" or to "pdf:href.flag", as appropriate
 .\"
 .als pdf:href.opt-A pdf:href.option   \" affixed text
-.als pdf:href.opt-D pdf:href.option   \" destination name
+.als pdf:href.opt-D pdf:href.option-hex   \" destination name
 .als pdf:href.opt-E pdf:href.flag     \" echo link descriptor
 .als pdf:href.opt-F pdf:href.option   \" remote file specifier
-.als pdf:href.opt-N pdf:href.option   \" reference name
+.als pdf:href.opt-N pdf:href.option-hex   \" reference name
 .als pdf:href.opt-P pdf:href.option   \" prefixed text
-.als pdf:href.opt-T pdf:href.option   \" bookmark "tag"
+.als pdf:href.opt-T pdf:href.option-hex   \" bookmark "tag"
 .als pdf:href.opt-X pdf:href.flag     \" cross reference
 .\"
 .\" For references to another document file
@@ -533,6 +532,7 @@ am solely responsible for any bugs I may have introduced into this file.
 .   \"
 .   while \\n(.$ \{\
 .      if dpdf:href-\\$1 .rm pdf:href-\\$1
+.      if dpdf:hrefhex-\\$1 .rm pdf:hrefhex-\\$1
 .      shift
 .      \}
 .   \}
@@ -560,6 +560,8 @@ am solely responsible for any bugs I may have introduced into this file.
 .\"
 .ds pdf:href-D
 .ds pdf:href-N
+.ds pdf:hrefhex-D
+.ds pdf:hrefhex-N
 .\"
 .\" Parse, interpret, and strip any specified options from the
 .\" argument list.  (Note that only options with a declared handler
@@ -567,10 +569,22 @@ am solely responsible for any bugs I may have introduced into this file.
 .\" options -- anything which is not recognised is assumed to start
 .\" the "descriptive text" component of the argument list).
 .\"
-.while dpdf:href.opt\\$1 \{\
-.   pdf:href.opt\\$1 \\$@
-.   shift \\n[pdf:href.argc]
+.length pdf:l \\$1
+.ds pdf:char1 \\$1
+.substring pdf:char1 0 0
+.if !'\\*[pdf:char1]'-' .nr pdf:l 99
+.while (\\n[pdf:l]<4) \{\
+.   ie dpdf:href.opt\\$1 \{\
+.      pdf:href.opt\\$1 \\$@
+.      shift \\n[pdf:href.argc]
+.      length pdf:l \\$1
+.      if '\\$1'--' .break
+.      ds pdf:char1 \\$1
+.      substring pdf:char1 0 0
+.      if !'\\*[pdf:char1]'-' .nr pdf:l 99
 .   \}
+.   el .break
+.\}
 .\"
 .\" If we found "--", to mark the end of the options,
 .\" then we should discard it.
@@ -584,14 +598,24 @@ am solely responsible for any bugs I may have introduced into this file.
 .\" for the destination, then this marker will not be created.
 .\"
 .ds PDFBOOKMARK.NAME "\\*[pdf:href-N]\\*[pdf:href-D]
-.pdf*href.set \\*[PDFBOOKMARK.NAME] \\$1
-.ds pdf:look(\\*[PDFBOOKMARK.NAME]) \\$*
-.if dPDF.EXPORT .tm .ds pdf:look(\\*[PDFBOOKMARK.NAME]) \\$*
+.ds PDFBOOKMARK.NAME.HEX "\\*[pdf:hrefhex-N]\\*[pdf:hrefhex-D]
+.if '\\*[PDFBOOKMARK.NAME.HEX]'' \{\
+.  pdf:href.option-hex -D \\$1
+.  if '\\*[pdf:hrefhex-D]'' \{\
+.    pdf:error pdfhref has no destination
+.    nr pdf:href.ok 0
+.  \}
+.  ds PDFBOOKMARK.NAME \\*[pdf:href-D]
+.  ds PDFBOOKMARK.NAME.HEX \\*[pdf:hrefhex-D]
+.\}
+.pdf*href.set \\*[PDFBOOKMARK.NAME.HEX] \\$1
+.ds pdf:look(\\*[PDFBOOKMARK.NAME.HEX]) \\$*
+.if dPDF.EXPORT .tm .ds pdf:look(\\*[PDFBOOKMARK.NAME.HEX]) \\$*
 .\"
 .\"
 .\" Irrespective of whether this marker is created, or not,
 .\" the descriptive text will be copied to the groff output stream,
-.\" provided the "-E" option was specified
+.\" provided the "-E" option was sp0ecified
 .\"
 .if \\n[pdf:href-E] \&\\$*
 ..
@@ -650,10 +674,22 @@ am solely responsible for any bugs I may have introduced into this file.
 .\" options -- anything which is not recognised is assumed to start
 .\" the "link text" component of the argument list).
 .\"
-.while dpdf:href.opt\\$1 \{\
-.   pdf:href.opt\\$1 \\$@
-.   shift \\n[pdf:href.argc]
+.length pdf:l \\$1
+.ds pdf:char1 \\$1
+.substring pdf:char1 0 0
+.if !'\\*[pdf:char1]'-' .nr pdf:l 99
+.while (\\n[pdf:l]<4) \{\
+.   ie dpdf:href.opt\\$1 \{\
+.      pdf:href.opt\\$1 \\$@
+.      shift \\n[pdf:href.argc]
+.      length pdf:l \\$1
+.      if '\\.$1'--' .break
+.      ds pdf:char1 \\$1
+.      substring pdf:char1 0 0
+.      if !'\\*[pdf:char1]'-' .nr pdf:l 99
 .   \}
+.   el .break
+.\}
 .\"
 .\" If we found "--", to mark the end of the options, then we should
 .\" discard it.
@@ -666,8 +702,8 @@ am solely responsible for any bugs I may have introduced into this file.
 .\" can't identify the destination, then set "pdf:href.ok" to zero,
 .\" so this link will not be created.
 .\"
-.if !dpdf:href-D .pdf:href.option -D \\$1
-.if '\\*[pdf:href-D]'' \{\
+.if !dpdf:href-D .pdf:href.option-hex -D \\$1
+.if '\\*[pdf:hrefhex-D]'' \{\
 .   pdf:error pdfhref has no destination
 .   nr pdf:href.ok 0
 .   \}
@@ -680,6 +716,7 @@ am solely responsible for any bugs I may have introduced into this file.
 .\" And now, we have no further use for "pdf*href.link".
 .\"
 .rm pdf*href.link
+.nr pdf:href.pipe 0
 .\"
 .\" If the user specified any "link prefix" text, (using the "-P text"
 .\" option), then emit it BEFORE processing the "link text" itself.
@@ -699,7 +736,7 @@ am solely responsible for any bugs I may have introduced into this file.
 .      ds PDFHREF.DESC \\\\$*
 .      \}
 .   el \{\
-.      ie dpdf:look(\\*[pdf:href-D]) .ds PDFHREF.DESC \\*[pdf:look(\\*[pdf:href-D])]
+.      ie dpdf:look(\\*[pdf:hrefhex-D]) .ds PDFHREF.DESC \\*[pdf:look(\\*[pdf:hrefhex-D])]
 .      el .ds PDFHREF.DESC Unknown
 .      \}
 .   \" Apply border and colour specifications to the PDFMARK string
@@ -713,10 +750,12 @@ am solely responsible for any bugs I may have introduced into this file.
 .   \" text positions.
 .   \"
 \#.   if dPDFHREF.COLOUR .defcolor pdf:href.colour rgb \\*[PDFHREF.COLOUR]
+.   ds pdf:curcol \\n[.m]
 .   nr pdf:bm.width \\w'\\*[PDFHREF.DESC]'
+.   if '\\*[PDFHREF.DESC]'|' .nr pdf:href.pipe 1
 .   nop \&\m[\\*[PDFHREF.TEXT.COLOUR]]\c
 .   device pdf: markstart \\n[rst] \\n[rsb] \\n[PDFHREF.LEADING] \\*[pdf:href.link]
-.   nop \&\\*[PDFHREF.DESC]\X'pdf: markend'\m[]\c
+.   if !\\n[pdf:href.pipe] .nop \&\\*[PDFHREF.DESC]\X'pdf: markend'\m[\\*[pdf:curcol]]\c
 .   \"
 .   \" Clean up the temporary registers and strings, used to
 .   \" compute the "hot-spot" bounds, and format the reference,
@@ -733,7 +772,7 @@ am solely responsible for any bugs I may have introduced into this file.
 .\" And then, if the user specified any affixed text, (using the
 .\" "-A text" option), we tack it on at the end.
 .\"
-.nop \&\\*[pdf:href-A]
+.if !\\n[pdf:href.pipe] .nop \&\\*[pdf:href-A]
 ..
 .\" Macro "pdf*href-I" is used for one time initialisation of special
 .\" "pdfhref" features; (currently, only the above page trap hook is
@@ -773,7 +812,7 @@ am solely responsible for any bugs I may have introduced into this file.
 .\" in any other PDF document, through "pdf*href-L.file".
 .\"
 .als pdf*href-L      pdf*href
-.ds  pdf*href-L.link /Dest /\\\\*[pdf:href-D]
+.ds  pdf*href-L.link /Dest /\\\\*[pdf:hrefhex-D]
 .ds  pdf*href-L.file /Action /GoToR \\\\*[pdf:href.files] \\*[pdf*href-L.link]
 .\"
 .\" "pdf*href-O" is the "official" handler for creating PDF
@@ -824,6 +863,13 @@ am solely responsible for any bugs I may have introduced into this file.
 .device pdf: background \\$*
 ..
 .
+.de pdfpagenumbering
+.\" 1=type of [D=decimal,R=Roman,r=roman,A=Uppercase,a=lowercase]
+.\" 2=prefix label
+.\" 3=start number
+.device pdf: pagenumbering \\$*
+..
+.
 .cp \n[*groff_pdf_tmac_C]
 .do rr *groff_pdf_tmac_C
 .

============================================================

Remove artificial slanting in pdf:SS, now redundant with new
SS font.

No longer use asciify which prevented non-latin languages from
using bookmarks, now raw string is passed to gropdf.

Hexify key in pdf:look array.

If the pipe character '|' is used as the PDFHREF.DESC don't output,
just start marking a hotspot. The end of hotspot needs to be
signalled manually by calling \X'pdf: markend'. This is used in
.MT and .UR processing since they collect the parameter in a diversion
rather than passing a single parameter.

Harden Keith Marshall's original pdfhref parsing to fix bug #64576
among others.

Add .pdfpagenumbering

============================================================

diff --git a/tmac/an.tmac b/tmac/an.tmac
index 9e38ba569..8c25d875b 100644
--- a/tmac/an.tmac
+++ b/tmac/an.tmac
@@ -205,7 +205,7 @@
 .
 .\" Write a bookmark/anchor/link target $2 at hierarchical depth $1.
 .de an*bookmark
-.  if \\n[an*is-output-pdf] .pdfbookmark \\$1 \\$2
+.  if \\n[an*is-output-pdf] .pdfbookmark \\$1 "\\$2"
 ..
 .
 .\" Begin man page.
@@ -420,7 +419,7 @@
 .  ie \\n[cR] .pl +1v
 .  el         .sp .5i
 .  if !\\n[an*was-TH-bookmark-emitted] \{\
-.    an*bookmark 1 \E*[an*page-ref-string]
+.    an*bookmark 1 "\\*[an*page-ref-string]"
 .    nr an*was-TH-bookmark-emitted 1
 .  \}
 .  tl '\\*[an-pageref]'\\*[an-extra3]'\\*[an-pageref]'
@@ -586,6 +585,7 @@ contains unsupported escape sequence
 .  PT
 .  ev
 .  ns
+.  if '\\*[.T]'pdf' .pdfmarkrestart
 ..
 .
 .\" Schedule a page break when the next output line is written (not
@@ -597,6 +597,7 @@ contains unsupported escape sequence
 .\" Prepare the footer for a page of the document.
 .de1 an-footer
 .  if \\n[an-suppress-header-and-footer] .return
+.  if '\\*[.T]'pdf' .pdfmarksuspend
 .  ev an*env-header-and-footer
 .  ie \\n[cR] \
 .    ds an*ofoot "\\*[an-pageref]\"
@@ -695,7 +696,7 @@ contains unsupported escape sequence
 .  if \\n[.$] \{\
 .    ds an-section-heading \\$*\"
 .    if \\n[CS] .stringup an-section-heading
-.    an*bookmark 2 \E*[an-section-heading]
+.    an*bookmark 2 "\\*[an-section-heading]"
 .    nop \&\\*[an-section-heading]
 .  \}
 .  if \\n[an-remap-I-style-in-headings] .ftr I I
@@ -721,7 +722,7 @@ contains unsupported escape sequence
 .  if \\n[an-remap-I-style-in-headings] .ftr I \\*[an-heading-family]BI
 .  if \\n[.$] \{\
 .    ds an*subsection-heading \\$*\"
-.    an*bookmark 3 \E*[an*subsection-heading]
+.    an*bookmark 3 "\\*[an*subsection-heading]"
 .    nop \&\\$*
 .  \}
 .  if \\n[an-remap-I-style-in-headings] .ftr I I
@@ -1068,6 +1089,7 @@ contains unsupported escape sequence
 .  if (\\n[an*is-in-link-text-diversion] & \\n[an*do-hyperlink]) \{\
 .    \" Start diversion in a new environment.
 .    ev an*link-text-env
+.    if \\n[an*is-output-pdf] \&\m[\\*[PDFHREF.TEXT.COLOUR]]\c
 .    di an*link-text-div
 .    ll (\\n[an*saved-line-length]u - \\n[an*saved-indentation]u)
 .  \}
@@ -1091,6 +1113,9 @@ contains unsupported escape sequence
 .    ie \\n[dn] \{\
 .      if \\n[an*is-output-html] \
 .        nop \X^html:<a href="\\*[an*prefix]\\*[an*hyperlink]">^\c
+.      if \\n[an*is-output-pdf] \{\
+.        pdfhref W -D \\*[an*prefix]\\*[an*hyperlink] -- "|"
+.      \}
 .      if \\n[an*is-output-terminal] \
 .        nop \X^tty: link \\*[an*prefix]\\*[an*hyperlink]^\c
 .      \" Strip off the final newline of the diversion and emit it.
@@ -1101,6 +1126,8 @@ contains unsupported escape sequence
 .        nop \X^html:</a>^\c
 .      if \\n[an*is-output-terminal] \
 .        nop \X^tty: link^\c
+.      if \\n[an*is-output-pdf] \
+.        nop \X'pdf: markend'\m[\\*[pdf:curcol]]\c
 .    \}
 .    \" If there was no link text, format URI as its own link text.  We
 .    \" don't add angle brackets here.
@@ -1108,6 +1135,9 @@ contains unsupported escape sequence
 .      if \\n[an*is-output-html] \
 .        nop \X^html:<a href="\\*[an*prefix]\\*[an*hyperlink]">\
 \\*[an*hyperlink]</a>^\c
+.      if \\n[an*is-output-pdf] \
+.        pdfhref W -D \\*[an*prefix]\\*[an*hyperlink] -- \
+\\*[an*hyperlink]
 .      if \\n[an*is-output-terminal] \
 .        nop \X^tty: link \\*[an*prefix]\\*[an*hyperlink]^\
 \\*[an*hyperlink]\X^tty: link^\c

============================================================

Protect against hotspot crossing page boundary, .pdfmarksuspend
and .pdfmarkrestart

Don't use \E in parameters, no longer use copy-in mode.

Support .UR/.UE and .MT/.ME as pdf links.

============================================================

diff --git a/tmac/anmark.tmac b/tmac/anmark.tmac
new file mode 100644
index 000000000..39277753e
--- /dev/null
+++ b/tmac/anmark.tmac
@@ -0,0 +1,44 @@
+.nr PDFOUTLINE.FOLDLEVEL 1
+.defcolor pdf:href.colour rgb 0.00 0.25 0.75
+.pdfinfo /Title "The Groff Manpage Book"
+.de an*cln
+.  ds \\$1
+.  als an*cln:res \\$1
+.  shift
+.  ds an*cln:res \\$*\"
+.  ds an*cln:chr \\$*
+.  substring an*cln:chr 0 0
+.  if '\\*[an*cln:chr]'\%' \{\
+.    substring an*cln:res 1
+.  \}
+..
+.
+.de END
+..
+.
+.am reload-man END
+.de an*bookmark
+.  if \\\\n[an*is-output-pdf] \{\
+.    an*cln an*page-ref-nm \\\\$2\"
+.    ie (\\\\$1=1) .pdfbookmark -T "\\\\*[an*page-ref-nm]" \\\\$1 \\\\$2
+.    el .pdfbookmark \\\\$1 \\\\$2
+.  \}
+..
+.
+.de1 MR
+.  if ((\\\\n[.$] < 2) : (\\\\n[.$] > 3)) \
+.    an-style-warn .\\\\$0 expects 2 or 3 arguments, got \\\\n[.$]
+.  if \\\\n[an*is-output-pdf] \{\
+.    ie \\\\n(.$=1 \
+.      I \\\\$1
+.    el \{\
+.      an*cln an*page-ref-nm \\\\$1(\\\\$2)
+.      ds an*page-ref-nm-hex \\\\*[an*page-ref-nm]
+.      stringhex an*page-ref-nm-hex
+.      ie d pdf:look(\\\\*[an*page-ref-nm-hex]) .pdfhref L -D \\\\*[an*page-ref-nm] -A "\\\\$3" -- \fI\\\\$1\fP(\\\\$2)
+.      el .IR \\\\$1 (\\\\$2)\\\\$3
+.    \}
+.  \}
+.  hy \\\\n[an*hyphenation-mode]
+..
+.END