8

I have a large scientific manuscript that I am trying to make screen-reader friendly. In many places, I have written quantities such as \qty{32.5(12)}{\micro\gram\per\milli\litre}

I am trying to "redefine" the siunitx macros so that when I use them, they automatically contain the alt text/actual text. I tried to take inspiration from this previous post: Can I redefine a command to contain itself?. Here is what I have attempted:

\documentclass{article} \usepackage[separate-uncertainty=true, mode=text]{siunitx} \usepackage{accsupp} \let\oldmilli\milli%copying the \milli command into \oldmilli \renewcommand{\milli}{%renewing the \milli command \BeginAccSupp{ActualText="milli"}%Defining how I want to screen reader to read this. \OriginalMilli%Go back to using the old \milli \EndAccSupp{}% } \begin{document} \qty{32.5(12)}{\milli\gram\per\gram} \end{document} 

I tried to experiment with \milli first and then proceed to other units, but already I run into this issue:

! LaTeX Error: Command \milli undefined. See the LaTeX manual or LaTeX Companion for explanation. Type H <return> for immediate help. 

Why does LaTeX say this is undefined when siunitx has already defined this for us?

I know I can simply rewrite this code as:

\documentclass{article} \usepackage[separate-uncertainty=true, mode=text]{siunitx} \usepackage{accsupp} \begin{document} \BeginAccSupp{ActualText="Thirty two point five plus minus one point two milli gram per gram"} \qty{32.5(12)}{\milli\gram\per\gram} \EndAccSupp{} \end{document} 

But doing this for the hundreds of quantities noted in the manuscript is simply impractical. Any suggestions? I'm not a power user, so please consider that when you give your suggestion.

P.S: I am using pdfTeX as that's what I have learnt and used for many years. Current version is:

  • pdfTeX 3.141592653-2.6-1.40.28 (TeX Live 2025)
  • Siunitx version is 2025-05-17 v3.4.11
  • accsupp package version is 2019/12/05 v0.6
1
  • well at first siunitx defines command locally, and at second \milli is not a unit but a prefix, so I fear it is not so easy ... Commented Oct 3 at 20:14

1 Answer 1

9

This is not a complete solution, but it may be enough to deal with many uses of \qty. It is then probably not too unmanageable to handle remaining cases by hand.

The following should be relatively straightforward to extend to other high-level siunitx commands.

The idea is to redefine \qty. Obviously, this will break if siunitx changes that definition, but it is a high-level command and fairly simple, so should be easily updated, if required.

We start by saving the original definition.

\NewCommandCopy \origqty \qty 

siunitx is the model package for expl3, so we switch the required syntax on.

\ExplSyntaxOn 

Needful variables.

\tl_new:N \l__miloop_actual_tl \prop_new:N \g_miloop_actuals_prop 

Our main function uses siunitx to format the number, then iterates through the units. For each token in the units, it checks if an actual text is defined. If so, it appends it to the formatted number. If not, it appends the token as-is.

\cs_new_protected_nopar:Npn \miloop_make_actual:nn #1#2 { \tl_clear:N \l__miloop_actual_tl \siunitx_number_format:nN {#1} \l_tmpa_tl \tl_put_right:Nn \l_tmpa_tl {~#2} \tl_map_inline:Nn \l_tmpa_tl { \prop_get:NnNTF \g_miloop_actuals_prop {##1} \l_tmpb_tl { \tl_put_right:NV \l__miloop_actual_tl \l_tmpb_tl }{ \tl_put_right:Nn \l__miloop_actual_tl {##1} } } } 

Now we redefine \qty to generate the actual text and enclose the typeset quantity between the appropriate accsupp macros.

\RenewDocumentCommand \qty { O { } m > { \TrimSpaces } m } { \mode_leave_vertical: \group_begin: \miloop_make_actual:nn {#2} {#3} \siunitx_unit_options_apply:n {#3} \keys_set:nn { siunitx } {#1} \exp_args:Ne \BeginAccSupp{ActualText={\l__miloop_actual_tl}} \siunitx_quantity:nn {#2} {#3} \EndAccSupp{} \group_end: } 

We need a function to easily add actual texts to our list. This is ultra-simple.

\cs_new_protected:Npn \miloop_gset_actuals:n #1 { \prop_gset_from_keyval:Nn \g_miloop_actuals_prop {#1} } 

We don't want to have to use expl3 in the document, so define a LaTeX2e alias.

\cs_new_eq:NN \setActualTexts \miloop_gset_actuals:n 

Switch out of expl3 syntax.

\ExplSyntaxOff 

We then set the actual texts for the MWE to illustrate usage.

\setActualTexts{% \gram = { gram }, \milli = { milli }, \per = { per }, \pm = { plus minus }, } 

The output in the PDF:

/Span<</ActualText(32.5\040plus\040minus\0401.2\040milli\040\040gram\040\040per\040\040gr am)>>BDC 

Complete code:

% \DocumentMetadata{uncompress} \documentclass{article} \usepackage[separate-uncertainty=true, mode=text]{siunitx} \usepackage{accsupp} \NewCommandCopy \origqty \qty \ExplSyntaxOn \tl_new:N \l__miloop_actual_tl \prop_new:N \g_miloop_actuals_prop \cs_generate_variant:Nn \siunitx_number_output:n {V} \cs_new_protected_nopar:Npn \miloop_make_actual:nn #1#2 { \tl_clear:N \l__miloop_actual_tl \siunitx_number_format:nN {#1} \l_tmpa_tl \tl_put_right:Nn \l_tmpa_tl {~#2} \tl_map_inline:Nn \l_tmpa_tl { \prop_get:NnNTF \g_miloop_actuals_prop {##1} \l_tmpb_tl { \tl_put_right:NV \l__miloop_actual_tl \l_tmpb_tl }{ \tl_put_right:Nn \l__miloop_actual_tl {##1} } } } \RenewDocumentCommand \qty { O { } m > { \TrimSpaces } m } { \mode_leave_vertical: \group_begin: \miloop_make_actual:nn {#2} {#3} \siunitx_unit_options_apply:n {#3} \keys_set:nn { siunitx } {#1} \exp_args:Ne \BeginAccSupp{ActualText={\l__miloop_actual_tl}} \siunitx_quantity:nn {#2} {#3} \EndAccSupp{} \group_end: } \cs_new_protected:Npn \miloop_gset_actuals:n #1 { \prop_gset_from_keyval:Nn \g_miloop_actuals_prop {#1} } \cs_new_eq:NN \setActualTexts \miloop_gset_actuals:n \ExplSyntaxOff \setActualTexts{% \gram = { gram }, \milli = { milli }, \per = { per }, \pm = { plus minus }, } \begin{document} \origqty{32.5(12)}{\milli\gram\per\gram} \qty{32.5(12)}{\milli\gram\per\gram} \end{document} 
11
  • 1
    Thank you so much! Just so you know, your solution even works for declared qualifiers. This year, I have three students who need screen readers and I am so glad this can be deployed. Commented Oct 4 at 10:37
  • 5
    @Miloop with students with screen readers you should produce properly tagged pdf. Without it the actualtext you are inserting here is simply ignored when the text is read. That means use lualatex and add \DocumentMetadata{tagging=on}. Commented Oct 4 at 11:05
  • 1
    @cfr the quotes symbols around the ActualText are unneeded. And you should remove the spaces in {~##1~}. This separates all numbers and you get 3 2 . 5 plus minus 1 . 2 and the reading is very odd. Commented Oct 4 at 11:07
  • 1
    @UlrikeFischer ah, thanks & edited. Commented Oct 4 at 11:50
  • 1
    Shouldn't it be \prop_gset_from_keyval:Nn \g_miloop_actuals_prop {#1}? Commented Oct 5 at 17:48

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.