7

How can we encode some text/string using Base64 encoding in LaTeX? or any alternative solution?

For example, the Base64 encoding of string Latex is bGF0ZXg=.

Here's a representation of what I want to do:

\newcommand{\String}{LATEX}
\newcommand{\BaseEncode}{**something** \String **something**}
ZEESHAN
  • 247

3 Answers3

15

Interesting programming task! :)

Here's a base64 encoding and decoding code. To encode, it uses \int_to_bin:n on each character, padding to 8 bits, then separates the output in chunks of 6 bits and uses a lookup macro to select the encoded characters. To decode, it does the lookup to normalise the encoded input to ASCII, and then uses \int_to_bin:n on each character, padding to 6 bits, then separates that in chunks of 8 bits and uses \char_generate:nn to make ASCII characters back.

The code below prints the base64 encoding of Hello, world!, and then the base 64 decoding of that:

enter image description here

The commands are expandable, so you can easily use them to write to files or to the terminal

\documentclass{article}

\ExplSyntaxOn \NewExpandableDocumentCommand \BaseEncode { m } { \zeeshan_base_encode:n {#1} } \NewExpandableDocumentCommand \BaseDecode { m } { \zeeshan_base_decode:n {#1} } \cs_new:Npn \zeeshan_base_encode:n #1 { \exp_args:Ne __zeeshan_base_encode_bin:n { \str_map_function:nN {#1} __zeeshan_base_char_to_bin:n } } \cs_new:Npn __zeeshan_base_char_to_bin:n #1 { \exp_args:Nf __zeeshan_base_char_to_bin_pad:nn { \int_to_bin:n { #1 } } { 8 } } \cs_new:Npn \__zeeshan_base_char_to_bin_pad:nn #1 #2 { \prg_replicate:nn { #2 - \str_count:n {#1} } { 0 } #1 } \cs_new:Npn \__zeeshan_base_encode_bin:n #1 { \__zeeshan_base_encode_bin:w #1 222222 \q_stop } \cs_new:Npn \__zeeshan_base_encode_bin:w #1#2#3#4#5#6 { \__zeeshan_base_encode_bin_aux:w #1#2#3#4#5#6 2 \q_nil } \cs_new:Npn \__zeeshan_base_encode_bin_aux:w #1 2 #2 \q_nil { \tl_if_empty:nTF {#2} { \exp_args:Nf \__zeeshan_base_encode_lookup:n { \int_from_bin:n {#1} } } { \tl_if_empty:nT {#1} { \use_none_delimit_by_q_stop:w } \exp_args:Nf \__zeeshan_base_encode_lookup:n { \exp_args:Ne \int_from_bin:n { #1 \prg_replicate:nn { \str_count:n {#2} } { 0 } } } \int_compare:nNnTF { \str_count:n {#2} } = { 2 } { = } { == } \use_none_delimit_by_q_stop:w } \__zeeshan_base_encode_bin:w } \cs_new:Npn \__zeeshan_base_encode_lookup:n #1 { \if_int_compare:w #1 &lt; 26 ~ \char_generate:nn { #1 +A } { 12 } \else: \if_int_compare:w #1 < 52 ~ \char_generate:nn { #1 + a - 26 } { 12 } \else: \if_int_compare:w #1 &lt; 62 ~ \char_generate:nn { #1 +0 - 52 } { 12 } \else: \if_int_compare:w #1 = 62 ~ + \else: / \fi: \fi: \fi: \fi: } % \cs_new:Npn \zeeshan_base_decode:n #1 { \exp_args:Ne __zeeshan_base_decode_bin:n { \str_map_function:nN {#1} __zeeshan_base_to_bin:n } } \cs_new:Npn __zeeshan_base_to_bin:n #1 { \exp_args:Ne __zeeshan_base_char_to_bin_pad:nn { __zeeshan_base_to_bin_lookup:n {#1} } { 6 } } \cs_new:Npn __zeeshan_base_to_bin_lookup:n #1 { \if_int_compare:w #1 &gt; 47 ~ \if_int_compare:w#1 > 64 ~ \int_to_bin:n { #1 - \if_int_compare:w#1 > 96 ~ 71 \else: 65 \fi: } \else: \if_int_compare:w #1 = 61 ~ 222222 \else: \int_to_bin:n {#1 + 4 } \fi: \fi: \else: 11111 \if_int_compare:w `#1 = 43 ~ 0 \else: 1 \fi: \fi: } \cs_new:Npn __zeeshan_base_decode_bin:n #1 { __zeeshan_base_decode_bin:w #1 2222 2222 \q_stop } \cs_new:Npn __zeeshan_base_decode_bin:w #1#2#3#4#5#6#7#8 { __zeeshan_base_decode_bin_aux:w #1#2#3#4#5#6#7#8 2 \q_nil } \cs_new:Npn __zeeshan_base_decode_bin_aux:w #1 2 #2 \q_nil { \tl_if_empty:nTF {#2} { \exp_args:Nf __zeeshan_base_output_decoded:n { \int_from_bin:n {#1} } } { \use_none_delimit_by_q_stop:w } __zeeshan_base_decode_bin:w } \cs_new:Npn __zeeshan_base_output_decoded:n #1 { \int_compare:nNnTF {#1} = { 32 } { ~ } { \char_generate:nn {#1} { 12 } } } \ExplSyntaxOff

\begin{document}

\texttt{\BaseEncode{Hello, world!}}

\BaseDecode{SGVsbG8sIHdvcmxkIQ==}

\edef\x{\BaseEncode{Hello, world!}} \typeout{Encoded: \x}

\typeout{Decoded: \expandafter\BaseDecode\expandafter{\x}}

\end{document}

11

LuaTeX ships with LuaSocket whose MIME module supports a bunch of common encodings and decodings.

All codeblocks are one file:

\documentclass{article}
\usepackage{luacode}
\begin{luacode*}
local mime = require("mime")
local lft = lua.get_functions_table()
lft[#lft + 1] = function()
    local str = token.scan_string()
    tex.sprint(-2, (mime.b64(str)))
end
token.set_lua("BaseEncode", #lft, "global")
lft[#lft + 1] = function()
    local str = token.scan_string()
    tex.sprint(-2, (mime.unb64(str)))
end
token.set_lua("BaseDecode", #lft, "global")
\end{luacode*}
\begin{document}
\texttt{\BaseEncode{Hello, world!}}

\BaseDecode{SGVsbG8sIHdvcmxkIQ==} \end{document}

Henri Menke
  • 109,596
3

A rather simple solution if you have -shell-escape enabled (note that enabling -shell-escape opens all sorts of security questions, so proceed with care). The \BaseEncode and \BaseDecode commands can save the return string in the optional argument as in the third example. If you're on Windows you just have to change the definition of \base@sixtyfour@cmd to whatever command-line tool for base64 encoding/decoding is available.

\documentclass{article}
\makeatletter
\newcommand\BaseEncode[2][\relax]{\expandafter\base@sixtyfour\detokenize{#2}$E#1}
\newcommand\BaseDecode[2][\relax]{\expandafter\base@sixtyfour\detokenize{#2}$D#1}
\def\base@sixtyfour#1$#2#3{%
  \begingroup
    \endlinechar\m@ne \everyeof{\noexpand}%
    \ifx \relax#3\@@input"|\base@sixtyfour@cmd{#1}{#2}" \relax
    \else\xdef#3{\@@input"|\base@sixtyfour@cmd{#1}{#2}" }%
    \fi
  \endgroup}
\def\base@sixtyfour@cmd#1#2{echo #1 | base64 \ifx D#2--decode \fi -}
\makeatother
\begin{document}
\texttt{\BaseEncode{Hello, world!}}

\BaseDecode{SGVsbG8sIHdvcmxkIQo=}

\BaseEncode[\tmp]{Hello, world!} \typeout{Encoded: \tmp} \end{document}