10

In this example, the bit representation of BIOS font from '@' to 'G' is in two forms. The first is a sequence of 1s and 0s separated by commas. The second is a string of 1s and 0s crammed into tokens. Truth be told, I want to have a single macro which generates a tikzpicture from the second form. This is as far as I got, and I need help to finish it.

Running 'pdflatex BIOS.tex' with this as its source does the right thing with the first form and fails to output the second form. The code is actually broken, but the goal is to have indices (\xi,\yi) and a \chr to place a filled circle for each \chr in each \row.

Please help me finish this. I don't know what to try and no search result worked.

\documentclass[12pt]{article}

\usepackage{tikz}

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% TODO make this work by enumerating each character with \xi
\makeatletter
\def\dowithchar#1#2{%
    \begingroup%
    \def\myspace{}% defined with local scope
    \@tfor\chr:=#1\do{%
        \myspace\ifx\chr{X} \fill (0.18*\xi,-0.18*#2) circle (0.08) \fi;%
        \let\myspace\space%
    }%
    \endgroup%
}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

\makeatother

\listfiles
\begin{document}

\newcommand\ASCIIbits{
    {0,1,1,1,1,1,0,0,0,0,0,1,1,0,0,0,0,0,1,1,1,1,1,1,0,0,0,0,0,1,1,1,1,0,0,%
     0,1,1,1,1,1,0,0,0,0,1,1,1,1,1,1,1,0,0,1,1,1,1,1,1,1,0,0,0,0,1,1,1,1,0},%
    {1,1,0,0,0,1,1,0,0,0,1,1,1,1,0,0,0,0,0,1,1,0,0,1,1,0,0,0,1,1,0,0,1,1,0,%
     0,0,1,1,0,1,1,0,0,0,0,1,1,0,0,0,1,0,0,0,1,1,0,0,0,1,0,0,0,1,1,0,0,1,1},%
    {1,1,0,1,1,1,1,0,0,1,1,0,0,1,1,0,0,0,0,1,1,0,0,1,1,0,0,1,1,0,0,0,0,0,0,%
     0,0,1,1,0,0,1,1,0,0,0,1,1,0,1,0,0,0,0,0,1,1,0,1,0,0,0,0,1,1,0,0,0,0,0},%
    {1,1,0,1,1,1,1,0,0,1,1,0,0,1,1,0,0,0,0,1,1,1,1,1,0,0,0,1,1,0,0,0,0,0,0,%
     0,0,1,1,0,0,1,1,0,0,0,1,1,1,1,0,0,0,0,0,1,1,1,1,0,0,0,0,1,1,0,0,0,0,0},%
    {1,1,0,1,1,1,0,0,0,1,1,1,1,1,1,0,0,0,0,1,1,0,0,1,1,0,0,1,1,0,0,0,0,0,0,%
     0,0,1,1,0,0,1,1,0,0,0,1,1,0,1,0,0,0,0,0,1,1,0,1,0,0,0,0,1,1,0,0,1,1,1},%
    {1,1,0,0,0,0,0,0,0,1,1,0,0,1,1,0,0,0,0,1,1,0,0,1,1,0,0,0,1,1,0,0,1,1,0,%
     0,0,1,1,0,1,1,0,0,0,0,1,1,0,0,0,1,0,0,0,1,1,0,0,0,0,0,0,0,1,1,0,0,1,1},%
    {0,1,1,1,1,1,0,0,0,1,1,0,0,1,1,0,0,0,1,1,1,1,1,1,0,0,0,0,0,1,1,1,1,0,0,%
     0,1,1,1,1,1,0,0,0,0,1,1,1,1,1,1,1,0,0,1,1,1,1,0,0,0,0,0,0,0,1,1,1,0,1},%
    {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,%
     0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}%
}
\newcommand\ASCIIfont{
    OXXXXXOOOOOXXOOOOOXXXXXXOOOOOXXXXOOOXXXXXOOOOXXXXXXXOOXXXXXXXOOOOXXXXO,%
    XXOOOXXOOOXXXXOOOOOXXOOXXOOOXXOOXXOOOXXOXXOOOOXXOOOXOOOXXOOOXOOOXXOOXX,%
    XXOXXXXOOXXOOXXOOOOXXOOXXOOXXOOOOOOOOXXOOXXOOOXXOXOOOOOXXOXOOOOXXOOOOO,%
    XXOXXXXOOXXOOXXOOOOXXXXXOOOXXOOOOOOOOXXOOXXOOOXXXXOOOOOXXXXOOOOXXOOOOO,%
    XXOXXXOOOXXXXXXOOOOXXOOXXOOXXOOOOOOOOXXOOXXOOOXXOXOOOOOXXOXOOOOXXOOXXX,%
    XXOOOOOOOXXOOXXOOOOXXOOXXOOOXXOOXXOOOXXOXXOOOOXXOOOXOOOXXOOOOOOOXXOOXX,%
    OXXXXXOOOXXOOXXOOOXXXXXXOOOOOXXXXOOOXXXXXOOOOXXXXXXXOOXXXXOOOOOOOXXXOX,%
    OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO%
}

\begin{tikzpicture}
    \foreach\row [count=\yi] in \ASCIIbits{
        \foreach\chr [count=\xi] in \row {
            \ifodd\chr \fill (0.18*\xi,-0.18*\yi) circle (0.08) \fi;
        }
    }
\end{tikzpicture}

\begin{tikzpicture}
    \foreach\row [count=\yi] in \ASCIIfont{
        \dowithchar\row\yi
    }
\end{tikzpicture}

\end{document}
7

TikZ has the parser module for that purpose. It allows you to parse a string of characters and do something with them.

\documentclass[12pt]{article}

\usepackage{tikz}
\usepgfmodule{parser}
\newcounter{pft}
\pgfparserdef{ASCIIswitch}{initial}{the letter X}% 
{\stepcounter{pft}%
\fill (0.18*\number\value{pft},0) circle [radius=0.08];}%
\pgfparserdef{ASCIIswitch}{initial}{the letter O}% 
{\stepcounter{pft}}%
\pgfparserdef{ASCIIswitch}{initial}{the character ;}% 
{\pgfparserswitch{final}}%

\listfiles
\begin{document}

\newcommand\ASCIIbits{
    {0,1,1,1,1,1,0,0,0,0,0,1,1,0,0,0,0,0,1,1,1,1,1,1,0,0,0,0,0,1,1,1,1,0,0,%
     0,1,1,1,1,1,0,0,0,0,1,1,1,1,1,1,1,0,0,1,1,1,1,1,1,1,0,0,0,0,1,1,1,1,0},%
    {1,1,0,0,0,1,1,0,0,0,1,1,1,1,0,0,0,0,0,1,1,0,0,1,1,0,0,0,1,1,0,0,1,1,0,%
     0,0,1,1,0,1,1,0,0,0,0,1,1,0,0,0,1,0,0,0,1,1,0,0,0,1,0,0,0,1,1,0,0,1,1},%
    {1,1,0,1,1,1,1,0,0,1,1,0,0,1,1,0,0,0,0,1,1,0,0,1,1,0,0,1,1,0,0,0,0,0,0,%
     0,0,1,1,0,0,1,1,0,0,0,1,1,0,1,0,0,0,0,0,1,1,0,1,0,0,0,0,1,1,0,0,0,0,0},%
    {1,1,0,1,1,1,1,0,0,1,1,0,0,1,1,0,0,0,0,1,1,1,1,1,0,0,0,1,1,0,0,0,0,0,0,%
     0,0,1,1,0,0,1,1,0,0,0,1,1,1,1,0,0,0,0,0,1,1,1,1,0,0,0,0,1,1,0,0,0,0,0},%
    {1,1,0,1,1,1,0,0,0,1,1,1,1,1,1,0,0,0,0,1,1,0,0,1,1,0,0,1,1,0,0,0,0,0,0,%
     0,0,1,1,0,0,1,1,0,0,0,1,1,0,1,0,0,0,0,0,1,1,0,1,0,0,0,0,1,1,0,0,1,1,1},%
    {1,1,0,0,0,0,0,0,0,1,1,0,0,1,1,0,0,0,0,1,1,0,0,1,1,0,0,0,1,1,0,0,1,1,0,%
     0,0,1,1,0,1,1,0,0,0,0,1,1,0,0,0,1,0,0,0,1,1,0,0,0,0,0,0,0,1,1,0,0,1,1},%
    {0,1,1,1,1,1,0,0,0,1,1,0,0,1,1,0,0,0,1,1,1,1,1,1,0,0,0,0,0,1,1,1,1,0,0,%
     0,1,1,1,1,1,0,0,0,0,1,1,1,1,1,1,1,0,0,1,1,1,1,0,0,0,0,0,0,0,1,1,1,0,1},%
    {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,%
     0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}%
}
\newcommand\ASCIIfont{
    OXXXXXOOOOOXXOOOOOXXXXXXOOOOOXXXXOOOXXXXXOOOOXXXXXXXOOXXXXXXXOOOOXXXXO,%
    XXOOOXXOOOXXXXOOOOOXXOOXXOOOXXOOXXOOOXXOXXOOOOXXOOOXOOOXXOOOXOOOXXOOXX,%
    XXOXXXXOOXXOOXXOOOOXXOOXXOOXXOOOOOOOOXXOOXXOOOXXOXOOOOOXXOXOOOOXXOOOOO,%
    XXOXXXXOOXXOOXXOOOOXXXXXOOOXXOOOOOOOOXXOOXXOOOXXXXOOOOOXXXXOOOOXXOOOOO,%
    XXOXXXOOOXXXXXXOOOOXXOOXXOOXXOOOOOOOOXXOOXXOOOXXOXOOOOOXXOXOOOOXXOOXXX,%
    XXOOOOOOOXXOOXXOOOOXXOOXXOOOXXOOXXOOOXXOXXOOOOXXOOOXOOOXXOOOOOOOXXOOXX,%
    OXXXXXOOOXXOOXXOOOXXXXXXOOOOOXXXXOOOXXXXXOOOOXXXXXXXOOXXXXOOOOOOOXXXOX,%
    OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO%
}

Comma-separated list:

\begin{tikzpicture}
    \foreach\row [count=\yi] in \ASCIIbits{
        \foreach\chr [count=\xi] in \row {
            \ifodd\chr \fill (0.18*\xi,-0.18*\yi) circle [radius=0.08] \fi;
        }
    }
\end{tikzpicture}

\bigskip

ASCII strings parsed by parser module:

\begin{tikzpicture}
    \foreach\row [count=\yi] in \ASCIIfont{
       \begin{scope}[yshift=-0.18*\yi*1cm]
        \setcounter{pft}{0}
        \edef\temp{\noexpand\pgfparserparse{ASCIIswitch}\row;}
        \temp
       \end{scope}
    }
\end{tikzpicture}

\end{document}

enter image description here

ADDENDUM: As for your question in the comments whether you can split the string into a comma-separated list of atoms: of course, you almost had the code yourself.

\documentclass[12pt]{article}

\usepackage{tikz}

\makeatletter
\def\splitstring#1#2->#3;{%
      \edef#3{#1}\@tfor\chr:=#2\do{%
        \edef#3{#3,\chr}%
      }%
}
\makeatother
\begin{document}
\newcommand\ASCIIfont{
    OXXXXXOOOOOXXOOOOOXXXXXXOOOOOXXXXOOOXXXXXOOOOXXXXXXXOOXXXXXXXOOOOXXXXO,%
    XXOOOXXOOOXXXXOOOOOXXOOXXOOOXXOOXXOOOXXOXXOOOOXXOOOXOOOXXOOOXOOOXXOOXX,%
    XXOXXXXOOXXOOXXOOOOXXOOXXOOXXOOOOOOOOXXOOXXOOOXXOXOOOOOXXOXOOOOXXOOOOO,%
    XXOXXXXOOXXOOXXOOOOXXXXXOOOXXOOOOOOOOXXOOXXOOOXXXXOOOOOXXXXOOOOXXOOOOO,%
    XXOXXXOOOXXXXXXOOOOXXOOXXOOXXOOOOOOOOXXOOXXOOOXXOXOOOOOXXOXOOOOXXOOXXX,%
    XXOOOOOOOXXOOXXOOOOXXOOXXOOOXXOOXXOOOXXOXXOOOOXXOOOXOOOXXOOOOOOOXXOOXX,%
    OXXXXXOOOXXOOXXOOOXXXXXXOOOOOXXXXOOOXXXXXOOOOXXXXXXXOOXXXXOOOOOOOXXXOX,%
    OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO%
}

\begin{tikzpicture}
    \foreach\row [count=\yi] in \ASCIIfont{
        \expandafter\splitstring \row->\myrow;
        \foreach\chr [count=\xi] in \myrow {\def\myX{X}
            \ifx\chr\myX \fill (0.18*\xi,-0.18*\yi) circle[radius=0.08] \fi;
        }
    }
\end{tikzpicture}
\end{document}
  • Wow! It looks like you nailed it, but I must learn an entirely unfamiliar part of latex to understand what you did. This is wonderful, and thank you. A follow-on question is "why does latex make this so hard?" I could do this quickly, simply, and easily in any of a dozen different programming languages. Perhaps I shall write a preprocessor to generate latex so that these kinds of operations are more easily done. Perhaps a Python library which can be used by authors to write source generators. Kind of like a mediawiki syntax reader that outputs latex. Nevertheless... THANK YOU! – jlettvin Feb 25 at 15:06
  • 1
    @jlettvin I think that the answer why LaTeX makes it so hard is that it is really a typesetting system and rather old. Given this it is actually very versatile. Python and company are much newer, and, more importantly, designed to tackle such tasks. (BTW, circle (0.08) is deprecated by now.) – Schrödinger's cat Feb 25 at 15:12
  • I appreciate the solutions, and I am trying them out. What I had hoped for, and searched for days to find, was a macro that would split a string into characters suitable for use with \foreach\chr [count=\xi] in \split{\row}{do something}. I became despondent over the days of search. Is there any chance of showing how that might be done? – jlettvin Feb 25 at 15:21
  • @jlettvin Sure. I added that to the answer as an addendum. – Schrödinger's cat Feb 25 at 15:39
  • @jlettvin Another fundamental reason while “simple programming tasks” often appear so difficult to do in TeX to experienced non-TeX programmers, is that TeX is a macro language. Tokens get replaced by other tokens, which in turn get replaced by others etc. until we have an unexpandable token. Then, depending on that token, TeX does this or that. Most programming languages don't work this way at all. In all macro languages I've seen (e.g., m4), doing “moderately complex things” appears very difficult when you are not well versed in the system. One really needs a lot of practice to... – frougon Feb 25 at 16:01
6

You can use expl3: no need for fancy indexing, which is provided out of the box with “indexed maps”.

\documentclass[12pt]{article}

\usepackage{tikz}
\usepackage{xparse}

\newcommand{\makedot}[2]{%
  \fill (0.18*#1,-0.18*#2) circle (0.08);
}

\ExplSyntaxOn
\NewDocumentCommand{\ASCIIfont}{O{}m}
 {
  \begin{tikzpicture}[#1]
  \jlettvin_asciifont:n { #2 }
  \end{tikzpicture}
 }

\seq_new:N \l__jlettvin_asciifont_rows_seq
\seq_new:N \l__jlettvin_asciifont_row_seq

\cs_new_protected:Nn \jlettvin_asciifont:n
 {
  \seq_set_from_clist:Nn \l__jlettvin_asciifont_rows_seq { #1 }
  \seq_indexed_map_function:NN \l__jlettvin_asciifont_rows_seq \__jlettvin_asciifont_row:nn
 }

\cs_new_protected:Nn \__jlettvin_asciifont_row:nn
 {% #1 = row index, #2 = row
  \seq_set_split:Nnn \l__jlettvin_asciifont_row_seq { } { #2 }
  \seq_indexed_map_inline:Nn \l__jlettvin_asciifont_row_seq
   {% ##1 = column index, ##2 = item
    \str_if_eq:nnT { ##2 } { X }
     {
      \makedot{##1}{#1}
     }
   }
 }
\ExplSyntaxOff

\begin{document}

\ASCIIfont{
    OXXXXXOOOOOXXOOOOOXXXXXXOOOOOXXXXOOOXXXXXOOOOXXXXXXXOOXXXXXXXOOOOXXXXO,
    XXOOOXXOOOXXXXOOOOOXXOOXXOOOXXOOXXOOOXXOXXOOOOXXOOOXOOOXXOOOXOOOXXOOXX,
    XXOXXXXOOXXOOXXOOOOXXOOXXOOXXOOOOOOOOXXOOXXOOOXXOXOOOOOXXOXOOOOXXOOOOO,
    XXOXXXXOOXXOOXXOOOOXXXXXOOOXXOOOOOOOOXXOOXXOOOXXXXOOOOOXXXXOOOOXXOOOOO,
    XXOXXXOOOXXXXXXOOOOXXOOXXOOXXOOOOOOOOXXOOXXOOOXXOXOOOOOXXOXOOOOXXOOXXX,
    XXOOOOOOOXXOOXXOOOOXXOOXXOOOXXOOXXOOOXXOXXOOOOXXOOOXOOOXXOOOOOOOXXOOXX,
    OXXXXXOOOXXOOXXOOOXXXXXXOOOOOXXXXOOOXXXXXOOOOXXXXXXXOOXXXXOOOOOOOXXXOX,
    OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO
}

\end{document}

The input is split at commas and each row is processed taking into account its number (y-coordinate); the row is again split into items (at nothing), and the same is done taking into account the item number (x-coordinate).

The command \ASCIIfont has an optional argument for specifying options to the tikzpicture.

The trailing row of zeros doesn't produce vertical space, but that's a problem also with the other implementation.

enter image description here

  • I have seen much expl3 in my search, but I find the syntax and sheer volume of code intimidating. I want to learn how to read and code like this. Can you recommend an online tutorial? – jlettvin Feb 25 at 15:31
  • @jlettvin I've found very clarifying texdoc expl3 (a dozen pages introduction) and then texdoc interface3 (which is a sort of reference) – Matteo Gamboz Feb 26 at 8:32
5

Here are two ways to fix your code (quite different from the one given by Schrödinger's cat).

Probably, your most important “mistake” is that you needed to expand the \row macro before feeding the result to \dowithchar, in the second picture. Also, I don't see the purpose of your \myspace macro, so I removed it.

The second problem is that you needed \if rather than \ifx (well, \ifx could be used, but not this way). What happens with your \ifx test is that it compares the \chr macro with an opening brace { of category code 1 (beginning of group): the comparison is always false; that is why the second picture was invisible. \ifx compares the next two tokens, it never expands them. On the other hand, \if expands what follows until it has two unexpandable tokens; then, it compares their character codes (this is a bit simplified: there are special rules for unexpandable control sequence tokens and active characters—see the TeXbook p. 209 or specialized questions).

With \pgfmathtruncatemacro

Here, inside \dowithchar, \xi is a macro, just as in the first picture.

\documentclass{article}
\usepackage{tikz}

\makeatletter
\def\dowithchar#1#2{%
  \begingroup
    \def\xi{1}
      \@tfor\chr:=#1\do{%
        \if X\chr \fill (0.18*\xi,-0.18*#2) circle (0.08); \fi
        \pgfmathtruncatemacro{\xi}{\xi+1}
      }%
  \endgroup
}
\makeatother

\begin{document}

\newcommand\ASCIIbits{
    {0,1,1,1,1,1,0,0,0,0,0,1,1,0,0,0,0,0,1,1,1,1,1,1,0,0,0,0,0,1,1,1,1,0,0,%
     0,1,1,1,1,1,0,0,0,0,1,1,1,1,1,1,1,0,0,1,1,1,1,1,1,1,0,0,0,0,1,1,1,1,0},%
    {1,1,0,0,0,1,1,0,0,0,1,1,1,1,0,0,0,0,0,1,1,0,0,1,1,0,0,0,1,1,0,0,1,1,0,%
     0,0,1,1,0,1,1,0,0,0,0,1,1,0,0,0,1,0,0,0,1,1,0,0,0,1,0,0,0,1,1,0,0,1,1},%
    {1,1,0,1,1,1,1,0,0,1,1,0,0,1,1,0,0,0,0,1,1,0,0,1,1,0,0,1,1,0,0,0,0,0,0,%
     0,0,1,1,0,0,1,1,0,0,0,1,1,0,1,0,0,0,0,0,1,1,0,1,0,0,0,0,1,1,0,0,0,0,0},%
    {1,1,0,1,1,1,1,0,0,1,1,0,0,1,1,0,0,0,0,1,1,1,1,1,0,0,0,1,1,0,0,0,0,0,0,%
     0,0,1,1,0,0,1,1,0,0,0,1,1,1,1,0,0,0,0,0,1,1,1,1,0,0,0,0,1,1,0,0,0,0,0},%
    {1,1,0,1,1,1,0,0,0,1,1,1,1,1,1,0,0,0,0,1,1,0,0,1,1,0,0,1,1,0,0,0,0,0,0,%
     0,0,1,1,0,0,1,1,0,0,0,1,1,0,1,0,0,0,0,0,1,1,0,1,0,0,0,0,1,1,0,0,1,1,1},%
    {1,1,0,0,0,0,0,0,0,1,1,0,0,1,1,0,0,0,0,1,1,0,0,1,1,0,0,0,1,1,0,0,1,1,0,%
     0,0,1,1,0,1,1,0,0,0,0,1,1,0,0,0,1,0,0,0,1,1,0,0,0,0,0,0,0,1,1,0,0,1,1},%
    {0,1,1,1,1,1,0,0,0,1,1,0,0,1,1,0,0,0,1,1,1,1,1,1,0,0,0,0,0,1,1,1,1,0,0,%
     0,1,1,1,1,1,0,0,0,0,1,1,1,1,1,1,1,0,0,1,1,1,1,0,0,0,0,0,0,0,1,1,1,0,1},%
    {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,%
     0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}%
}
\newcommand\ASCIIfont{
    OXXXXXOOOOOXXOOOOOXXXXXXOOOOOXXXXOOOXXXXXOOOOXXXXXXXOOXXXXXXXOOOOXXXXO,%
    XXOOOXXOOOXXXXOOOOOXXOOXXOOOXXOOXXOOOXXOXXOOOOXXOOOXOOOXXOOOXOOOXXOOXX,%
    XXOXXXXOOXXOOXXOOOOXXOOXXOOXXOOOOOOOOXXOOXXOOOXXOXOOOOOXXOXOOOOXXOOOOO,%
    XXOXXXXOOXXOOXXOOOOXXXXXOOOXXOOOOOOOOXXOOXXOOOXXXXOOOOOXXXXOOOOXXOOOOO,%
    XXOXXXOOOXXXXXXOOOOXXOOXXOOXXOOOOOOOOXXOOXXOOOXXOXOOOOOXXOXOOOOXXOOXXX,%
    XXOOOOOOOXXOOXXOOOOXXOOXXOOOXXOOXXOOOXXOXXOOOOXXOOOXOOOXXOOOOOOOXXOOXX,%
    OXXXXXOOOXXOOXXOOOXXXXXXOOOOOXXXXOOOXXXXXOOOOXXXXXXXOOXXXXOOOOOOOXXXOX,%
    OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO%
}

\begin{tikzpicture}
    \foreach\row [count=\yi] in \ASCIIbits{
        \foreach\chr [count=\xi] in \row {
            \ifodd\chr \fill (0.18*\xi,-0.18*\yi) circle (0.08) \fi;
        }
    }
\end{tikzpicture}

\bigskip
\begin{tikzpicture}
    \foreach\row [count=\yi] in \ASCIIfont{
        \expandafter\dowithchar\expandafter{\row}\yi
    }
\end{tikzpicture}

\end{document}

With a TeX \count register

Here, we use a TeX \count register via a \countdef token to count the columns in \ASCIIfont (second picture). The \countdef token is \myXi. We could reuse the same name \xi, but I prefer using a different name to avoid confusion since in the first picture, \xi is a macro (a \countdef token is unexpandable; in particular, it is not a macro).

\documentclass{article}
\usepackage{tikz}

\newcount\myXi

\makeatletter
\def\dowithchar#1#2{%
  \myXi=1                     % <--- don't remove the space
  \@tfor\chr:=#1\do{%
    \if X\chr \fill (0.18*\myXi,-0.18*#2) circle (0.08); \fi
    \advance \myXi by 1       % <--- don't remove the space
  }%
}
\makeatother

\begin{document}

\newcommand\ASCIIbits{
    {0,1,1,1,1,1,0,0,0,0,0,1,1,0,0,0,0,0,1,1,1,1,1,1,0,0,0,0,0,1,1,1,1,0,0,%
     0,1,1,1,1,1,0,0,0,0,1,1,1,1,1,1,1,0,0,1,1,1,1,1,1,1,0,0,0,0,1,1,1,1,0},%
    {1,1,0,0,0,1,1,0,0,0,1,1,1,1,0,0,0,0,0,1,1,0,0,1,1,0,0,0,1,1,0,0,1,1,0,%
     0,0,1,1,0,1,1,0,0,0,0,1,1,0,0,0,1,0,0,0,1,1,0,0,0,1,0,0,0,1,1,0,0,1,1},%
    {1,1,0,1,1,1,1,0,0,1,1,0,0,1,1,0,0,0,0,1,1,0,0,1,1,0,0,1,1,0,0,0,0,0,0,%
     0,0,1,1,0,0,1,1,0,0,0,1,1,0,1,0,0,0,0,0,1,1,0,1,0,0,0,0,1,1,0,0,0,0,0},%
    {1,1,0,1,1,1,1,0,0,1,1,0,0,1,1,0,0,0,0,1,1,1,1,1,0,0,0,1,1,0,0,0,0,0,0,%
     0,0,1,1,0,0,1,1,0,0,0,1,1,1,1,0,0,0,0,0,1,1,1,1,0,0,0,0,1,1,0,0,0,0,0},%
    {1,1,0,1,1,1,0,0,0,1,1,1,1,1,1,0,0,0,0,1,1,0,0,1,1,0,0,1,1,0,0,0,0,0,0,%
     0,0,1,1,0,0,1,1,0,0,0,1,1,0,1,0,0,0,0,0,1,1,0,1,0,0,0,0,1,1,0,0,1,1,1},%
    {1,1,0,0,0,0,0,0,0,1,1,0,0,1,1,0,0,0,0,1,1,0,0,1,1,0,0,0,1,1,0,0,1,1,0,%
     0,0,1,1,0,1,1,0,0,0,0,1,1,0,0,0,1,0,0,0,1,1,0,0,0,0,0,0,0,1,1,0,0,1,1},%
    {0,1,1,1,1,1,0,0,0,1,1,0,0,1,1,0,0,0,1,1,1,1,1,1,0,0,0,0,0,1,1,1,1,0,0,%
     0,1,1,1,1,1,0,0,0,0,1,1,1,1,1,1,1,0,0,1,1,1,1,0,0,0,0,0,0,0,1,1,1,0,1},%
    {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,%
     0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}%
}
\newcommand\ASCIIfont{
    OXXXXXOOOOOXXOOOOOXXXXXXOOOOOXXXXOOOXXXXXOOOOXXXXXXXOOXXXXXXXOOOOXXXXO,%
    XXOOOXXOOOXXXXOOOOOXXOOXXOOOXXOOXXOOOXXOXXOOOOXXOOOXOOOXXOOOXOOOXXOOXX,%
    XXOXXXXOOXXOOXXOOOOXXOOXXOOXXOOOOOOOOXXOOXXOOOXXOXOOOOOXXOXOOOOXXOOOOO,%
    XXOXXXXOOXXOOXXOOOOXXXXXOOOXXOOOOOOOOXXOOXXOOOXXXXOOOOOXXXXOOOOXXOOOOO,%
    XXOXXXOOOXXXXXXOOOOXXOOXXOOXXOOOOOOOOXXOOXXOOOXXOXOOOOOXXOXOOOOXXOOXXX,%
    XXOOOOOOOXXOOXXOOOOXXOOXXOOOXXOOXXOOOXXOXXOOOOXXOOOXOOOXXOOOOOOOXXOOXX,%
    OXXXXXOOOXXOOXXOOOXXXXXXOOOOOXXXXOOOXXXXXOOOOXXXXXXXOOXXXXOOOOOOOXXXOX,%
    OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO%
}

\begin{tikzpicture}
    \foreach\row [count=\yi] in \ASCIIbits{
        \foreach\chr [count=\xi] in \row {
            \ifodd\chr \fill (0.18*\xi,-0.18*\yi) circle (0.08) \fi;
        }
    }
\end{tikzpicture}

\bigskip
\begin{tikzpicture}
    \foreach\row [count=\yi] in \ASCIIfont{
        \expandafter\dowithchar\expandafter{\row}\yi
    }
\end{tikzpicture}

\end{document}

Output

The output is the same in both cases:

enter image description here

  • I learned a lot reading your response and code. I have only one question... What is the reasoning for "do not remove space?" – jlettvin Feb 25 at 15:33
  • Because this is in places where TeX reads a <number> (with explicitly-written low-level TeX commands: counter assignment and counter \advance). The grammar for <number> is peculiar (see TeXbook p. 269). One very important aspect is that if TeX has only seen digits so far while it is reading a <number>, it will continue looking for the rest and expand tokens as it goes. A space token terminates this search and is automatically removed (it is part of the syntax of a <number>). ... – frougon Feb 25 at 15:39
  • This way, you can be sure TeX won't expand further tokens from the input stream for the <number> being read, possibly taking unwanted digits as part of it. This is explained here and certainly in many other places. :-) – frougon Feb 25 at 15:44
  • Regarding my first comment: TeXnically, \advance \myXi by 1 is also a “counter assignment.” In partic., it is not an operation that can be performed while \edef is expanding its braced argument, and it can't be triggered by \expandafter either. In short, it can't be done in expansion-only contexts (for completeness, LuaTeX can perform assignments in expansion-only contexts when \directlua is used, but forget about this for now). – frougon Feb 25 at 16:16
  • I was so impressed with the responses you gave me that I will ask for one slight improvement which, also, eludes me. I want to take two characters at a time instead of 1. I've been searching but failed to find an example, or sufficient documentation. The salient line is this one... \@tfor\chr:=#1\do{% – jlettvin Feb 25 at 21:53
4

For the sake of having fun you can implement your own \romannumeral0-expansion-based tail-recursive-loop-macro for interspersing the tokens that form the expansion of \row with commas. (If you do this, a bit of expansion-control is needed to get all tokens into correct order, but that might be a nice and relaxing exercise.) Then you can use the comma-list based \foreach..[count=...]...-loop instead of the non-delimited-argument-based \@tfor-loop.

\documentclass[12pt]{article}

\usepackage{tikz}

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\makeatletter
\newcommand\insertcommas[3]{%
  % #1 tokens to put in front of brace-nested comma-list
  % #2 comma-list accumulated so far
  % #3 either the appended \insertcommas or an element from non-comma-list
  \romannumeral0%
  \ifx\insertcommas#3\expandafter\@secondoftwo\else\expandafter\@firstoftwo\fi
  {\expandafter\@gobbletwo\insertcommas{#1}{#2#3,}}{ #1{#2}}%
}%
\newcommand\exchange[2]{#2#1}%
\newcommand\dowithchar[2]{%
    % #1 = the token \row needs to be expanded before \insertcommas can be applied.
    \expandafter\exchange\expandafter{#1}{\insertcommas{\foreach\chr [count=\xi] in }{}}\insertcommas{%
       % You have nullfont inside tikzpicture-environment, so \space has no effect inside
       % tikzpicture-environment, so it is not clear to me why you want it, but here
       % you get it:
       \ifnum\xi>1 \space\fi
       %%%%%%%%
       \expandafter\ifx\expandafter X\chr\fill(0.18*\xi,-0.18*#2) circle (0.08) \fi;%
    }%
}%
\makeatother
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

\listfiles
\begin{document}

\newcommand\ASCIIbits{
    {0,1,1,1,1,1,0,0,0,0,0,1,1,0,0,0,0,0,1,1,1,1,1,1,0,0,0,0,0,1,1,1,1,0,0,%
     0,1,1,1,1,1,0,0,0,0,1,1,1,1,1,1,1,0,0,1,1,1,1,1,1,1,0,0,0,0,1,1,1,1,0},%
    {1,1,0,0,0,1,1,0,0,0,1,1,1,1,0,0,0,0,0,1,1,0,0,1,1,0,0,0,1,1,0,0,1,1,0,%
     0,0,1,1,0,1,1,0,0,0,0,1,1,0,0,0,1,0,0,0,1,1,0,0,0,1,0,0,0,1,1,0,0,1,1},%
    {1,1,0,1,1,1,1,0,0,1,1,0,0,1,1,0,0,0,0,1,1,0,0,1,1,0,0,1,1,0,0,0,0,0,0,%
     0,0,1,1,0,0,1,1,0,0,0,1,1,0,1,0,0,0,0,0,1,1,0,1,0,0,0,0,1,1,0,0,0,0,0},%
    {1,1,0,1,1,1,1,0,0,1,1,0,0,1,1,0,0,0,0,1,1,1,1,1,0,0,0,1,1,0,0,0,0,0,0,%
     0,0,1,1,0,0,1,1,0,0,0,1,1,1,1,0,0,0,0,0,1,1,1,1,0,0,0,0,1,1,0,0,0,0,0},%
    {1,1,0,1,1,1,0,0,0,1,1,1,1,1,1,0,0,0,0,1,1,0,0,1,1,0,0,1,1,0,0,0,0,0,0,%
     0,0,1,1,0,0,1,1,0,0,0,1,1,0,1,0,0,0,0,0,1,1,0,1,0,0,0,0,1,1,0,0,1,1,1},%
    {1,1,0,0,0,0,0,0,0,1,1,0,0,1,1,0,0,0,0,1,1,0,0,1,1,0,0,0,1,1,0,0,1,1,0,%
     0,0,1,1,0,1,1,0,0,0,0,1,1,0,0,0,1,0,0,0,1,1,0,0,0,0,0,0,0,1,1,0,0,1,1},%
    {0,1,1,1,1,1,0,0,0,1,1,0,0,1,1,0,0,0,1,1,1,1,1,1,0,0,0,0,0,1,1,1,1,0,0,%
     0,1,1,1,1,1,0,0,0,0,1,1,1,1,1,1,1,0,0,1,1,1,1,0,0,0,0,0,0,0,1,1,1,0,1},%
    {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,%
     0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}%
}
\newcommand\ASCIIfont{%
    OXXXXXOOOOOXXOOOOOXXXXXXOOOOOXXXXOOOXXXXXOOOOXXXXXXXOOXXXXXXXOOOOXXXXO,%
    XXOOOXXOOOXXXXOOOOOXXOOXXOOOXXOOXXOOOXXOXXOOOOXXOOOXOOOXXOOOXOOOXXOOXX,%
    XXOXXXXOOXXOOXXOOOOXXOOXXOOXXOOOOOOOOXXOOXXOOOXXOXOOOOOXXOXOOOOXXOOOOO,%
    XXOXXXXOOXXOOXXOOOOXXXXXOOOXXOOOOOOOOXXOOXXOOOXXXXOOOOOXXXXOOOOXXOOOOO,%
    XXOXXXOOOXXXXXXOOOOXXOOXXOOXXOOOOOOOOXXOOXXOOOXXOXOOOOOXXOXOOOOXXOOXXX,%
    XXOOOOOOOXXOOXXOOOOXXOOXXOOOXXOOXXOOOXXOXXOOOOXXOOOXOOOXXOOOOOOOXXOOXX,%
    OXXXXXOOOXXOOXXOOOXXXXXXOOOOOXXXXOOOXXXXXOOOOXXXXXXXOOXXXXOOOOOOOXXXOX,%
    OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO%
}

\begin{tikzpicture}
    \foreach\row [count=\yi] in \ASCIIbits{
        \foreach\chr [count=\xi] in \row {
            \ifodd\chr \fill (0.18*\xi,-0.18*\yi) circle (0.08) \fi;
        }
    }
\end{tikzpicture}

\begin{tikzpicture}
    \foreach\row [count=\yi] in \ASCIIfont{
        \dowithchar\row\yi
    }
\end{tikzpicture}

\end{document}
  • Thank you. I am learning much from your answers. I am making rapid progress both in my writings and my TeX. – jlettvin Feb 26 at 3:17
  • @jlettvin I am happy to hear that. I hope you also have fun. ;-) By the way: TeXbook, chapter 6 "Running TeX" says about fun and errors: "Error messages can be terrifying when you aren’t prepared for them; but they can be fun when you have the right attitude. Just remember that you really haven’t hurt the computer’s feelings, and that nobody will hold the errors against you. Then you’ll find that running TeX might actually be a creative experience instead of something to dread." Imho that kind of sayings really sucks when grappling with (La)TeX while working/writing towards a deadline. ;-) – Ulrich Diez Feb 26 at 18:38

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.