tabularray와 결합된 Odsfile 패키지: 잘못 배치된 정렬 탭 문자 및 오류

tabularray와 결합된 Odsfile 패키지: 잘못 배치된 정렬 탭 문자 및 오류

tabularray 패키지와 함께 odsfile 패키지를 사용하여 ods 파일을 포함하고 싶습니다. 그러나 이로 인해 Misplaced alignment tab character &오류가 발생합니다. M(n)WE는 다음과 같습니다.

%% Compile with lualatex
\documentclass{article}

\usepackage{odsfile}
\usepackage{tabularray}


\tabletemplate{tblr}{%
\begin{tblr}{-{coltypes}}
-{content}
\end{tblr}
}

\begin{document}

%% Works:
\begin{tabular}{ll}
\includespread[file=data.ods,range=a1:b3]
\end{tabular}

%% This is what I want (in particular automaticaly determining the number of columns), but doesn't work
\begin{tblr}{}
\includespread[file=data.ods,range=a1:b3]
\end{tblr}

%% For testing purposes I specified the column types (which I don't want), but it works. 
\begin{tblr}{ll}
\includespread[file=data.ods,range=a1:b3]
\end{tblr}

%% This works, but I don't want to use a template in this case
\includespread[file=data.ods,range=a1:b3,template=tblr]

\end{document}

파일 data.ods다음을 포함합니다:

X   Y
1   a
2   b

오류는 다음과 같습니다:

1 & a\\ 2 & b\\
X & Y\\ 1 & a\\ 2 & b\\

! Misplaced alignment tab character &.
l.1 X &
      Y\\ 1 & a\\ 2 & b\\
l.24 \end
       {tblr}

어떻게 작동하게 할 수 있나요?

편집하다michal.h21의 답변에서 알 수 있듯이 완전한 솔루션을 찾는 것은 꽤 복잡한 것 같습니다. 그래서 저는 특히 달성하고 싶은 것에 대한 배경 지식을 좀 더 제공하려고 노력합니다.

%% Compile with lualatex
\documentclass{article}

\usepackage{odsfile}
\usepackage{tabularray}

\begin{document}


\begin{tabular}{cc}
\includespread[file=data.ods,range=a1:b1]
\includespread[file=data.ods,range=a3:b3]
%% Some more rows...
\end{tabular}

\end{document}

이 예제의 요점은 odsfile의 첫 번째 행과 세 번째 행(및 콘로우가 아닌 행)을 선택하고 이를 하나의 LaTeX 테이블로 병합한다는 것입니다.

나는 매번 행 수를 계산하거나 계산하고 싶지 않기 때문에(사소하지만 대규모 프로젝트에서는 성가신 일임) 테이블의 모양을 수정하기 위해 tabularray의 뛰어난 기능을 사용하기를 원하기 때문에 이것이 tblr과 함께 작동하기를 원합니다.

따라서 이것이 템플릿과 함께 작동한다면 현재 사용 사례에는 충분할 것이라고 생각합니다.

답변1

몇 가지 문제가 있습니다. 첫 번째는 Tabularray가 다음과 같은 콘텐츠와 관련된 일반적인 문제를 가지고 있다는 것입니다.Lua에서 전달됨. 확장하는 방식으로 환경을 전달할 수 없었고 항상 오류가 발생 \includespread했습니다 . 옵션을 사용하는 방법이 유일하게 작동하는 것이 tblr두렵습니다 .template\includespread

또 다른 문제는 가 테이블의 줄을 구분하는 데 odsfile사용되지 않고 \\자체 명령 \OdsNl\OdsLastNl. tblr명시적인 문자가 필요 하기 때문에 \\컴파일 오류가 발생하지 않더라도 잘못된 테이블이 표시됩니다.

다음은 두 개의 새로운 옵션 을 odsfile.sty추가하는 수정된 버전입니다 newline. \lastnewline이를 사용하여 다음 명령을 사용하도록 지정할 수 있습니다 \\.

% Package odsfile. Author Michal Hoftich <[email protected]>
% This package is subject of LPPL license, version 1.3c
\ProvidesPackage{odsfile}[2023/09/07 v0.8 odsfile package to select cells from ODS sheets and
      typeset them as LaTeX tables]
\RequirePackage{luacode,xkeyval,xparse}

%keyval keys

\define@key{includespread}{file}{\loadodsfile{#1}}%
\define@key{includespread}{sheet}{\luaexec{sheetname = "\luatexluaescapestring{#1}"}}%
\define@key{includespread}{range}{\luaexec{range="\luatexluaescapestring{#1}"}}%
\define@key{includespread}{template}{\luaexec{currenttemplate="\luatexluaescapestring{#1}"}}%
\define@key{includespread}{rowtemplate}{\luaexec{rowtemplate="\luatexluaescapestring{\detokenize{#1}}"}}%
\define@key{includespread}{celltemplate}{\luaexec{celltpl="\luatexluaescapestring{\detokenize{#1}}"}}%
\define@key{includespread}{multicoltemplate}{\luaexec{multicoltpl="\luatexluaescapestring{\unexpanded{#1}}"}}%
\define@key{includespread}{escape}{\luaexec{latexescape="\luatexluaescapestring{\unexpanded{#1}}"}}%

\newcommand\OdsNl{\\}
\newcommand\OdsLastNl{\\}

\define@choicekey*+{includespread}{rowseparator}[\val\nr]{tableline,hline,newline}[tableline]%
{%
\ifcase\nr\relax%
  \luaexec{rowseparator=""}%
\or%
  \luaexec{rowseparator="\\hline "}%
\or%
\luaexec{%
rowseparator=[[\\n]]
}
\fi%
}{%
    \luaexec{rowseparator="\luatexluaescapestring{#1}"}
}
\define@key{includespread}{columnbreak}{%
  \luaexec{columnbreak="\luatexluaescapestring{\unexpanded{#1}}{}"}%
}
\define@key{includespread}{coltypes}{%
  \luaexec{coltypes="\luatexluaescapestring{\unexpanded{#1}}"}%
}
\define@choicekey*+{includespread}{columns}[\val\nr]{head,top,none}{%
\ifcase\nr\relax%
  \luaexec{columns=1}%
\or%
  \luaexec{columns=2}%
\fi%
}{%
\luaexec{%
local function split(s,sep)
        local sep, fields = sep or ":", {}
        local pattern = string.format("([^\%s]+)", sep)
        s:gsub(pattern, function(c) fields[\#fields+1] = c end)
        return fields
end
local s="\luatexluaescapestring{#1}"
columns = split(s,",")
}%
}{}%

\define@key{includespread}{newline}{%
  \luaexec{odsnl="\luatexluaescapestring{\unexpanded{#1}}"}%
}

\define@key{includespread}{lastnewline}{%
  \luaexec{odslastnl="\luatexluaescapestring{\unexpanded{#1}}"}%
}

% Variable initialization and helper functions
\begin{luacode*}
odsreader   = require("odsfile")
odsfile     = nil
sheetname   = nil
range       = {nil,nil,nil,nil}
columns     = nil
templates   = {}
row         = {}
body        = nil
odsfilename = ""
currenttemplate = nil
rowtemplate = nil
celltpl   = "-{value}"
multicoltpl = "\\multicolumn{-{count}}{l}{-{value}}"
latexescape = "true"
odsnl       = "\\OdsNl"
odslastnl   = "\\OdsLastNl"
\end{luacode*}

\newcommand\loadodsfile[2][]{%
  \setkeys{includespread}{#1}%
  \luaexec{%
    odsfilename     = "\luatexluaescapestring{#2}"%
    local ods   = odsreader.load(odsfilename)%
    odsfile, e  = ods:loadContent()%
  }%
}

\newcommand\tabletemplate[2]{%
  \luaexec{%
    templates["#1"]="\luatexluaescapestring{\unexpanded{#2}}"%
  }
}

\NewDocumentCommand\includespread{s o}{%
  \IfBooleanTF#1%
  {\ods@includespread@star{#2}}%
  {\ods@includespread@unstar{#2}}%
}

\newcommand\ods@includespread@star[1]{%
  \let\ods@tmp@lastNL\OdsLastNl%
  \renewcommand\OdsLastNl{}%
  \ods@includespread@unstar{#1}%
  \let\OdsLastNl\ods@tmp@lastNL%
}

\newcommand\ods@includespread@unstar[1]{%
  \luaexec{%
    range = nil
    rowseparator = ""
    columns=nil
    currenttemplate = nil 
    rowtemplate = nil
    celltpl = "-{value}"
    columnbreak = "\\linebreak{}"
    coltypes    = nil
    latexescape = "true"
  }%
  \setkeys{includespread}{#1}%
  \luaexec{%
    body   = odsreader.getTable(odsfile,sheetname)
    local real_range = odsreader.getRange(range)
    local values = odsreader.tableValues(body,real_range[1],real_range[2],real_range[3],real_range[4])
    %-- Conversion of odsfile table values to LaTeX tabular  
    local concatParagraphs = function(column)
      % -- second returned value signalize whether cell contain paragraph, or not 
          local getCell = odsreader.get_cell
      if type(column) =="table" then 
        return getCell(column, columnbreak), true
      end
      return getCell(column,""), false
    end
    local rowValues = function(row, headings)
      local headings = headings or {}
      local t={} 
      local i = 1
      for _,column in pairs(row) do 
        local attr = column.attr or {}
        local value, br  = concatParagraphs(column.value) 
        value = value or ""
        local x = attr["table:number-columns-spanned"] or "1"
        x = tonumber(x)
        if x > 1 then 
           value = odsreader.interp(multicoltpl, {value = value, count = x})
        else
          value = odsreader.interp(celltpl, {value = value})
        end
        % table.insert(t,value)
        t[i] = value
        headings[i] = br
        i = i + x
      end
      return t, headings
    end
    local makeColtypes = function(h)
      local maxsize = tex.hsize / 65536
      local h = h or {}
      local p = 0
      for _, c in pairs(h) do
        if c then 
          p = p + 1 
        end 
      end
      if p > 0 then
        local j = {}
        local size = tostring(math.floor(maxsize / \#h)) .. "pt"
        for _, c in pairs(h) do 
          local k = "l"
          if c then k = "p{"..size.."}" end
          j[\#j+1]= k
        end
        return table.concat(j)
      else 
        return string.rep("l",\#h)
      end
    end
    if rowtemplate == nil then
      local headings = {}
      local currow = {}
      currenttemplate = currenttemplate or "default"
      content = {}   
      for i,row in pairs(values) do
        currow, headings = rowValues(row, headings)
        table.insert(content,table.concat(currow," & "))
      end
      %-- Column headings handling
      local colheading=""
      if type(columns) == "number" and columns == 1 then
        columns = rowValues(values[1])
        content = odsreader.table_slice(content,2,nil)
      elseif type(columns) == "number" and columns == 2 then
        local t = odsreader.tableValues(body,real_range[1],1,real_range[3],2)
        columns = rowValues(t[1])
      end  
      if type(columns) == "table" then colheading = table.concat(columns," & ") .. odsnl .. " " end
      % coltypes = ""
      if type(content)== "table" then 
        % coltypes= string.rep("l",\#content[1]) 
        if not coltypes then
          coltypes = makeColtypes(headings)
        end
      end
      content = table.concat(content, odsnl .. " " ..rowseparator) .. odslastnl
      local result = odsreader.interp(templates[currenttemplate],{content=content,coltypes=coltypes,colheading=colheading,rowsep=rowseparator})
      print(result)
      tex.sprint(result)
    else
      local content = {}  
      currenttemplate = currenttemplate or "empty"
      for _,row in pairs(values) do
        table.insert(content,odsreader.interp(rowtemplate,rowValues(row)))
      end
      content = table.concat(content,rowseparator) 
      local result = odsreader.interp(templates[currenttemplate],{content=content,coltypes=coltypes,colheading=colheading,rowsep=rowseparator})
      print(result)
      tex.sprint(result)
    end
  }%
}%

\tabletemplate{empty}{-{content}}

\tabletemplate{default}{-{colheading}-{rowsep}-{content}}

\tabletemplate{booktabs}{%
\begin{tabular}{-{coltypes}}
\toprule
-{colheading}
\midrule
-{content}
\bottomrule
\end{tabular}
}

% Interface for adding of new rows

\newenvironment{AddRow}[1][]{%
\def\AddString##1##2{% 
\luaexec{%
local pos = "\luatexluaescapestring{##2}"%
if pos == "" then pos = nil end; row:addString("\luatexluaescapestring{\unexpanded{##1}}",nil,pos)%
}%
}%
\def\AddNumber##1##2{%
\luaexec{%
local pos = "\luatexluaescapestring{##2}"%
if pos == "" then pos = nil end; row:addFloat("\luatexluaescapestring{##1}",nil,pos)%
}%
}%
\luaexec{%
pos = "\luatexluaescapestring{#1}"%
if pos == "" then pos = nil end; row = odsreader.newRow()%
}%
}{%
\luaexec{%
body = body or odsreader.getTable(odsfile)
row:insert(body,pos)%
% we must save the updated table to the original table
odsfile.root["office:document-content"]["office:body"]["office:spreadsheet"]["table:table"] = body%
}%
}

% Interface for saving the spreadsheet

\newcommand\savespreadsheet{%
\luaexec{%
 local xml = require("luaxml-mod-xml")
 f = io.open("content.xml","w")%
 f:write(xml.serialize(odsfile.root))%
 f:close()%
 odsreader.updateZip(odsfilename,"content.xml")%
}%
}

% support for hyperlinks in cells

\newcommand\odslink[2]{\texttt{#2}}

\AtBeginDocument{%
    \@ifpackageloaded{hyperref}{%
        \renewcommand\odslink[2]{\href{#1}{#2}}%
    }{}
}
\endinput

다음과 같은 방법으로 사용할 수 있습니다.

\includespread[file=data.ods,range=a1:b3,template=tblr, newline={\\},lastnewline={\\}]

결과는 다음과 같습니다.

여기에 이미지 설명을 입력하세요

관련 정보