Логика алгоритма: создать цикл для каждых 3 строк в datatools

Логика алгоритма: создать цикл для каждых 3 строк в datatools

Я хочу сделать цикл с процессом:

Текущий: (с кодом ниже)

  • каждая строка создает одну страницу
  • Ящики A, B, C: одни и те же данные из одной строки datatool

Необходимо обновление:

  • Создать только одну страницу \Break=break или каждые 3 строки

  • Ящик A = данные строки: 1,4,7...,

  • Ящик B = данные строки: 2,5,8…

  • Ящик C= данные строки: 3,6,9

  • Каждые 3 строки в datatools создают одну страницу

Пример:

Страница 1:

A: данные строки: 1

B: данные строки: 2

C: данные строки: 3

Страница 2:

A: данные строки: 4

B: данные строки: 5

C; данные строки: 6

Минимальное кодирование:

\documentclass[a5paper,twoside,8pt]{article}
\usepackage[a5paper,landscape,left=1.0cm,right=0.3cm,top=0.5cm,bottom=0.5cm]{geometry}
\usepackage{tcolorbox}
\tcbuselibrary{poster}
\usepackage{tikz,everypage}
\usepackage[absolute,overlay]{textpos} 
\usepackage{filecontents}  

\begin{filecontents*}{product.tex}
%Type =1,2...10
No,Type,Name,Description,Break
1,1,A1,D1,xx
2,1,A1,D2,yy
3,1,A1,D3,break
4,1,A1,D30,ll
5,1,A2,D31,mm
6,1,A2,D131,break
7,1,A3,D132,bb
8,1,A3,D133,tt
9,1,A3,D134,break
10,1,A4,D249,ii
11,1,A10,D1000,bb
12,1,A2,D11,break
13,1,A3,D13,qq
14,1,A3,D135,gg
15,1,A3,D137,break
16,1,A4,D249,ff
17,1,A10,D100,gg
18,1,A43,D318,break
19,1,A44,D319,ss
20,1,A40,D320,ww
21,1,A43,D318,break
22,2,A44,D319,as
23,2,A40,D320,aw
\end{filecontents*}

\usepackage{datatool}
\usepackage{ifthen}
\DTLloaddb[autokeys=false]{products}{product.tex}

\newcommand{\printtype}[1]{%
  \DTLforeach*
    [\DTLiseq{\Type}{#1}]% Condition
    {products}% Database
    {\NoCoding=No,\Type=Type,\Name=Name,\Description=Description,\Break=Break}{%

    \begin{tcbposter}[
  poster = {
    columns=1,
    rows=2,
    spacing=3mm,
    height=14cm,
    width=12cm,
  },
  ]
%Box A
  \posterbox[
  colframe = red,
  width=5cm, height= 5cm
  ]{xshift=1 cm,yshift=-3cm}{\includegraphics[height=2cm]{example-image-a}
  \\
  \noindent \NoCoding \quad \Name  \quad \Break\par
  }
  %Box B
  \posterbox[
  colframe = blue,
  width=5cm, height= 5cm
  ]{xshift=7cm,yshift =-3cm }{\includegraphics[height=3cm]{example-image-b}
\\
  \noindent \NoCoding \quad \Name \quad \Break\par
  }
%Box C
  \posterbox[
  colframe = green,
  width=5cm, height= 5cm
  ]{xshift=13cm,yshift =-3cm }{\includegraphics[height=3cm]{example-image-c}
\\
  \noindent \NoCoding \quad \Name  \quad \Break \par
  }

\end{tcbposter}
    \newpage
  }% 
}
\begin{document}
\printtype{1}

\end{document}

Пример изображения текущего кода введите описание изображения здесь

Заранее спасибо

решение1

Следующий код реализует буферизацию поверх datatool, чтобы позволить вам обрабатывать строкинкн. Это работает через среду, lfbufferingкоторая называется так:

\begin{lfbuffering}{n}{macro names for needed columns}{code}
  \DTLforeach*{database}% Database
      {\macro1=colname1, \macro2=colname2, ..., \macrop=colnamep}
      {\lfbufProcessOneRow}
\end{lfbuffering}

Это будет вызывать код в третьем аргументе среды lfbufferingкаждый разнстроки были прочитаны (буферизованы) \DTLforeach*. Если меньше, чемнстроки доступны для последнего выполнениякод, он все равно будет выполнен; \lfbufNbBufferedRowsсообщает, сколько строк доступно в буфере (технически \lfbufNbBufferedRowsявляется \countdefтокеном; в частности, это TeX 〈number〉, т. е. целое число).

Так, например, еслинравен 4 и \DTLforeach*обеспечивает в общей сложности 11 строк базы данных, последовательные вызовыкодувидит \lfbufNbBufferedRowsравное 4, 4, затем 3 (4 + 4 + 3 = 11).кодможет быть макрос-имя или несколько токенов. Он имеет доступ к буферизованным полям, используя \lfbufField{k}{macroName}where

  • к1 для первой буферизованной строки, 2 для второй буферизованной строки и т. д. (кдолжно быть меньше или равно \lfbufNbBufferedRows);

  • Имя макросаявляется любым из macro1, macro2, ... (элементы из второго аргумента lfbuffering, соответствующие части или всем именам макросов, определенным во \DTLforeach*втором обязательном аргументе вызова, без начальных обратных косых черт).

Давайте рассмотрим простой пример:

\begin{lfbuffering}{3}{Type, Name, Description}{\myPrintBufferedData}
  \DTLforeach*{products}% Database
      {\NoCoding=No,\Type=Type,\Name=Name,\Description=Description,\Break=Break}
      {\lfbufProcessOneRow}
\end{lfbuffering}

Здесь мы обрабатываем строки (записи вашей productsбазы данных) 3 на 3. \lfbufProcessOneRow— единственный токен в третьем обязательном аргументе \DTLforeach*: его роль — собирать в памяти считанные строки, \DTLforeach*пока не будет 3, после чего он вызовет \myPrintBufferedData(содержимоекодаргумент среды lfbuffering). Вам нужно определить, \myPrintBufferedDataчто вы хотите сделать с буферизованными строками. Его определение может выглядеть следующим образом (учитывая значение, используемое для второго аргумента lfbufferingв этом примере, \myPrintBufferedDataможно получить доступ к полям Type, Nameи Description):

\newcommand*{\myPrintBufferedData}{%
  \setlength{\parindent}{0pt}% for instance
  \ifnum\lfbufNbBufferedRows>0 % <-- space or end-of-line here, important!
    \lfbufField{1}{Type}, \lfbufField{1}{Name},
    \lfbufField{1}{Description}\par
  \fi
  %
  \ifnum\lfbufNbBufferedRows>1 % here too
    \lfbufField{2}{Type}, \lfbufField{2}{Name},
    \lfbufField{2}{Description}\par
  \fi
  %
  \ifnum\lfbufNbBufferedRows>2 % and here
    \lfbufField{3}{Type}, \lfbufField{3}{Name},
    \lfbufField{3}{Description}\par\medskip
  \fi
}

Так каккодАргумент окружения lfbufferingникогда не вызывается с пустым буфером, первый тест ( \ifnum\lfbufNbBufferedRows>0[заканчивается пробелом]) можно опустить. Но таким образом все случаи следуют одному и тому же шаблону. Вот полный пример, аналогичный тому, что мы только что объяснили:

\RequirePackage{filecontents}
\begin{filecontents*}{product.tex}
%Type =1,2...10
No,Type,Name,Description,Break
1,1,A1,D1,xx
2,1,A1,D2,yy
3,1,A1,D3,break
4,1,A1,D30,ll
5,1,A2,D31,mm
6,1,A2,D131,break
7,1,A3,D132,bb
8,1,A3,D133,tt
9,1,A3,D134,break
10,1,A4,D249,ii
11,1,A10,D1000,bb
12,1,A2,D11,break
13,1,A3,D13,qq
14,1,A3,D135,gg
15,1,A3,D137,break
16,1,A4,D249,ff
17,1,A10,D100,gg
18,1,A43,D318,break
19,1,A44,D319,ss
20,1,A40,D320,ww
21,1,A43,D318,break
22,2,A44,D319,as
23,2,A40,D320,aw
\end{filecontents*}

\documentclass{article}
\usepackage{xparse}
\usepackage{datatool}
\DTLloaddb[autokeys=false]{products}{product.tex}

\ExplSyntaxOn

\int_new:N \l_lfbuf_buffer_depth_int
\seq_new:N \l_lfbuf_colnames_seq
\tl_new:N \l_lfbuf_output_callback_tl

% #1: zero-based index of buffered row
% #2: field name
% #3: value
\cs_new_protected:Npn \lfbuf_store_field_aux:nnn #1#2#3
  {
    \tl_set:cn { l_lfbuf_data_#1_#2_tl } {#3}
  }

\cs_generate_variant:Nn \lfbuf_store_field_aux:nnn { nnV }

% #1: zero-based index of buffered row
% #2: field name
\cs_new_protected:Npn \lfbuf_store_field:nn #1#2
  {
    % Get the field contents; this requires 3 expansion steps
    \tl_set:No \l_tmpa_tl { \use:c {#2} }
    \exp_args:NNNo \exp_args:NNo \tl_set:No \l_tmpa_tl { \l_tmpa_tl}
    \lfbuf_store_field_aux:nnV {#1} {#2} \l_tmpa_tl
  }

\cs_generate_variant:Nn \lfbuf_store_field:nn { Vn }

\cs_new_protected:Npn \lfbuf_clear_buffer_vars:
  {
    \int_step_inline:nnn { 0 } { \l_lfbuf_buffer_depth_int - 1 }
      {
        \seq_map_inline:Nn \l_lfbuf_colnames_seq
          { \tl_clear_new:c { l_lfbuf_data_##1_####1_tl } }
      }
  }

% These two are often identical, but not always
\int_new:N \l_lfbuf_buffered_row_index_int
\int_new:N \lfbufNbBufferedRows       % user-accessible from callback code

\cs_new_protected:Npn \lfbuf_process_one_row:
  {
    \seq_map_inline:Nn \l_lfbuf_colnames_seq
      { \lfbuf_store_field:Vn \l_lfbuf_buffered_row_index_int {##1} }

    % Advance the index, but stay modulo \l_lfbuf_buffer_depth_int
    \int_set:Nn \l_lfbuf_buffered_row_index_int
      { \int_mod:nn
          { \l_lfbuf_buffered_row_index_int + 1 }
          { \l_lfbuf_buffer_depth_int }
      }

    % Is the buffer full?
    \int_compare:nNnT { \l_lfbuf_buffered_row_index_int } = { 0 }
      {
        % Print output and start over with an empty buffer.
        \int_set_eq:NN \lfbufNbBufferedRows \l_lfbuf_buffer_depth_int
        \tl_use:N \l_lfbuf_output_callback_tl
      }
  }

\cs_new:Npn \lfbuf_get_field:nn #1#2
  {
    \use:c { l_lfbuf_data_#1_#2_tl }
  }

\cs_generate_variant:Nn \lfbuf_get_field:nn { f }

% *********************************************************************
% As opposed to all code-level functions, document commands use 1-based
% indexing (datatool also uses 1-based indexing for rows and columns).
% *********************************************************************

% Expand to field #2 (column title) of buffered row #1 (index starting from 1).
\NewExpandableDocumentCommand \lfbufField { m m }
  {
    \lfbuf_get_field:fn { \int_eval:n {#1-1} } {#2}
  }

\NewDocumentCommand \lfbufProcessOneRow { }
  {
    \lfbuf_process_one_row:
  }

\NewDocumentEnvironment { lfbuffering } { m m +m }
  {
    \int_set:Nn \l_lfbuf_buffer_depth_int {#1}
    \seq_set_from_clist:Nn \l_lfbuf_colnames_seq {#2}
    \tl_set:Nn \l_lfbuf_output_callback_tl {#3}
    \int_set:Nn \l_lfbuf_buffered_row_index_int { 0 }
    \lfbuf_clear_buffer_vars:
    \ignorespaces
  }
  {
    \unskip
    % If there is buffered data that hasn't been output, process it now (this
    % means that the last row of the datatool table didn't fill the buffer).
    \int_compare:nNnT { \l_lfbuf_buffered_row_index_int } > { 0 }
      {
        \int_set_eq:NN \lfbufNbBufferedRows \l_lfbuf_buffered_row_index_int
        \tl_use:N \l_lfbuf_output_callback_tl
      }
  }

\ExplSyntaxOff

\newcommand*{\myPrintBufferedData}{%
  \setlength{\parindent}{0pt}%
  % I keep this test for symmetry with the other cases, but it is always true.
  % You can remove it if you prefer.
  \ifnum\lfbufNbBufferedRows>0 % if there remains at least one row
    \lfbufField{1}{NoCoding}, \lfbufField{1}{Type}, \lfbufField{1}{Name},
    \lfbufField{1}{Description}, \lfbufField{1}{Break}\par
  \fi
  %
  \ifnum\lfbufNbBufferedRows>1
    \lfbufField{2}{NoCoding}, \lfbufField{2}{Type}, \lfbufField{2}{Name},
    \lfbufField{2}{Description}, \lfbufField{2}{Break}\par
  \fi
  %
  \ifnum\lfbufNbBufferedRows>2
    \lfbufField{3}{NoCoding}, \lfbufField{3}{Type}, \lfbufField{3}{Name},
    \lfbufField{3}{Description}, \lfbufField{3}{Break}\par\medskip
  \fi
}

\begin{document}

% Read and process 3 lines at a time. Call \myPrintBufferedData every time
% the buffer is full as well as at the end (i.e., the last call can have 1,
% 2 or 3 lines, as indicated by \lfbufNbBufferedRows).
\begin{lfbuffering}{3}{NoCoding, Type, Name, Description, Break}
                   {\myPrintBufferedData}
  \DTLforeach*{products}% Database
      {\NoCoding=No,\Type=Type,\Name=Name,\Description=Description,\Break=Break}
      {\lfbufProcessOneRow}
\end{lfbuffering}

\end{document}

Скриншот

А вот пример с вашим tcbposter:

\RequirePackage{filecontents}
\begin{filecontents*}{product.tex}
%Type =1,2...10
No,Type,Name,Description,Break
1,1,A1,D1,xx
2,1,A1,D2,yy
3,1,A1,D3,break
4,1,A1,D30,ll
5,1,A2,D31,mm
6,1,A2,D131,break
7,1,A3,D132,bb
8,1,A3,D133,tt
9,1,A3,D134,break
10,1,A4,D249,ii
11,1,A10,D1000,bb
12,1,A2,D11,break
13,1,A3,D13,qq
14,1,A3,D135,gg
15,1,A3,D137,break
16,1,A4,D249,ff
17,1,A10,D100,gg
18,1,A43,D318,break
19,1,A44,D319,ss
20,1,A40,D320,ww
21,1,A43,D318,break
22,2,A44,D319,as
23,2,A40,D320,aw
\end{filecontents*}

\documentclass{article}
\usepackage[landscape,hscale=0.8]{geometry}
\usepackage{tcolorbox}
\tcbuselibrary{poster}
\usepackage{xparse}
\usepackage{datatool}
\DTLloaddb[autokeys=false]{products}{product.tex}

\ExplSyntaxOn

\int_new:N \l_lfbuf_buffer_depth_int
\seq_new:N \l_lfbuf_colnames_seq
\tl_new:N \l_lfbuf_output_callback_tl

% #1: zero-based index of buffered row
% #2: field name
% #3: value
\cs_new_protected:Npn \lfbuf_store_field_aux:nnn #1#2#3
  {
    \tl_set:cn { l_lfbuf_data_#1_#2_tl } {#3}
  }

\cs_generate_variant:Nn \lfbuf_store_field_aux:nnn { nnV }

% #1: zero-based index of buffered row
% #2: field name
\cs_new_protected:Npn \lfbuf_store_field:nn #1#2
  {
    % Get the field contents; this requires 3 expansion steps
    \tl_set:No \l_tmpa_tl { \use:c {#2} }
    \exp_args:NNNo \exp_args:NNo \tl_set:No \l_tmpa_tl { \l_tmpa_tl}
    \lfbuf_store_field_aux:nnV {#1} {#2} \l_tmpa_tl
  }

\cs_generate_variant:Nn \lfbuf_store_field:nn { Vn }

\cs_new_protected:Npn \lfbuf_clear_buffer_vars:
  {
    \int_step_inline:nnn { 0 } { \l_lfbuf_buffer_depth_int - 1 }
      {
        \seq_map_inline:Nn \l_lfbuf_colnames_seq
          { \tl_clear_new:c { l_lfbuf_data_##1_####1_tl } }
      }
  }

% These two are often identical, but not always
\int_new:N \l_lfbuf_buffered_row_index_int
\int_new:N \lfbufNbBufferedRows       % user-accessible from callback code

\cs_new_protected:Npn \lfbuf_process_one_row:
  {
    \seq_map_inline:Nn \l_lfbuf_colnames_seq
      { \lfbuf_store_field:Vn \l_lfbuf_buffered_row_index_int {##1} }

    % Advance the index, but stay modulo \l_lfbuf_buffer_depth_int
    \int_set:Nn \l_lfbuf_buffered_row_index_int
      { \int_mod:nn
          { \l_lfbuf_buffered_row_index_int + 1 }
          { \l_lfbuf_buffer_depth_int }
      }

    % Is the buffer full?
    \int_compare:nNnT { \l_lfbuf_buffered_row_index_int } = { 0 }
      {
        % Print output and start over with an empty buffer.
        \int_set_eq:NN \lfbufNbBufferedRows \l_lfbuf_buffer_depth_int
        \tl_use:N \l_lfbuf_output_callback_tl
      }
  }

\cs_new:Npn \lfbuf_get_field:nn #1#2
  {
    \use:c { l_lfbuf_data_#1_#2_tl }
  }

\cs_generate_variant:Nn \lfbuf_get_field:nn { f }

% *********************************************************************
% As opposed to all code-level functions, document commands use 1-based
% indexing (datatool also uses 1-based indexing for rows and columns).
% *********************************************************************

% Expand to field #2 (column title) of buffered row #1 (index starting from 1).
\NewExpandableDocumentCommand \lfbufField { m m }
  {
    \lfbuf_get_field:fn { \int_eval:n {#1-1} } {#2}
  }

\NewDocumentCommand \lfbufProcessOneRow { }
  {
    \lfbuf_process_one_row:
  }

\NewDocumentEnvironment { lfbuffering } { m m +m }
  {
    \int_set:Nn \l_lfbuf_buffer_depth_int {#1}
    \seq_set_from_clist:Nn \l_lfbuf_colnames_seq {#2}
    \tl_set:Nn \l_lfbuf_output_callback_tl {#3}
    \int_set:Nn \l_lfbuf_buffered_row_index_int { 0 }
    \lfbuf_clear_buffer_vars:
    \ignorespaces
  }
  {
    \unskip
    % If there is buffered data that hasn't been output, process it now (this
    % means that the last row of the datatool table didn't fill the buffer).
    \int_compare:nNnT { \l_lfbuf_buffered_row_index_int } > { 0 }
      {
        \int_set_eq:NN \lfbufNbBufferedRows \l_lfbuf_buffered_row_index_int
        \tl_use:N \l_lfbuf_output_callback_tl
      }
  }

\ExplSyntaxOff

\newcommand*{\myPrintBufferedData}{%
  \begin{tcbposter}[poster={columns=1, rows=2, spacing=3mm,
                            height=14cm, width=12cm}]
  % Box A
  \posterbox[colframe=red, width=6cm, height=5cm]{xshift=0cm, yshift=-3cm}
    {% I keep this test for symmetry with the other cases, but it is always
     % true. You can remove it if you prefer.
      \ifnum\lfbufNbBufferedRows>0
        \includegraphics[width=4cm]{example-image-a}\\
        \noindent
        \lfbufField{1}{NoCoding}\quad
        \lfbufField{1}{Name}\quad
        \lfbufField{1}{Break}%
      \fi
    }%
  % Box B
  \posterbox[colframe=blue, width=6cm, height=5cm]{xshift=8cm, yshift=-3cm}
    {%
      \ifnum\lfbufNbBufferedRows>1
        \includegraphics[width=4cm]{example-image-b}\\
        \noindent
        \lfbufField{2}{NoCoding}\quad
        \lfbufField{2}{Name}\quad
        \lfbufField{2}{Break}%
      \fi
    }%
  % Box C
  \posterbox[colframe=green, width=6cm, height=5cm]{xshift=16cm, yshift=-3cm}
    {%
      \ifnum\lfbufNbBufferedRows>2
        \includegraphics[width=4cm]{example-image-c}\\
        \noindent
        \lfbufField{3}{NoCoding}\quad
        \lfbufField{3}{Name}\quad
        \lfbufField{3}{Break}%
      \fi
    }%
  \end{tcbposter}%
  \newpage
}

\newcommand{\printtype}[1]{%
  % Read and process 3 lines at a time. Call \myPrintBufferedData every time
  % the buffer is full as well as at the end (i.e., the last call can have 1,
  % 2 or 3 lines, as indicated by \lfbufNbBufferedRows).
  \begin{lfbuffering}{3}{NoCoding, Type, Name, Description, Break}
                     {\myPrintBufferedData}
    \DTLforeach*
      [\DTLiseq{\Type}{#1}]% Condition
      {products}% Database
      {\NoCoding=No,\Type=Type,\Name=Name,\Description=Description,\Break=Break}
      {\lfbufProcessOneRow}
  \end{lfbuffering}%
}

\begin{document}

\printtype{1}

\end{document}

Страница 1:

Страница 1

Страница 2:

Страница 2

Страница 3:

Страница 3

...

Страница 7:

Страница 7

Связанный контент