16

I want to create a SEPA bank transfer form with IBAN

How can I create a formfield with N fields, each 5 mm wide with separations like in the picture? formfield

At the moment I use a workaround with a for loop, but this renders the code hard to maintain. Finally I need a function \formline{N}{cellwidth}{label}{value} to create the complete formfield and fill it with an optional value: complete formfield

The following code is available on https://github.com/jonasstein/bankformtex.

\documentclass[a4paper,11pt]{article}
\usepackage[utf8]{inputenc}
\usepackage{tikz}
\usetikzlibrary{shapes}

\thispagestyle{empty} % no pagenumber. The form should be a stand alone macro... later.

\definecolor{SEPAOrange}{RGB}{254,213,161}
\definecolor{SEPADOrange}{RGB}{253,185,19}
\definecolor{SEPABlindcolor}{RGB}{255,0,0}
\begin{document}
\begin{tikzpicture}[x=1 mm, y=-1mm]
\pgfmathsetmacro{\yh}{4.2333} % y heigth step defined as 1/6 inch => 1/6 * 25.4 mm = 4.2333 mm
\pgfmathsetmacro{\xs}{9} % x start (own definition)
\pgfmathsetmacro{\xe}{141.5} % x end (own definition)
\pgfmathsetmacro{\widefield}{4.9859} % def: 134.62 mm / 27
\pgfmathsetmacro{\narrowfiels}{3.9594} % def: 134.62 mm/ 34

\filldraw[draw=black,color=SEPAOrange] (7.62, 4.5*\yh) rectangle (149.86-7.62,105.83-1); %orange background

\filldraw[draw=black,color=white] (\xs, 5*\yh) rectangle (\xe, 6.5*\yh -1.5); %Recepient 27 Char
\foreach \x in {1,...,26}
{
 \draw[color=SEPAOrange, line width=0.3mm] (7.62 + \x*\widefield, 5*\yh ) -- (7.62 + \x*\widefield, 6.5*\yh);
 \draw[color=SEPAOrange, line width=0.7mm] (7.62 + \x*\widefield, 5.8*\yh ) -- (7.62 + \x*\widefield, 6.5*\yh);
}

\filldraw[draw=black,color=white] (\xs, 7*\yh) rectangle (\xe, 8.5*\yh -1.5); %IBAN 34 Char
\foreach \x in {1,...,33}
 \draw[color=SEPAOrange, line width=0.3mm] (7.62 + \x*4, 7*\yh ) -- (7.62 + \x*4, 8.5*\yh);

\filldraw[draw=black,color=white] (\xs, 9*\yh) rectangle (\xe, 10.5*\yh -1.5); %BIC
\filldraw[draw=black,color=white] (\xe-12*5, 11*\yh) rectangle (\xe, 12.5*\yh -1.5); %Value 12 Char

\filldraw[draw=black,color=white] (\xs, 13*\yh) rectangle (\xe, 14.5*\yh -1.5); %Subject1
\filldraw[draw=black,color=white] (\xs, 15*\yh) rectangle (\xe, 16.5*\yh -1.5); %Subject2
\filldraw[draw=black,color=white] (\xs, 17*\yh) rectangle (\xe, 18.5*\yh -1.5); %Subject3

\filldraw[draw=black,color=white] (\xs, 19*\yh) rectangle (\xs+22*5, 20.5*\yh -1.5); % IBAN 22 Char

\filldraw[draw=black,color=white] (149.86-17.59, 19* \yh) rectangle (\xe, 20.5*\yh -1.5); % counter
\filldraw[draw=black,color=white] (\xs, 21* \yh) rectangle (42.52, 24.5*\yh); % date
\filldraw[draw=black,color=white] (42.52+5, 21*\yh) rectangle (149.86-17.59, 24.5*\yh); % signature

\draw[color=SEPADOrange] (0, 4.5 *\yh) --(149.86, 4.5*\yh); upper dark orange line
\draw[color=SEPADOrange] (0, 20.5 *\yh) --(149.86, 20.5*\yh); lower dark orange line
\draw[draw=black,color=black, line width=0.3mm] (0,0) rectangle (149.86,105.83); % black border
%\draw[align=left] at (\yh,\yh) {SEPA-Überweisung};

\end{tikzpicture}
\end{document} 
  • If you post a Minimum Working Example showing what you've tried, I'm sure people will be happy to advise on any sticky details causing trouble. you might want to start with hyperref which can make the forms fillable, for example. – cfr Jan 2 '14 at 2:30
18

usage

\formline*(coord)[field width][scope options]{N}{label}[value]<dividers>

mandatory arguments

  • coord: where to place the line; anchor is west of the first field.
  • N: number of fields.
  • label: name of the line.

optional arguments

  • *: ignore spaces in value.
  • field width: width of a field.
  • scope options: options are passed to the {scope}.
  • value: fill in some thing, should contain only normal characters.
  • dividers: add dividers between files; can be either a comma list of file numbers or every n (n can be positiv or negative).

examples

\begin{tikzpicture}
    % background
    \fill [SEPAOrange] (0,0) rectangle (15,12);
    % formlines
    \formline(1,10){25}{Bank}[Deutsche Bank Hamburg]
    \formline(1,9)[6mm]{5}{Number 1}[123456]
    \formline(1,8)[6mm]{5}{Number 2}[1234]
    \formline*(1,7){22}{IBAN 1}[DE00 2105 0170 0012 3456 78]<every 4>
        % dividers every 4th field from right
    \formline*(1,6){22}{IBAN 2}<every -4>% dividers every 4th from left
    \formline*(1,5){22}{IBAN 3}<2,5,10,15,20>% irregular dividers
\end{tikzpicture}

examples

notes

  • Every field is a single node and is named with form field label n, where label is the argument value and n is the number of the field (starting from 0). You could use this node names to add frames or some stuff like that.
  • There are several TikZ styles that can be used to change the appearance.
  • I added some explanations to the code – please ask if anything is unclear!

full code

\documentclass{article}

% packages
\usepackage{tikz,xparse}
\usetikzlibrary{positioning,fit,calc}

% colors
\definecolor{SEPAOrange}{RGB}{254,213,161}
\definecolor{SEPADOrange}{RGB}{253,185,19}

% switch on expl3 syntax
% (_ and : become part of macro names; spaces are ignored; ~ is normal space)
\ExplSyntaxOn
% make @ available as part of macro name
\makeatletter
% commad to genreate internal lengths
% use \form_generate_lengths:n {<comma list>}
\cs_new:Npn \form_generate_lengths:n #1 {
    \clist_set:Nn \l_tmpa_clist { #1 }
    \clist_map_inline:Nn \l_tmpa_clist {
        \expandafter\newlength \csname form @ x ##1 \endcsname
        \expandafter\newlength \csname form @ y ##1 \endcsname
    }
}
% gererate internal length used in node shapes
\form_generate_lengths:n {
    A, Ab, At, Abt,
    B, Bb, Bt, Bbt,
    D,
    C,
}
% sep between fields
\dim_new:N \g_form_sep_dim
\dim_set:Nn \g_form_sep_dim { 0.5pt }
% dimensions of the edges in fields
\dim_new:N \g_form_x_edge_dim
\dim_set:Nn \g_form_x_edge_dim { 0.5pt }
\dim_new:N \g_form_y_edge_dim
\dim_set:Nn \g_form_y_edge_dim { 3pt }
\dim_new:N \formdividerwidth
\dim_set:Nn \formdividerwidth { 2\g_form_x_edge_dim + \g_form_sep_dim }
\dim_new:N \formdividerheight
\dim_set:Nn \formdividerheight { \g_form_y_edge_dim }
% new node shapes for fields
\pgfdeclareshape{form~field~middle}{% from p. 631 in pgfmanual.pdf
    \inheritsavedanchors[from=rectangle]
    \inheritanchorborder[from=rectangle]
    \inheritanchor[from=rectangle]{base}
    \inheritanchor[from=rectangle]{center}
    \inheritanchor[from=rectangle]{north}
    \inheritanchor[from=rectangle]{north~east}
    \inheritanchor[from=rectangle]{east}
    \inheritanchor[from=rectangle]{south~east}
    \inheritanchor[from=rectangle]{south}
    \inheritanchor[from=rectangle]{south~west}
    \inheritanchor[from=rectangle]{west}
    \inheritanchor[from=rectangle]{north~west}
    \backgroundpath{%  this is new
        %  store corners
        \southwest \form@xA = \pgf@x \form@yA = \pgf@y
        \northeast \form@xC = \pgf@x \form@yC = \pgf@y
        \form@xB = \form@xC \form@yB = \form@yA
        \form@xD = \form@xA \form@yD = \form@yC
        % calculate edges
        \form@xAt = \form@xA
        \form@yAt = \form@yA \advance \form@yAt by \g_form_y_edge_dim
        \form@xAb = \form@xA \advance \form@xAb by \g_form_x_edge_dim
        \form@yAb = \form@yA
        \form@xAbt = \form@xAb
        \form@yAbt = \form@yAt
        \form@xBt = \form@xB
        \form@yBt = \form@yB \advance \form@yBt by \g_form_y_edge_dim
        \form@xBb = \form@xB \advance \form@xBb by -\g_form_x_edge_dim
        \form@yBb = \form@yB
        \form@xBbt = \form@xBb
        \form@yBbt = \form@yBt
        %  construct main path
        \pgfpathmoveto{\pgfpoint{\form@xAt}{\form@yAt}}
        \pgfpathlineto{\pgfpoint{\form@xD}{\form@yD}}
        \pgfpathlineto{\pgfpoint{\form@xC}{\form@yC}}
        \pgfpathlineto{\pgfpoint{\form@xBt}{\form@yBt}}
        \pgfpathlineto{\pgfpoint{\form@xBbt}{\form@yBbt}}
        \pgfpathlineto{\pgfpoint{\form@xBb}{\form@yBb}}
        \pgfpathlineto{\pgfpoint{\form@xAb}{\form@yAb}}
        \pgfpathlineto{\pgfpoint{\form@xAbt}{\form@yAbt}}
        \pgfpathlineto{\pgfpoint{\form@xAt}{\form@yAt}}
        \pgfpathclose
    }
}
\pgfdeclareshape{form~field~start}{
    \inheritsavedanchors[from=rectangle]
    \inheritanchorborder[from=rectangle]
    \inheritanchor[from=rectangle]{center}
    \inheritanchor[from=rectangle]{base}
    \inheritanchor[from=rectangle]{north}
    \inheritanchor[from=rectangle]{north~east}
    \inheritanchor[from=rectangle]{east}
    \inheritanchor[from=rectangle]{south~east}
    \inheritanchor[from=rectangle]{south}
    \inheritanchor[from=rectangle]{south~west}
    \inheritanchor[from=rectangle]{west}
    \inheritanchor[from=rectangle]{north~west}
    \backgroundpath{%  this is new
        %  store corners
        \southwest \form@xA = \pgf@x \form@yA = \pgf@y
        \northeast \form@xC = \pgf@x \form@yC = \pgf@y
        \form@xB = \form@xC \form@yB = \form@yA
        \form@xD = \form@xA \form@yD = \form@yC
        % calculate edges
        \form@xAt = \form@xA
        \form@yAt = \form@yA \advance \form@yAt by \g_form_y_edge_dim
        \form@xAb = \form@xA \advance \form@xAb by \g_form_x_edge_dim
        \form@yAb = \form@yA
        \form@xAbt = \form@xAb
        \form@yAbt = \form@yAt
        \form@xBt = \form@xB
        \form@yBt = \form@yB \advance \form@yBt by \g_form_y_edge_dim
        \form@xBb = \form@xB \advance \form@xBb by -\g_form_x_edge_dim
        \form@yBb = \form@yB
        \form@xBbt = \form@xBb
        \form@yBbt = \form@yBt
        %  construct main path
        \pgfpathmoveto{\pgfpoint{\form@xA}{\form@yA}}
        \pgfpathlineto{\pgfpoint{\form@xD}{\form@yD}}
        \pgfpathlineto{\pgfpoint{\form@xC}{\form@yC}}
        \pgfpathlineto{\pgfpoint{\form@xBt}{\form@yBt}}
        \pgfpathlineto{\pgfpoint{\form@xBbt}{\form@yBbt}}
        \pgfpathlineto{\pgfpoint{\form@xBb}{\form@yBb}}
        \pgfpathlineto{\pgfpoint{\form@xA}{\form@yA}}
        \pgfpathclose
    }
}
\pgfdeclareshape{form~field~end}{
    \inheritsavedanchors[from=rectangle]
    \inheritanchorborder[from=rectangle]
    \inheritanchor[from=rectangle]{center}
    \inheritanchor[from=rectangle]{base}
    \inheritanchor[from=rectangle]{north}
    \inheritanchor[from=rectangle]{north~east}
    \inheritanchor[from=rectangle]{east}
    \inheritanchor[from=rectangle]{south~east}
    \inheritanchor[from=rectangle]{south}
    \inheritanchor[from=rectangle]{south~west}
    \inheritanchor[from=rectangle]{west}
    \inheritanchor[from=rectangle]{north~west}
    \backgroundpath{%  this is new
        %  store corners
        \southwest \form@xA = \pgf@x \form@yA = \pgf@y
        \northeast \form@xC = \pgf@x \form@yC = \pgf@y
        \form@xB = \form@xC \form@yB = \form@yA
        \form@xD = \form@xA \form@yD = \form@yC
        % calculate edges
        \form@xAt = \form@xA
        \form@yAt = \form@yA \advance \form@yAt by \g_form_y_edge_dim
        \form@xAb = \form@xA \advance \form@xAb by \g_form_x_edge_dim
        \form@yAb = \form@yA
        \form@xAbt = \form@xAb
        \form@yAbt = \form@yAt
        \form@xBt = \form@xB
        \form@yBt = \form@yB \advance \form@yBt by \g_form_y_edge_dim
        \form@xBb = \form@xB \advance \form@xBb by -\g_form_x_edge_dim
        \form@yBb = \form@yB
        \form@xBbt = \form@xBb
        \form@yBbt = \form@yBt
        %  construct main path
        \pgfpathmoveto{\pgfpoint{\form@xAt}{\form@yAt}}
        \pgfpathlineto{\pgfpoint{\form@xD}{\form@yD}}
        \pgfpathlineto{\pgfpoint{\form@xC}{\form@yC}}
        \pgfpathlineto{\pgfpoint{\form@xB}{\form@yB}}
        \pgfpathlineto{\pgfpoint{\form@xAb}{\form@yAb}}
        \pgfpathlineto{\pgfpoint{\form@xAbt}{\form@yAbt}}
        \pgfpathlineto{\pgfpoint{\form@xAt}{\form@yAt}}
        \pgfpathclose
    }
}
% new command to draw a line of fiels
% usage: \formline*(coord)[field width][scope options]{N}{label}[value]<dividers>
%                #1   #2       #3           #4        #5    #6    #7      #8
%        * = ignore spaces in [value]
%        required: #2, #5, #6,
%        optional: #1, #3, #4, #7, #8
\NewDocumentCommand { \formline } { s r() O{3mm} O{} m m o d<> } {
    % save field width
    \dim_set:Nn \l_tmpa_dim { #3 }
    % begin a scope
    \begin{scope}
        [every~form~field/.append~style={minimum~width=\dim_use:N \l_tmpa_dim}, #4]
        % draw fields
        %% set temp counter to 0
        \int_set:Nn \l_tmpa_int {0}
        %% first node (number 0)
        \node (form~field~#6~0) [at={(#2)}, every~form~field~start] {\strut};
        %% middle nodes, go through list form 1 to N-1
        \int_step_inline:nnnn { 1 } { 1 } { #5 - 2 } {
            %%% draw node
            \node (form~field~#6~##1) [
                every~form~field~middle,
                right=\dim_use:N \g_form_sep_dim of~form~field~#6~\int_use:N \l_tmpa_int
            ] {\strut};
            %%% incremet counter by 1
            \int_incr:N \l_tmpa_int
        }
        %% draw last node (number N)
        \node (form~field~#6~\int_eval:n { #5 - 1 }) [
            every~form~field~end,
            right=\dim_use:N \g_form_sep_dim of~form~field~#6~\int_use:N \l_tmpa_int
        ] {\strut};
        % draw label
        \node [every~form~label, above~right=0pt~of~form~field~#6~0.north~west] {#6};
        % add value, if exist
        \IfValueT { #7 } {
            %% save value to token list
            \tl_set:Nn \l_tmpa_tl { #7 }
            %% replace input space with space token
            \IfBooleanF { #1 } {
                \tl_replace_all:Nnn \l_tmpa_tl { ~ } { \c_space_token }
            }
            %% set temp counter to 0
            \int_set:Nn \l_tmpa_int { 0 }
            %% iterate through tokes and print digits above fields
            \tl_map_inline:Nn \l_tmpa_tl {
                %%% chack overlong values
                \int_compare:nT { \l_tmpa_int = #5 } {
                    \node at (form~field~#6~\int_eval:n { \l_tmpa_int - 1 })
                        [fill=red, font=\bfseries] {ERROR:~value~too~long};% some error message
                    \tl_map_break:
                }
                %%% draw digit
                \node [every~form~digit] at (form~field~#6~\int_use:N \l_tmpa_int.base) { ##1 };
                %%% incremet counter by 1
                \int_incr:N \l_tmpa_int
            }
        }
        % add sub-dividers, if exist
        \IfValueT { #8 } {
            %% save lowercase version of #7 to token list
            \tl_set:Nn \l_tmpa_tl { #8 }
            %% if it contains "every"
            \tl_if_in:NnTF \l_tmpa_tl { every } {
                %%% remove "every"
                \tl_remove_all:Nn \l_tmpa_tl { every }
                %%% save to int
                \int_set:Nn \l_tmpa_int { \l_tmpa_tl }
                %%% from left (> 0) or right (< 0)?
                \int_compare:nTF { \l_tmpa_int > 0 } {
                    %%%% draw marks from left
                    \int_step_inline:nnnn { \l_tmpa_int - 1 } { \l_tmpa_int } { #5 - 2 } {
                        \node at
                            ($(form~field~#6~##1.south~east)+(\dim_use:N \g_form_sep_dim / 2,0)$)
                            [every~form~divider] {};
                    }
                } {
                    %%%% draw marks from right
                    \int_step_inline:nnnn { #5 - 1 + \l_tmpa_int } { \l_tmpa_int } { 0 } {
                        \node at
                            ($(form~field~#6~##1.south~east)+(\dim_use:N \g_form_sep_dim / 2,0)$)
                            [every~form~divider] {};
                    }
                }
            } {
                %%% iterate throug comma list
                \clist_map_inline:nn { #8 } {
                    \node at
                        ($(form~field~#6~##1.south~east)+(\dim_use:N \g_form_sep_dim / 2,0)$)
                        [every~form~divider] {};
                }
            }
        }
    \end{scope}
}
% switch off expl3 syntax and @
\ExplSyntaxOff\makeatother

\tikzset{
    every form field/.style = {
        fill=white,
        inner sep=0pt,
        minimum height=4mm,
        align=center,
    },
    every form field middle/.style = {
        every form field,
        form field middle,
    },
    every form field start/.style = {
        every form field,
        form field start,
        anchor=south west,
    },
    every form field end/.style = {
        every form field,
        form field end,
    },
    every form label/.style = {
        fill=white, text=SEPADOrange,
        inner sep=1pt,
        outer sep=0pt,
        font=\tiny\sffamily\bfseries,
    },
    every form digit/.style = {
        anchor=base,
        font=\ttfamily,
        inner sep=0pt,
    },
    every form divider/.style = {
        fill=black,
        inner sep=0pt,
        outer sep=0pt,
        anchor=south,
        minimum width=\formdividerwidth,
        minimum height=\formdividerheight,
    },
}

\begin{document}
\begin{tikzpicture}
    % background
    \fill [SEPAOrange] (0,0) rectangle (15,12);
    % formlines
    \formline(1,10){25}{Bank}[Deutsche Bank Hamburg]
    \formline(1,9)[6mm]{5}{Number 1}[123456]
    \formline(1,8)[6mm]{5}{Number 2}[1234]
    \formline*(1,7){22}{IBAN 1}[DE00 2105 0170 0012 3456 78]<every 4>
        % dividers every 4th field from left
    \formline*(1,6){22}{IBAN 2}<every -4>% dividers every 4th from right
    \formline*(1,5){22}{IBAN 3}<2,5,10,15,20>% irregular dividers
\end{tikzpicture}
\end{document}
  • If have a solution with key-value-options in mind instead of this one using eight arguments; but at the moment I don’t find the time to implement it … – Tobi Jan 3 '14 at 14:01
  • It looks already very good. There is a hairline between the label box and fill-in area. – Jonas Stein Jan 5 '14 at 0:43
  • I know, but I was to lazy for such fine-tuning. You can use a small negative value in the line \node [every~form~label, above~right=0pt~of~form~field~#6~0.north~west] {#6}; … I hope I find some time next week to implement the key-value-options and add the possibility to have plain rectangular fields. – Tobi Jan 5 '14 at 1:35
  • In my tests it looks, if the distance of 10 5 mm boxes is more than 50 mm. – Jonas Stein Jan 5 '14 at 15:34
  • Sure there is a gap of 1pt between the boxes. So 10 boxes should have a width of 10*5mm + 9*1pt = 53.18 mm ;-) – Tobi Jan 5 '14 at 17:13

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.