Markdown中的普通表


降价表令人毛骨悚然:


  1. 您在单元格中写的文本不能超过几个单词,甚至不能少于一个列表。
  2. 如果方言允许使用第1款,则格式不便。
  3. 如果单元格未对齐,则无法读取表格。
  4. 不支持相同类型和自动化的表,例如行号。

现在该为Pandoc编写过滤器了,该过滤器从结构化的YAML中绘制表,并具有行编号,水平方向,图形模式,同时弄清楚如何编写Lua过滤器。


我通常使用Markdown编写文本,然后使用Pandoc转换为目标格式。 这是一个可以在格式之间转换文档的程序,例如,您可以从Markdown中获取HTML,MD,DOCX和PDF的另一种方言(超过30种输入和超过50种输出格式)。 Pandoc Markdown具有许多方便的扩展,用于链接,脚注,签名,公式。


Pandoc是功能的组合(可以用Haskell编写):特定的输入格式→文档的抽象表示→特定的输出格式。 可以使用Lua编写的过滤器来修改抽象表示。 过滤器不需要了解输出格式,但是可以将其考虑在内。


我们的过滤器将以抽象表示形式在条件语言table中查找代码的抽象块,在其中读取YAML并生成Pandoc自身将以目标格式生成的表的抽象表示形式。


 pandoc --lua-filter table.lua input.md -o output.html 

有哪些替代方案,为什么会更糟?


  • HTML表仅在Markdown中工作,并且只能转换为HTML; 仅解决了单元格中格式丰富的问题​​。
  • 表生成器需要从文本编辑器切换;不方便编辑其中的单元格内容( 示例 )。
  • 编辑器插件Emacs Org-ModeVIM插件 )不是通用的,并非始终可用。

相反, pandoc-crossref和所有Pandoc包都与汇总表过滤器一起使用。 通过指定适当的输出格式,该过滤器还可用于生成标准的Markdown表。 缺点:


  • 单元无法合并; Pandoc不支持此功能(尚未)。
  • 对于水平表,必须使用输出格式(例如,通过CSS)进行样式化。

该表的描述包括三个部分:


  1. 表结构


    图(列)的有序列表:


    • 该列至少应有一个标题。
    • 为了能够在不触摸数据的情况下重新排列列,必须指定列( id )中显示的记录属性。
    • 特殊列没有ID,但是有关于如何填充它们的描述。 首先,您需要一个序列号( special: number )。
    • 列对齐( align )。

    而且,桌子可以是垂直的或水平的( orientation )。 在后一种情况下,图形将是行。


  2. 表格属性:链接的ID( id )和签名( caption )。 Pandoc允许您签署表,但不能签署代码块。


  3. YAML字典数组形式的数据



该结构对于多个表可能是通用的,因此您既可以直接与表一起描述它,也可以在元数据中(一次)描述它,然后引用命名的模板。


实施计划:


  1. 根据文档的元数据,我们形成模板字典。


  2. 对于带有类table每个代码块:


    1. 我们解析YAML表。
    2. 如果指定了模板,则从字典中获取模板,否则从YAML填写模板。
    3. 我们从YAML填写表格的各个属性。
    4. 我们从YAML形成表格条目(记录是常规表格中的一行或水平表格中的一列)。
    5. 我们根据模板,属性和记录“绘制”表格。


上层是以书面形式实现的(所有代码都可以在文章结尾的链接上找到):


 function Pandoc(doc) local meta_templates = doc.meta['table-templates'] if meta_templates then for name, value in pairs(meta_templates) do templates[name] = parse_template(value) end end local blocks = pandoc.walk_block(pandoc.Div(doc.blocks), { CodeBlock = create_table }) return pandoc.Pandoc(blocks, doc.meta) end 

parse_template()函数会稍微转换元数据格式。 Pandoc将它们的值表示为MetaBlockMetaInlinepandoc.utils.stringify()函数(例如,方向)或视觉元素(例如,列标题中的一段文本)来组成简单的行。


关于调试。 Pandoc文档中有很多示例,但是类型不是很详细。 对于调试过滤器,使用变量转储功能很方便。 严肃的库打印了太多的细节,我更喜欢简单的选项之一


将元数据转换为文档元素的功能
 local function to_inlines(content) if content == nil then return {} elseif type(content) == 'string' then return {pandoc.Str(content)} elseif type(content) == 'number' then return to_inlines(tostring(content)) elseif content.t == 'MetaInlines' then inlines = {} for i, item in ipairs(content) do inlines[i] = item end return inlines end end local function to_blocks(content) if (type(content) == 'table') and content.t == 'MetaBlocks' then return content else return {pandoc.Plain(to_inlines(content))} end end 

在三元backtics中为每个代码块调用create_table()函数。


我们只对table “以语言显示”的代码块感兴趣:


 if not contains('table', block.classes) then return block end 

为了在代码块中解析YAML,我们创建一个仅包含YAML元数据的文档,使用Pandoc对其进行解析,仅保留元数据:


 local meta = pandoc.read('---\n' .. block.text .. '\n---').meta 

接下来,从meta读取到模板或表结构和特定表的属性的链接。


fill_table()函数从meta数据中读取图形说明中指定的属性。 在同一阶段,如果将该列标记为特殊,则会生成其内容:


 local data = {} for i, serie in ipairs(template.series) do if serie.special == 'number' then data[i] = to_blocks(#datum + 1) else data[i] = to_blocks(item[serie.id]) end end 

format_table()函数根据表的方向形成单元格的结果数组,并创建一个抽象表对象。 应该注意的是,如果应该为所有列设置宽度或标题,或者为所有列设置宽度或标题,否则Pandoc不会简单地创建表。


可以将完成的脚本放在~/.local/share/pandoc~/.local/share/pandoc 数据目录 )中,以从任何位置按名称访问它。


聚苯乙烯


关于考虑输出格式过滤器。 例如,我在Pandoc中编写扰流器,如下所示:


 ::: {.spoiler title=""}  . ::: 

Pandoc文档模型中没有破坏者,因此过滤器应大致按照以下方式生成原始块。 当然,实际代码( spoiler.lua )应该通过FORMAT变量考虑输出格式,而不是机械地考虑:尽管输出格式是markdown,但下面的片段会生成HTML原始块。


 function Div(el) if not el.attr or not contains('spoiler', el.attr.classes) then return el end local title = el.attr.attributes['title'] or '' table.insert(el.content, 1, pandoc.RawBlock('html', '<' .. 'spoiler title="' .. title .. '">', 'RawBlock')) table.insert(el.content, pandoc.RawBlock('html', '<' .. '/spoiler>', 'RawBlock')) return el.content end 

参考文献


Source: https://habr.com/ru/post/zh-CN474826/


All Articles