8

I am an occasional latex user (means that I never used it to the point of writing a big professional text with, but wrote a few pieces for personal use) and want to create some visting cards, basic and simple, non pretentious but not too ugly.

Here, I want to format my pgp key fingerprint. Usually pgp keys got a space every four digits to make them more readable.

So it transform this :

47D6459B2DCEE5C1439C53330403A28B2D8DE8FB

into this :

47D6 459B 2DCE E5C1 439C 5333 0403 A28B 2D8D E8FB

So how to insert a space between each group ?

Second, it would be nice if I could create a new line in the middle, so at the 5th space. How do I do that ?

47D6 459B 2DCE E5C1 439C
5333 0403 A28B 2D8D E8FB

I will create a macro out of this. Thanks for help !

4
  • 1
    Welcome to TeX.SX! Please help us to help you and add a minimal working example (MWE) that shows what you've tried and your basic document setup. It will be much easier for us to reproduce your situation and find out what the issue is when we see compilable code, starting with \documentclass{...} and ending with \end{document}. What are all those wiggly bits? Is there any rhyme or reason to the transformation? (Apart from the spaces. Why so many things->5?) – cfr Jan 31 '15 at 23:23
  • @cfr Welcome to the Unicode revolution. – percusse Jan 31 '15 at 23:40
  • 1
    I can't vote to close, but this is essentially a duplicate of: tex.stackexchange.com/questions/128524/… – wchargin Feb 1 '15 at 2:05
  • If you want a copy/paste of the string (in the PDF) to omit the spaces, then this answer tex.stackexchange.com/questions/198516/… might be of use. – Steven B. Segletes Feb 2 '15 at 11:34
7

A simple solution that doesn't check for the length of the string:

\documentclass{article}

\makeatletter
\newcommand{\printpgp}[1]{\print@pgp#1\@nil}

\def\print@pgp#1#2#3#4#5\@nil{%
  #1#2#3#4%
  \ifx\\#5\\%
    \expandafter\@gobble
  \else
    \expandafter\@firstofone
  \fi
  {~\print@pgp#5\@nil}%
}
\makeatother

\begin{document}
$|$\printpgp{47D6459B2DCEE5C1439C53330403A28B2D8DE8FB}$|$
\end{document}

I added two bars at the sides just to show that no spurious space has been added.

enter image description here

If you want to print the fingerprint in monospaced font, just change the first definition into

\newcommand{\printpgp}[1]{\texttt{\print@pgp#1\@nil}}

enter image description here

More features. If the length is wrong, then “INVALID PGP” is printed. Spaces in the strings are ignored. You can specify a key-value interface: break is by default true, but with break=false the key is printed on one line. With font you can specify a font choice (default \ttfamily).

\documentclass{article}

\usepackage{xparse}

\ExplSyntaxOn
\NewDocumentCommand{\printpgp}{O{}m}
 {
  \par\noindent
  \group_begin:
  \keys_set:nn { xxiidecembre/printpgp } { #1 }
  \xxiidecembre_printpgp:n { #2 }
  \par
  \group_end:
 }

\keys_define:nn { xxiidecembre/printpgp }
 {
  font .tl_set:N = \l_xxiidecembre_pgpfont_tl,
  font .initial:n = \ttfamily,
  break .bool_set:N = \l_xxiidecembre_pgpbreak_bool,
  break .initial:n = true,
 }

\tl_new:N \l__xiidecembre_pgp_tl
\int_new:N \l__xxiidecembre_step_int
\int_new:N \l__xxiidecembre_group_int
\cs_new_protected:Npn \xxiidecembre_printpgp:n #1
 {
  \tl_use:N \l_xxiidecembre_pgpfont_tl
  \tl_set:Nn \l__xiidecembre_pgp_tl { #1 }
  \tl_replace_all:Nnn \l__xiidecembre_pgp_tl { ~ } { }
  \int_compare:nTF { \tl_count:N \l__xiidecembre_pgp_tl = 40 }
   {
    \__xxiidecembre_printpgp:V \l__xiidecembre_pgp_tl
   }
   {
    INVALID~PGP
   }
 }

\cs_new_protected:Npn \__xxiidecembre_printpgp:n #1
 {
  \tl_map_inline:nn { #1 }
   {
    ##1
    \int_incr:N \l__xxiidecembre_step_int
    \int_compare:nT { \l__xxiidecembre_step_int = 4 }
     {
      \int_incr:N \l__xxiidecembre_group_int
      \int_compare:nTF { \l__xxiidecembre_group_int = 5 }
       {
        \bool_if:NTF \l_xxiidecembre_pgpbreak_bool { \\ } { ~ }
       }
       {
        \c_space_tl
       }
      \int_zero:N \l__xxiidecembre_step_int
     }
   }
 }
\cs_generate_variant:Nn \__xxiidecembre_printpgp:n { V }
\ExplSyntaxOff

\begin{document}

\printpgp{47D6459B2DCEE5C1439C53330403A28B2D8DE8FB}

\medskip

\printpgp[break=false]{47D6459B2DCEE5C1439C53330403A28B2D8DE8FB}

\medskip

\printpgp[font=\ttfamily\LARGE]{47D6 459B 2DCE E5C1 439C 5333 0403 A28B 2D8D E8FB}

\medskip

\printpgp{0000 1111 2222}

\end{document}

enter image description here

First spaces are removed, then the input is mapped character by character, incrementing a counter at each step; if the counter is at 4, the group counter is stepped; a space is issued, but \\ is used if the group counter is 5 and break=true is in force.

2
  • 1
    @22decembre egrep (grep with extended regular expression) or egreg? ;-) – Paul Gaborit Feb 1 '15 at 10:42
  • egreg indeed... – 22decembre Feb 1 '15 at 14:05
5

Both answers here use not so optimal approach of parameter manipulation because the #5 parameter (the rest of the digits) is rewritten for each step in the loop. Better solution is:

\newcount\tmpnum
\def\pgp#1{\tmpnum=0 \pgpA #1 {}...}
\def\pgpA#1#2#3#4{\ifx^#1^\unskip\else
   \ifnum\tmpnum=5\par\fi
   \advance\tmpnum by1
   #1#2#3#4
   \expandafter\pgpA\fi}

\pgp{47D6459B2DCEE5C1439C53330403A28B2D8DE8FB}
4
\documentclass{scrartcl}

\newcount\pgpcount
\long\def\firstofone#1{#1}
\long\def\gobbleone#1{}
\def\dopgp#1#2#3#4#5\relax
  {#1#2#3#4\if\relax\detokenize{#5}\relax
    \expandafter\gobbleone
   \else
    \expandafter\firstofone
   \fi
   {\advance\pgpcount by 1
    \ifnum\pgpcount=5
      \par\pgpcount=0
    \else\ \fi
    \dopgp#5\relax\unskip}}

\def\pgp#1{\dopgp#1\relax}

\begin{document}

\pgp{47D6459B2DCEE5C1439C53330403A28B2D8DE8FB}

\end{document}

egreg beat me, but I think it's my first time playing with “TeX macros”, so I'll leave it here.

1

I am surprised there hasn't been a luatex based solution yet. Here is one in ConTeXt that works for both balanced and non-balanced inputs. I have defined a separate highlight to change the style of the output (monospaced font, etc.).

\startluacode
  local C, Ct     = lpeg.C, lpeg.Ct
  local lpegmatch = lpeg.match
  local hexdigit  = lpeg.patterns.hexdigit 
  local concat    = table.concat

  userdata = userdata or {}

  userdata.splitPGP = function(str)
    local block    = C(hexdigit * hexdigit * hexdigit * hexdigit)
    local nonblock = C(hexdigit^0)
    local pattern  = Ct(block^0 * nonblock)
    local tbl      = lpegmatch(pattern, str)

    context.PGPstyle(concat(tbl, " "))

  end
\stopluacode

\definehighlight[PGPstyle][style=mono]

\define[1]\splitPGP{\ctxlua{userdata.splitPGP([==[#1]==])}}

\starttext
\startlines
\splitPGP{47D6459B2DCEE5C1439C53330403A28B2D8DE8FB}
\splitPGP{47D6459B2DCEE5C1439C53330403A28B2D8DE8}
\stoplines
\stoptext

enter image description here

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.