udpated:
Since 2018/04/01 LaTeX release, inputenc+utf8 is active (sic) by default, and this broke the v3 approach below, because the ascii bytes in range 128-255 are now active and they were assumed here of catcode 12.
I have made a small edit to my v3 code to let it be usable both with or without inputenc loaded for old LaTeX or with new LaTeX.
(pdflatex only answer)
Table of contents
v1 and v2 exploit already existing macros from the LaTeX kernel to produce the macros ultimately expected by the LaTeX font selection system.
v3 is completely different. It produces expandably the UTF-8 bytes. It does not use inputenc at all but does produce active leading bytes so that it is "inputenc+utf8" ready. The active tokens are protected using \protect
(no e-TeX \protected
here), and to write them to file stream one needs to issue \set@display@protect
. They can be use inside an \edef
, but need the LaTeX \protected@edef
for that, for the same reason. Also macro \UniToUTF
now admits numerical input, so that the "
must be explicitely used in case of hexadecimal input (I do not understand why LaTeX's \DeclareUnicodeCharacter
contrarily to utf8x
's one does not do that either). The advantage here is that it is easy to set-up loops producing such characters in a row using an associated counter or other devices. For the sake of illustrating this, the mwe uses package xinttools
but this is only for the examples.
v1
Here is quickly jotted down hack which seems to achieve with pdflatex what you want. May need some corrections for working in general, (short on time today).
\documentclass{article}
\usepackage[utf8]{inputenc}
\usepackage[T1]{fontenc}
\DeclareUnicodeCharacter{20AC}{Hallo}
\makeatletter
\newcommand\unicodetoutf[1]{{\count@ "#1 \parse@XML@charref}%
\UTFviii@tmp}
\let\@preamblecmds\@empty
\makeatother
\begin{document}
€ ^^e2^^82^^ac \unicodetoutf{20AC} %<-- should output Hallo
\end{document}
v2
Here is edit to address issues about avoiding \let\@preamblecmds\@empty
and allowing ascii chars:
\documentclass{article}
\usepackage[utf8]{inputenc}
\usepackage[LGR,T1]{fontenc}
%\tracingmacros1
\DeclareUnicodeCharacter{20AC}{Hallo}
%\tracingmacros0
\makeatletter
\let\UF@parse@UTFviii@a\parse@UTFviii@a
\let\UF@parse@UTFviii@b\parse@UTFviii@b
\newcommand*\UF@parse@XML@charref{%
\ifnum\count@<"A0\relax
\xdef\UTFviii@tmp{\char\the\count@\space}% \count@ not globally modified
\else\ifnum\count@<"800\relax
\UF@parse@UTFviii@a,%
\UF@parse@UTFviii@b C\UTFviii@two@octets.,%
\else\ifnum\count@<"10000\relax
\UF@parse@UTFviii@a;%
\UF@parse@UTFviii@a,%
\UF@parse@UTFviii@b E\UTFviii@three@octets.{,;}%
\else
\UF@parse@UTFviii@a;%
\UF@parse@UTFviii@a,%
\UF@parse@UTFviii@a!%
\UF@parse@UTFviii@b F\UTFviii@four@octets.{!,;}%
\fi
\fi
\fi
}
\newcommand\unicodetoutf[1]{%
{\count@ "#1 \UF@parse@XML@charref}%
\UTFviii@tmp
}
\makeatother
\begin{document}
€ ^^e2^^82^^ac \unicodetoutf{20AC} %<-- should output Hallo
A ^^41 \unicodetoutf{0041}
e ^^65 \unicodetoutf{0065}
é ^^c3^^a9 \unicodetoutf{00E9}
\fontencoding{LGR}\selectfont
α ^^ce^^b1 \unicodetoutf{03B1}
\end{document}
The idea is to plug into the utf8.def
Unicode to UTF-8 already coded parsing by the LaTeX team.
v3
Here is the expandable method. I long hesitated with the 7bit ascii range and the problems with catcodes, to start with many of them are declared ignored tokens by TeX/LaTeX. In the end I decided that all will be produced with catcode 12. However, when writing out to a file, they will nevertheless be sometimes output in TeX's core ^^
notation, not as genuine UTF-8 7bit ascii bytes.
It is not completely obvious to set-up macros expanding to all those tokens as catcode 12 tokens, I have chosen a rather systematic way rather than use shortcuts.
Please check out the code comments for more details.
\documentclass{article}
\makeatletter
% First, the most complicated, define macros extending to catcode 12
% tokens in the 7bit ascii range. Unfortunately, when writing out
% to a file, some of them will get converted back to ^^ notation, and
% not be written there as UTF-8 bytes.
%% 7bit ASCII RANGE (with all its TeX catcode complications)
%% First we handle the upper part (ascii code 127 handled later)
\begingroup
\catcode`\^ 12
\catcode`\_ 12
\catcode`\{ 12
\catcode`\} 12
\catcode`\~ 12
\catcode`\? 0
\catcode`\< 1
\catcode`\> 2
\catcode`\\ 12
?def?y<?endgroup
?count@ 64
?@tfor?x:=%
@ABCDEFGHIJKLMNO%
PQRSTUVWXYZ[\]^_%
`abcdefghijklmno%
pqrstuvwxyz{|}~?do<%
?expandafter?xdef?csname utf8byte?the?count@?endcsname<?expandafter?string?x>%
?advance?count@?@ne>%
>?y
%% Now we handle the lower part
\begingroup
\catcode127 14
\def\z{utf8byte}%
\def\x{%
\count@ 0
\loop
\catcode\count@ 12
\advance\count@\@ne
\ifnum\count@<64
\repeat}\x^^?
\def\y{^^?
\count@=0\relax^^?
\@tfor\x:=^^?
^^@^^A^^B^^C^^D^^E^^F^^G^^H^^I^^J^^K^^L^^M^^N^^O^^?
^^P^^Q^^R^^S^^T^^U^^V^^W^^X^^Y^^Z^^[^^\^^]^^^^^_^^?
^^`^^a^^b^^c^^d^^e^^f^^g^^h^^i^^j^^k^^l^^m^^n^^o^^?
^^p^^q^^r^^s^^t^^u^^v^^w^^x^^y^^z^^{^^|^^}^^~\do{^^?
\expandafter\xdef\csname\z\the\count@\endcsname{\x}^^?
\advance\count@\@ne}}^^?
\y\endgroup
%% ascii codes 63 and 127 were not handled yet
\@namedef{utf8byte63}{?}%
\begingroup
\catcode127 12
\global\@namedef{utf8byte127}{^^?}%
\endgroup
%% Now, the easier part, we define the UTF-8 continuation bytes
%% (catcode 12) and the UTF-8 leading bytes (as \protect'ed active
%% tokens). This set-up is ready for utf8+inputenc but does not need
%% it for its definitions. (this document does not load inputenc)
%% UPDATE: but LaTeX newer than 2018/04/01 loads inputenc+utf8 per
%% default, so a small change has been made below.
%% CONTINUATION BYTES
\begingroup
% prior to LaTeX 2018/04/01 these 64 bytes were assigned catcode12
% by LaTeX in absence of inputenc; but nowadays these bytes are active
% by default. Hence I modified \x into \expandafter\string\x which
% works whether or not the document incorporating this code is with
% old (with or without utf8+inputenc) LaTeX or with newer LaTeX
\count@ "80
\@tfor\x:=%
^^80^^81^^82^^83^^84^^85^^86^^87^^88^^89^^8a^^8b^^8c^^8d^^8e^^8f%
^^90^^91^^92^^93^^94^^95^^96^^97^^98^^99^^9a^^9b^^9c^^9d^^9e^^9f%
^^a0^^a1^^a2^^a3^^a4^^a5^^a6^^a7^^a8^^a9^^aa^^ab^^ac^^ad^^ae^^af%
^^b0^^b1^^b2^^b3^^b4^^b5^^b6^^b7^^b8^^b9^^ba^^bb^^bc^^bd^^be^^bf%
\do{\expandafter\xdef\csname utf8byte\the\count@\endcsname{\expandafter\string\x}%
\advance\count@\@ne}%
%% LEADING BYTES
%% we will make them TeX active, as inputenc does with utf8
%% We \protect them à la LaTeX, and writing to a file will
%% have to be made in a \set@display@protect context
\count@ "C2
\loop
\catcode\count@\active
\advance\count@ 1
\ifnum\count@ < "F5
\repeat
\count@ "C2
\@tfor\x:=^^c2^^c3^^c4^^c5^^c6^^c7^^c8^^c9^^ca^^cb^^cc^^cd^^ce^^cf%
^^d0^^d1^^d2^^d3^^d4^^d5^^d6^^d7^^d8^^d9^^da^^db^^dc^^dd^^de^^df%
^^e0^^e1^^e2^^e3^^e4^^e5^^e6^^e7^^e8^^e9^^ea^^eb^^ec^^ed^^ee^^ef%
^^f0^^f1^^f2^^f3^^f4%
\do{\expandafter\protected@xdef
\csname utf8byte\the\count@\endcsname{\expandafter\protect\x}%
\advance\count@\@ne}%
\endgroup
%% Time now to define our expandable Unicode to UTF-8 converter
%% a "case-switch" utility
\long\def\xintdothis #1#2\xintorthat #3{\fi #1}%
\let\xintorthat \@firstofone
%% EXPANDABLE UNICODE TO UTF-8 BYTES CONVERTER
%% The macro accepts any numexpr compatible input.
%% This means hexadecimal input must be prefixed by "
%% (and must use uppercase hex digits.)
%% Advantage with such numerical inputs is to allow easy usage
%% over some arithmetic range with counts or counters.
%% NOTE: \numexpr is the only e-TeX extension used.
\newcommand*\UniToUTF[1]{\expandafter\UniToUTF@\the\numexpr#1.}%
\def\UniToUTF@ #1.{%
\ifnum#1<"80 \xintdothis\uni@ascii\fi
\ifnum#1<"800 \xintdothis\uni@twooctets\fi
\ifnum#1<"10000 \xintdothis\uni@threeoctets\fi
%% maybe add here some out of range check ?
\xintorthat\uni@fouroctets {#1}%
}
%% CONVERSION FORMULAS MORALLY BASED ON EXPLANATIONS OF
%% https://en.wikipedia.org/wiki/UTF-8
%% USING OCTAL NOTATION
\def\uni@ascii#1{\csname utf8byte#1\endcsname}%
\def\uni@twooctets#1{\expandafter\uni@twooctets@i
\the\numexpr(#1+32)/64-1.#1.}%
\def\uni@twooctets@i#1.#2.{%
\csname utf8byte\the\numexpr192+#1\expandafter\expandafter\expandafter
\endcsname
\csname utf8byte\the\numexpr128+#2-#1*64\endcsname
}%
\def\uni@threeoctets#1{\expandafter\uni@threeoctets@i
\the\numexpr(#1+32)/64-1.#1.}%
\def\uni@threeoctets@i#1.#2.{\expandafter\uni@threeoctets@ii
\the\numexpr(#1+32)/64-1\expandafter.%
\the\numexpr#1\expandafter\expandafter\expandafter.%
\csname utf8byte\the\numexpr128+#2-#1*64\endcsname}%
\def\uni@threeoctets@ii#1.#2.{%
\csname utf8byte\the\numexpr224+#1\expandafter\expandafter\expandafter
\endcsname
\csname utf8byte\the\numexpr128+#2-#1*64\endcsname
}%
\def\uni@fouroctets#1{\expandafter\uni@fouroctets@i
\the\numexpr(#1+32)/64-1.#1.}%
\def\uni@fouroctets@i#1.#2.{\expandafter\uni@fouroctets@ii
\the\numexpr(#1+32)/64-1\expandafter.%
\the\numexpr#1\expandafter\expandafter\expandafter.%
\csname utf8byte\the\numexpr128+#2-#1*64\endcsname}%
\def\uni@fouroctets@ii#1.#2.{\expandafter\uni@fouroctets@iii
\the\numexpr(#1+32)/64-1\expandafter.%
\the\numexpr#1\expandafter\expandafter\expandafter.%
\csname utf8byte\the\numexpr128+#2-#1*64\endcsname}%
\def\uni@fouroctets@iii#1.#2.{%
\csname utf8byte\the\numexpr240+#1\expandafter\expandafter\expandafter
\endcsname
\csname utf8byte\the\numexpr128+#2-#1*64\endcsname}%
\makeatother
% xinttools loaded only for easying up loops in illustrative code
\usepackage{xinttools}
\makeatletter
\newcommand*{\WriteOutSixtyFourUTFchars}[2]{%
% #1 = starting Unicode code point
% #2 = out stream
\begingroup
\set@display@protect %%<<<---- makes \protect=\string
\def\mymacro##1{\UniToUTF{#1+##1}}%
\immediate\write#2{\xintApplyUnbraced\mymacro{\xintSeq{0}{63}}}%
\endgroup
}
% For \edef we need to take into account LaTeX's protection mechanism
% for the UTF-8
% leading bytes, as we defined them to be active for inputenc+utf8 readiness.
\begingroup
\global\let\protectedATedef\protected@edef
\endgroup
\makeatother
\begin{document}
\protectedATedef\x{\UniToUTF{"20AC}}
\typeout{\meaning\x}
\typeout{\x}
\typeout{\UniToUTF{"20AC}}
\typeout{}
\protectedATedef\x{\UniToUTF{"03B1}}
\typeout{\meaning\x}
\typeout{\x}
\typeout{\UniToUTF{"03B1}}
\typeout{}
\protectedATedef\x{\UniToUTF{"0416}}
\typeout{\meaning\x}
\typeout{\x}
\typeout{\UniToUTF{"0416}}
{\catcode127 12
\if\UniToUTF{127}\string^^?\else\ERROR\fi
}
\if\UniToUTF{126}\string~\else\ERROR\fi
{\makeatletter
\if\UniToUTF{37}\expandafter\@gobble\string\%\else\ERROR\fi}
\newwrite\foo
\immediate\openout\foo=\jobname ASCIIMEANINGS.txt
% we don't use \typeout here, but write to another file,
% else Emacs/AUCTeX wrongly
% reports a compilation error due to misinterpreation of log
% containing the ! character
\begingroup
\makeatletter
\set@display@protect %%<<<---- makes \protect=\string
\xintFor* #1 in{\xintSeq{0}{127}}\do
{\protectedATedef\x{\UniToUTF{#1}}%
\immediate\write\foo{\UniToUTF{#1} (\meaning\x)}}
\endgroup
\immediate\closeout\foo
\immediate\openout\foo=\jobname ASCII.txt
\WriteOutSixtyFourUTFchars{"0000}\foo
\WriteOutSixtyFourUTFchars{"0040}\foo
\immediate\closeout\foo
\immediate\openout\foo=\jobname0380.txt
\WriteOutSixtyFourUTFchars{"0380}\foo
\WriteOutSixtyFourUTFchars{"03C0}\foo
\immediate\closeout\foo
\immediate\openout\foo=\jobname0400.txt
\WriteOutSixtyFourUTFchars{"0400}\foo
\WriteOutSixtyFourUTFchars{"0440}\foo
\WriteOutSixtyFourUTFchars{"0480}\foo
\WriteOutSixtyFourUTFchars{"04C0}\foo
\WriteOutSixtyFourUTFchars{"0500}\foo
\immediate\closeout\foo
\immediate\openout\foo=\jobname2000.txt
\WriteOutSixtyFourUTFchars{"2000}\foo
\WriteOutSixtyFourUTFchars{"2040}\foo
\immediate\closeout\foo
\immediate\openout\foo=\jobname4000.txt
\WriteOutSixtyFourUTFchars{"4000}\foo
\WriteOutSixtyFourUTFchars{"4040}\foo
\immediate\closeout\foo
\immediate\openout\foo=\jobname10000.txt
\WriteOutSixtyFourUTFchars{"10000}\foo
\WriteOutSixtyFourUTFchars{"10040}\foo
\immediate\closeout\foo
\end{document}
To illustrate the functioning here is what one finds in log:
macro:->\protect €
€
€
macro:->\protect α
α
α
macro:->\protect Ж
Ж
Ж
then in file \jobname ASCII.txt
^^@^^A^^B^^C^^D^^E^^F^^G^^H
^^L^^M^^N^^O^^P^^Q^^R^^S^^T^^U^^V^^W^^X^^Y^^Z^^[^^\^^]^^^^^_ !"#$%&'()*+,-./0123456789:;<=>?
@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~^^?
(the CTRL-I
, CTRL-J
, CTRL-K
are genuine bytes)
then in file \jobname ASCIIMEANINGS.txt
(this is from default situation, one wonders why CTRL-K
which is vertical tabulation is handled as genuine byte... see remark below about pdflatex's -8bit
option ...)
^^@ (macro:->^^@)
^^A (macro:->^^A)
^^B (macro:->^^B)
^^C (macro:->^^C)
^^D (macro:->^^D)
^^E (macro:->^^E)
^^F (macro:->^^F)
^^G (macro:->^^G)
^^H (macro:->^^H)
(macro:-> )
(macro:->
)
(macro:->)
^^L (macro:->^^L)
^^M (macro:->^^M)
^^N (macro:->^^N)
^^O (macro:->^^O)
^^P (macro:->^^P)
^^Q (macro:->^^Q)
^^R (macro:->^^R)
^^S (macro:->^^S)
^^T (macro:->^^T)
^^U (macro:->^^U)
^^V (macro:->^^V)
^^W (macro:->^^W)
^^X (macro:->^^X)
^^Y (macro:->^^Y)
^^Z (macro:->^^Z)
^^[ (macro:->^^[)
^^\ (macro:->^^\)
^^] (macro:->^^])
^^^ (macro:->^^^)
^^_ (macro:->^^_)
(macro:-> )
! (macro:->!)
" (macro:->")
# (macro:->#)
$ (macro:->$)
% (macro:->%)
& (macro:->&)
' (macro:->')
( (macro:->()
) (macro:->))
* (macro:->*)
+ (macro:->+)
, (macro:->,)
- (macro:->-)
. (macro:->.)
/ (macro:->/)
0 (macro:->0)
1 (macro:->1)
2 (macro:->2)
3 (macro:->3)
4 (macro:->4)
5 (macro:->5)
6 (macro:->6)
7 (macro:->7)
8 (macro:->8)
9 (macro:->9)
: (macro:->:)
; (macro:->;)
< (macro:-><)
= (macro:->=)
> (macro:->>)
? (macro:->?)
@ (macro:->@)
A (macro:->A)
B (macro:->B)
C (macro:->C)
D (macro:->D)
E (macro:->E)
F (macro:->F)
G (macro:->G)
H (macro:->H)
I (macro:->I)
J (macro:->J)
K (macro:->K)
L (macro:->L)
M (macro:->M)
N (macro:->N)
O (macro:->O)
P (macro:->P)
Q (macro:->Q)
R (macro:->R)
S (macro:->S)
T (macro:->T)
U (macro:->U)
V (macro:->V)
W (macro:->W)
X (macro:->X)
Y (macro:->Y)
Z (macro:->Z)
[ (macro:->[)
\ (macro:->\)
] (macro:->])
^ (macro:->^)
_ (macro:->_)
` (macro:->`)
a (macro:->a)
b (macro:->b)
c (macro:->c)
d (macro:->d)
e (macro:->e)
f (macro:->f)
g (macro:->g)
h (macro:->h)
i (macro:->i)
j (macro:->j)
k (macro:->k)
l (macro:->l)
m (macro:->m)
n (macro:->n)
o (macro:->o)
p (macro:->p)
q (macro:->q)
r (macro:->r)
s (macro:->s)
t (macro:->t)
u (macro:->u)
v (macro:->v)
w (macro:->w)
x (macro:->x)
y (macro:->y)
z (macro:->z)
{ (macro:->{)
| (macro:->|)
} (macro:->})
~ (macro:->~)
^^? (macro:->^^?)
Important update: to get genuine bytes for the entire 7bit ascii range, use pdflatex -8bit
!
then in file \jobname 0380.txt
΄΅Ά·ΈΉΊΌΎΏΐΑΒΓΔΕΖΗΘΙΚΛΜΝΞΟΠΡΣΤΥΦΧΨΩΪΫάέήίΰαβγδεζηθικλμνξο
πρςστυφχψωϊϋόύώϏϐϑϒϓϔϕϖϗϘϙϚϛϜϝϞϟϠϡϢϣϤϥϦϧϨϩϪϫϬϭϮϯϰϱϲϳϴϵ϶ϷϸϹϺϻϼϽϾϿ
then in file \jobname 0400.txt
ЀЁЂЃЄЅІЇЈЉЊЋЌЍЎЏАБВГДЕЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯабвгдежзийклмноп
рстуфхцчшщъыьэюяѐёђѓєѕіїјљњћќѝўџѠѡѢѣѤѥѦѧѨѩѪѫѬѭѮѯѰѱѲѳѴѵѶѷѸѹѺѻѼѽѾѿ
Ҁҁ҂҃҄҅҆҇҈҉ҊҋҌҍҎҏҐґҒғҔҕҖҗҘҙҚқҜҝҞҟҠҡҢңҤҥҦҧҨҩҪҫҬҭҮүҰұҲҳҴҵҶҷҸҹҺһҼҽҾҿ
ӀӁӂӃӄӅӆӇӈӉӊӋӌӍӎӏӐӑӒӓӔӕӖӗӘәӚӛӜӝӞӟӠӡӢӣӤӥӦӧӨөӪӫӬӭӮӯӰӱӲӳӴӵӶӷӸӹӺӻӼӽӾӿ
ԀԁԂԃԄԅԆԇԈԉԊԋԌԍԎԏԐԑԒԓԔԕԖԗԘԙԚԛԜԝԞԟԠԡԢԣԤԥԦԧԨԩԪԫԬԭԮԯԱԲԳԴԵԶԷԸԹԺԻԼԽԾԿ
then in file \jobname 4000.txt
䀀䀁䀂䀃䀄䀅䀆䀇䀈䀉䀊䀋䀌䀍䀎䀏䀐䀑䀒䀓䀔䀕䀖䀗䀘䀙䀚䀛䀜䀝䀞䀟䀠䀡䀢䀣䀤䀥䀦䀧䀨䀩䀪䀫䀬䀭䀮䀯䀰䀱䀲䀳䀴䀵䀶䀷䀸䀹䀺䀻䀼䀽䀾䀿
䁀䁁䁂䁃䁄䁅䁆䁇䁈䁉䁊䁋䁌䁍䁎䁏䁐䁑䁒䁓䁔䁕䁖䁗䁘䁙䁚䁛䁜䁝䁞䁟䁠䁡䁢䁣䁤䁥䁦䁧䁨䁩䁪䁫䁬䁭䁮䁯䁰䁱䁲䁳䁴䁵䁶䁷䁸䁹䁺䁻䁼䁽䁾䁿
as examples. Rendering in your browser will depend on its font set-up.
Here is the \jobname 10000.txt
file as it shows in an Emacs buffer:
Update of an image to show how the ASCIIMEANINGS file renders in an Emacs buffer, proving the ^^K
is indeed a one-byte there, in the default usage of pdflatex
:
Important added note
This is the default situation, but using pdflatex -8bit
solves all our
anguishes, here is Emacs buffer screen shot of ASCII output file:
Thus the -8bit
option we can indeed create arbitrary UTF-8 encoded files from within pdflatex, using only Unicode code points as input.
inputenc
.\puthex
such that\puthex{e2}
is equivalent to typing^^e2
? (Or input could be in decimal like\puthex{226}
if that's easier.) I have everything else except this… maybe I should ask a separate question. :-)\uccode
and\uppercase
). So still not sure how to do it, or even whether it's possible. Now I think converting 20AC to the list [e2, 82, ac] is the easy part, how to input those tokens? (I don't understand how the current answer works either…)^^e2^^82^^ac
in the input stream but directly calls the inputenc/utf8 definition. So it is not like the lualatex version but unless there is some magic tric e.g. with \scantokens I have some doubts that one can get more in pdflatex.