我有一个列表(不是表格),我想将其排版为用于归档标签的网格。该列表采用类似 CSV 的格式,并带有注释,其中相似的条目被分组到一行中。
\startseparatedlist[NaturalTable]
One,One.Two,One.Three
Two
Three
#Four
Five
\stopseparatedlist
标签尺寸固定:2 英寸 x 0.5 英寸:
\startsetups labels
\setupTABLE[row][each,width=2in,height=0.5in,align={middle,lohi}]
\stopsetups
排版要求:
- 每行的表格单元格基于
\makeupwidth / labelwidth
而不是 CSV 列。使用该database
模块需要一些状态来跟踪何时添加\eTR
/\bTR
命令。 - 标签应可折叠,并带有虚线。我的想法是每个标签两行。顶行的底部框架是虚线(如何:MetaPost?)并且单元格是空的。底行包含实际内容。
- 按行主序排列。
这是一个最小的非工作示例:
从...来:
% Width should control the cells per row.
\setuplayout
[ backspace=0.75in
, width=7in
]
\definemagiccommand[NaturalTable]
[ width=2
, height=1
, ...=..., === % Maybe inherit from or wrap \defineseparatedlist?
]
\magiccommand[NaturalTable]
One,One.Two,One.Three
Two
Three
#Four
Five
Six
\stopmagiccommand
到:
请注意,最终结果中的所有线条都是相同的粗细。我通过手动删除 MetaFun 中的重叠线条来实现这一点,这并不好玩(而且仍然不完美,请参见右下角)。
这是我目前所拥有的。问题非常明显,我为每个问题创建了一个 MWE。
\startuseMPgraphic{dottedBottom}
draw bottomboundary OverlayBox withpen pencircle scaled \frameddimension{rulethickness} dashed withdots;
setbounds currentpicture to boundingbox OverlayBox;
\stopuseMPgraphic
\defineoverlay[dottedBottom][\useMPgraphic{dottedBottom}]
\startsetups label
\setupTABLE[frame=off,width=2in,height=0.5in]
\setupTABLE[row][each][align={middle,lohi}]
\setupTABLE[row][first][background={dottedBottom}]
\stopsetups
\define[1]\framedlabel{%
\framed[strut=no,offset=0pt]{%
\bTABLE[setups=label,split=yes]%
\bTR \bTD \eTD \eTR%
\bTR \bTD #1 \eTD \eTR%
\eTABLE%
}\hskip 0pt }
\vbox{
\rightskip 0pt plus 1fil
\setupinterlinespace[off]
\leavevmode
\framedlabel{One}
\framedlabel{Two}
\framedlabel{Three}
\framedlabel{Four}
\framedlabel{Five}
\framedlabel{Six}
}
\usemodule[database]
\define\framedlabelfor{
%TODO
}
\defineseparatedlist[LabelsCSV][separator=comma,command=\framedlabelfor]
\definestartstop[Labels]
[ before={\vbox\bgroup\rightskip 0pt plus 1fil\setupinterlinespace[off]\leavevmode\startLabelsCSV}
, after={\stopLabelsCSV\egroup}
]
如何避免出现重复线:
\setuplayout
[ backspace=0.75in
, width=7in
]
\vbox{
\rightskip 0pt plus 1fil
\setupinterlinespace[off]
\leavevmode
\framed[height=1in,width=2in]{001}\hskip0pt
\framed[height=1in,width=2in]{002}\hskip0pt
\framed[height=1in,width=2in]{003}\hskip0pt
\framed[height=1in,width=2in]{004}\hskip0pt
\framed[height=1in,width=2in]{005}\hskip0pt
\framed[height=1in,width=2in]{006}\hskip0pt
\framed[height=1in,width=2in]{007}\hskip0pt
}
编辑\frameoffset
:我通过将设置为的一半解决了双线问题\rulethickness
。不幸的是,现在有明显的故障,2 线和 3 线交叉点中无对立线略微延伸到交叉点之外。请参阅附图。这些是舍入误差吗 - 它们只发生在小于的偏移处1pt
?我该如何摆脱它们?
\vbox{
\rightskip 0pt plus 1fil
\setupinterlinespace[off]
\leavevmode
\framed[height=1in,width=2in,rulethickness=1pt,frameoffset=0.5pt]{001}\hskip0pt
\framed[height=1in,width=2in,rulethickness=1pt,frameoffset=0.5pt]{002}\hskip0pt
\framed[height=1in,width=2in,rulethickness=1pt,frameoffset=0.5pt]{003}\hskip0pt
\framed[height=1in,width=2in,rulethickness=1pt,frameoffset=0.5pt]{004}\hskip0pt
\framed[height=1in,width=2in,rulethickness=1pt,frameoffset=0.5pt]{005}\hskip0pt
\framed[height=1in,width=2in,rulethickness=1pt,frameoffset=0.5pt]{006}\hskip0pt
\framed[height=1in,width=2in,rulethickness=1pt,frameoffset=0.5pt]{007}\hskip0pt
}
以及如何创建 varargs 命令:
\define\foreach{
% TODO
}
\foreach{001}
\foreach{001}{002}
答案1
这command
database
模块示例抛出 Lua 错误,所以我放弃了 varargs 宏问题,自己用 Lua 处理缓冲区。中途我意识到我更喜欢 Lua 表语法而不是 CSV 语法,所以提供了两种方法。第一种方法是混合使用 Lua/ConTeXt 来处理 CSV。另一种是 Lua-only,适用于将数据存储在 Lua 中的情况。
这些字段是单独处理的,这使得它不适合表格数据,不像database
在记录级别工作的模块。支持嵌套数据就像修改函数一样简单csvforeach
,例如,如果我想添加仅在索引中可见的描述字段。使用 Lua 表的主要优势是将格式与结构分离。
\setuppapersize[letter]
% text heigh/width: 9in/7in
\setuplayout
[ topspace=0.5in
, height=10in
, header=0.5in
, footer=0.5in
, backspace=0.75in
, width=7in
, leftmargindistance=0.15in
, leftmargin=0.6in
, rightmargindistance=0.15in
, rightmargin=0.6in
]
\setupcolors[state=start]
\startluacode
userdata = userdata or {}
function userdata.parsecsv(s)
local field = '"' * lpeg.Cs(((1 - lpeg.P'"') + lpeg.P'""'/'"')^0) * '"'
+ lpeg.C((1 - lpeg.S'",\r\n')^0)
local record = lpeg.Ct(field * ("," * field)^0)
local file = lpeg.Ct(record * (lpeg.S"\r\n" * record)^0 * (lpeg.P"\r\n" + -1))
return lpeg.match(file,s)
end
function userdata.csvforeach(t,c)
local i = 0
for r,v in ipairs(t) do
for f,v in ipairs(v) do
i = i + 1
if type(c) == "function" then
c(i,v)
else
context("%s{%s}{%s}",c,i,v)
end
end
end
end
\stopluacode
\define[2]\csvforeach{%
\ctxlua{%
userdata.csvforeach(userdata.#1,"\luaescapestring{\normalunexpanded{#2}}")}}
\def\startcsv{\dosingleempty\dostartcsv}
\def\dostartcsv[#1]%
{\iffirstargument
\def\csvName{#1}
\else
\def\csvName{csv}
\fi
\dostartbuffer
[\csvName]
[startcsv]
[stopcsv]}
\def\stopcsv%
{\ctxlua
{userdata.\csvName = userdata.parsecsv(buffers.getcontent("\csvName"))}}
\startuseMPgraphic{dottedBottom}
draw bottomboundary OverlayBox withpen pencircle scaled \frameddimension{rulethickness} dashed withdots;
setbounds currentpicture to boundingbox OverlayBox;
\stopuseMPgraphic
\defineoverlay[dottedBottom][\useMPgraphic{dottedBottom}]
\startsetups label
\setupTABLE[frame=off,width=2in,height=0.5in]
\setupTABLE[row][each][align={middle,lohi}]
\setupTABLE[row][first][background={dottedBottom}]
\stopsetups
\startsetups labelframe
\setupframed[strut=no,offset=0pt,rulethickness=1pt,frameoffset=0.5pt]
\stopsetups
\setuphead[title][
align=middle,
after={\hairline\blank[4*big]}
]
\setupcolumns[n=3,separator=rule,balance=yes,distance=0.5in]
\define[2]\framedlabel{%
\setup{labelframe}%
\framed{%
\bTABLE[setups=label,split=no]%
\bTR \bTD \eTD \eTR%
\bTR \bTD \startitemize \sym{#1.} #2 \stopitemize \eTD \eTR%
\eTABLE%
}\hskip 0pt }
\define[2]\labelitem{\sym{#1.} #2}
\startcsv
Of,course
,it is!
\stopcsv
\starttext
\vbox{
\rightskip 0pt plus 1fil
\setupinterlinespace[off]
\leavevmode
\csvforeach{csv}{\framedlabel}
}
\title{Index}
\resetcounter[userpage]
\startcolumns
\startitemize
\csvforeach{csv}{\labelitem}
\stopitemize
\stopcolumns
\page[yes]
% If you prefer Lua, might as well stay in Lua.
\startluacode
local sp = 65536
context.vbox(function()
tex.setglue("rightskip", 0, sp, 0, 2, 0)
context.setupinterlinespace({"off"})
context.leavevmode()
userdata.csvforeach(userdata.csv,context.framedlabel)
end)
context.title("Index")
context.resetcounter({"userpage"})
context.startcolumns()
context.startitemize()
userdata.csvforeach(userdata.csv,context.labelitem)
context.stopitemize()
context.stopcolumns()
context.page({"yes"})
\stopluacode
% Now using Lua tables rather than CSV.
\define\eachtigpar{\EveryPar{%
\clubpenalties 5 10000 10000 10000 100 0%
\widowpenalties 5 10000 10000 10000 100 0%
}}
\defineitemgroup[tig]
[inner=\eachtigpar]
\define[2]\labelitem{\sym{#1.} #2}
\define[1]\labeldescription{\blank[medium]\page[no]#1}
\startluacode
local data =
{ "One", "Two"
, "Three"
, "Four"
,{"Five", [=[
This deserves an explanation. The explanation
is multiple lines long because of \ConTeXt.
\startitemize
\item Reason one.
\item Reason two.
\stopitemize
]=]}
, "six"
, "seven"
}
local function dedent(s)
local lx,fx = {},{}
for l in s:gmatch("[^\n]*") do
if l:match("^%s*$") then
table.insert(lx,"")
else
table.insert(lx,l)
table.insert(fx,l)
end
end
if #fx ~= 0 then
local i,s,c,n = 0,false
while true do
for _,v in pairs(fx) do
if #v - i == 0 then
s = true
break
end
n = string.sub(v,i+1,i+1)
if not n:match("^%s") then
s = true
break
end
if c == nil then
c = n
elseif c ~= n then
s = true
break
end
end
if s then
break
end
c = nil
i = i + 1
end
for k,v in pairs(lx) do
lx[k] = string.sub(v,i+1)
end
end
return table.concat(lx,"\n")
end
local function trim(s)
return s:match("^%s*(.-)%s*$")
end
local function tableforeach(t,c)
local i = 0
for _,v in ipairs(t) do
local d
i = i + 1
if type(v) == "table" then
v,d = unpack(v)
end
if d then
d = trim(dedent(d))
end
c(i,v,d)
end
end
local function tablelabel(i,v,d)
context.framedlabel(i,v)
end
local function tableindex(i,v,d)
context.labelitem(i,v)
if not d then
return
end
context.labeldescription(d)
end
local sp = 65536
context.vbox(function()
tex.setglue("rightskip", 0, sp, 0, 2, 0)
context.setupinterlinespace({"off"})
context.leavevmode()
tableforeach(data,tablelabel)
end)
context.title("Index")
context.resetcounter({"userpage"})
context.startcolumns()
context.starttig()
tableforeach(data,tableindex)
context.stoptig()
context.stopcolumns()
context.page({"yes"})
\stopluacode
\stoptext
它主要通过列和项组格式化来完成仍需要一些工作。