Pandoc 通用文档转换服务化指南:pandoc-server HTTP API 与 PandocPure 安全模型深度解析 Pandoc 通用文档转换服务化指南pandoc-server HTTP API 与 PandocPure 安全模型深度解析【免费下载链接】pandocUniversal markup converter项目地址: https://gitcode.com/gh_mirrors/pa/pandocpandoc-server是 Pandoc 官方提供的 HTTP 服务化组件它将 pandoc 强大的文档转换能力Markdown、LaTeX、docx、epub、ipynb 等上百种输入输出格式封装为 REST 风格的 JSON API既可作为一个常驻 Web 服务运行也可作为 CGI 程序挂在任意 Web 服务器之后。本文基于本仓库的 pandoc-server/README.md、完整 API 手册 doc/pandoc-server.md 与核心实现 pandoc-server/src/Text/Pandoc/Server.hs系统讲解它的两种运行形态、PandocPure安全模型、全部请求字段与端点协议并给出可直接运行的 curl 调用示例读完即可在自己的项目里搭建一个文档转换后端。一、pandoc-server 是什么库与可执行程序的双重身份pandoc-server是一个 Haskell 库cabal 包名为pandoc-server版本 0.1.4见 pandoc-server/pandoc-server.cabal提供以 HTTP 服务器形式访问 pandoc 文档转换功能的能力。它的 synopsis 写得很直白Pandoc document conversion as an HTTP servant-server。与此同时pandoc-server又是一个可执行程序名。在 CLI 入口 pandoc-cli/src/pandoc.hs 中程序会根据调用时的进程名分发到不同模式进程名为pandoc-server时执行runServerHTTP 常驻服务进程名为pandoc-server.cgi时执行runCGICGI 程序普通pandoc进程收到server子命令pandoc server时同样进入runServer。也就是说同一个二进制通过改名或子命令即可切换运行形态这也是文档中可以用作运行中的服务器也可以用作 CGI 程序的依据。二、两种运行形态HTTP 常驻服务与 CGI1. 作为 HTTP 服务器运行pandoc-server基于 WarpHaskell 高性能 HTTP 服务器与 Servant类型安全的 Web API 框架。CLI 侧的启动逻辑位于 pandoc-cli/server/PandocCLI/Server.hsrunServer :: [String] - IO () runServer args do sopts - parseServerOptsFromArgs args hPutStrLn stderr $ Starting server on port show (serverPort sopts) ... Warp.run (serverPort sopts) (timeout (serverTimeout sopts) app)可见实际对外服务的是Text.Pandoc.Server模块导出的app一个 WAIApplication并用timeout中间件包裹超过时限的转换请求会被强制终止。支持的启动参数来自 doc/pandoc-server.md 的 OPTIONS 一节以及 Server.hs 中cliOptions的实现参数说明默认值--port NUM或-p NUMHTTP 监听端口3030--timeout SECONDS或-t SECONDS单个转换的超时秒数超时后转换被杀死2--help或-h打印帮助信息—--version或-v打印版本号—启动示例pandoc-server --port 8080 --timeout 5 # 或等价的子命令形式 pandoc server --port 80802. 作为 CGI 程序运行把可执行文件重命名或符号链接为pandoc-server.cgi即可由 Apache、nginx配合 CGI 模块等 Web 服务器按 CGI 协议调用。此时每次请求都会拉起一个进程处理无需常驻端口。CGI 模式的实现同样在 pandoc-cli/server/PandocCLI/Server.hsrunCGI :: IO () runCGI do cgiTimeout - maybe 2 (readDef 2) $ lookupEnv PANDOC_SERVER_TIMEOUT CGI.run (timeout cgiTimeout app)要点CGI 模式下无法使用--port超时改由环境变量PANDOC_SERVER_TIMEOUT控制默认 2 秒若使用符号链接方式部署需要调整 Web 服务器的配置以允许 CGI 脚本跟随符号链接。三、安全模型PandocPure 单体与它的边界pandoc-server最重要的设计决策是所有 pandoc 转换都运行在PandocPure单体中。PandocPure是一个纯函数式、无 I/O 能力的求值环境从根上保证了服务器端不可能发生任意的文件系统或网络访问。代码中的注释Server.hs明确写道We use runPure for the pandoc conversions, which ensures that they will do no IO. This makes the server safe to use. However, it will mean that features requiring IO, like RST includes, will not work.也就是说把转换放进runPure换来了高安全性但也带来以下明确限制限制说明不能生成 PDFPDF 输出依赖外部 LaTeX 引擎等 I/O 操作在PandocPure中不可用不支持过滤器filters无论是 Lua 还是外部可执行过滤器都无法在纯环境中运行不能通过 HTTP 获取资源文档中引用的网络资源无法自动抓取外部资源必须显式随请求提交文档转换所需的图片、include 文件、参考文档等必须通过请求中的files字段见下文一并提供这些限制反过来定义了一条铁律凡是转换过程中可能读取的外部文件都必须放进files字段由客户端显式上传。源码中的convert函数会把这些文件写入一个虚拟文件树FileTree供 reader/writer 在纯环境中读取let addFile :: FilePath - Blob - FileTree - FileTree addFile fp (Blob lbs) insertInFileTree fp FileInfo{ infoFileMTime curtime , infoFileContents BL.toStrict lbs } ... Just fs - do let filetree M.foldrWithKey addFile mempty fs modifyPureState $ \st - st{ stFiles filetree }代码位置见 Server.hs。四、库嵌入五行的最小 Haskell 服务如果你正在编写 Haskell 程序可以直接把Text.Pandoc.Server当作库嵌入。README 给出了最小示例——只需引入app并用 Warp 启动module Main where import Text.Pandoc.Server (app) import qualified Network.Wai.Handler.Warp as Warp main :: IO () main Warp.run 3000 appapp的类型是 WAI 的Application因此任何 WAI 中间件CORS、日志、限流等都可以叠加在它外层。实际上源码本身也叠加了一个 CORS 中间件app corsWithContentType $ serve api serverServer.hs它允许跨域请求携带Content-Type头方便浏览器端 JavaScript 直接调用。库对外暴露的模块接口包括app、API、ServerOpts(..)、Params(..)、Blob(..)与parseServerOptsFromArgs。从 cabal 依赖pandoc-server/pandoc-server.cabal可以看到技术栈servant-server、wai、warp由 CLI 层引入、wai-cors、aeson、base64-bytestring、doctemplates、skylighting等与pandoc 3.11配套。五、API 详解根端点/根端点/只接受POST请求。请求体是一个 JSON 对象text字段是唯一必填项其余字段均可省略省略时取默认值多个字符串候选中第一个为默认值。1. 响应格式响应内容的媒体类型按Accept请求头依次协商application/octet-streamtext/plainapplication/json若目标格式是二进制如epub、docx而响应以纯文本或 JSON 返回二进制内容会被base64 编码。JSON 响应有两种形态失败时{ error: string with the error message }成功时{ output: string with textual or base64-encoded binary output, base64: boolean (true means the \output\ is base64-encoded), messages: array of message objects (see below) }messages数组中的每个元素格式为{ message: string, verbosity: string (either \WARNING\ or \INFO\) }在源码中这三种响应路径对应 Servant API 类型Server.hs中的三个并行的 POST 分支OctetStream返回原始字节、PlainText返回文本、JSON返回Output结构。Output的 JSON 编码在 Server.hs 中定义成功返回output/base64/messages三元组失败返回error字符串。2. 请求字段全表以下为 doc/pandoc-server.md 中定义的全部请求字段按功能分组整理基础转换字段类型/取值默认值说明textstring—待转换的文档内容若from是二进制格式如 epub、docx此处应为该文档的 base64 编码fromstringmarkdown输入格式可带扩展名写法与 pandoc 命令行一致tostringhtml输出格式可带扩展名写法与 pandoc 命令行一致shift-heading-level-byinteger0将所有标题级别整体增减indented-code-classesarray of strings—应用于 Markdown 缩进代码块的 class 列表default-image-extensionstring—给无扩展名的图片源补上的扩展名如.jpgmetadataJSON map—字符串值形式的元数据tab-stopinteger4制表位宽度每 Tab 对应的空格数track-changesaccept|reject|all—处理 MS Word 修订Track Changes产生的插入、删除与批注仅影响 docx 输入abbreviationsfile path—解析 Markdown 时视为缩写的字符串列表详见pandoc(1)的--abbreviationstypst-inputsJSON map—提供给 Typst 求值器的输入详见pandoc(1)的--typst-input模板与独立文档字段类型/取值默认值说明standalonebooleanfalse为 true 时产出独立文档使用默认模板或template指定的自定义模板为 false 时产出片段templatestring—文档模板的字符串内容格式见pandoc(1)的 Templates 章节variablesJSON map—在模板中插值的变量见pandoc(1)的 Templates 章节title-prefixstring—加到 HTML 头部标题上的前缀文本排版字段类型/取值默认值说明dpiinteger96像素与其他度量单位换算使用的每英寸点数用于图片尺寸wrapauto|preserve|none—换行策略auto按列宽自动硬换行preserve保留源中的换行none不插入任何多余换行columnsinteger72列宽影响文本换行与纯文本格式表格列宽计算目录与编号字段类型/取值默认值说明table-of-contentsbooleanfalse在支持的格式中包含目录toc-depthinteger3目录中包含的章节深度list-of-figuresbooleanfalse在支持的格式中包含图表列表list-of-tablesbooleanfalse在支持的格式中包含表格列表number-sectionsbooleanfalse在支持的格式中自动为章节编号number-offsetarray of integers—加到章节号各分量上的偏移。例如[1]使第一节编号为 2、第一小节为 2.1[0,1]使第一节为 1、第一小节为 1.2HTML 与 Markdown 输出字段类型/取值默认值说明strip-commentsbooleanfalse剥离 Markdown/Textile 源中的 HTML 注释而不是透传到输出syntax-highlightingdefault|none|idiomatic|style—语法高亮方式指定某个 style 时使用内部高亮引擎KDE 语法定义与样式idiomatic优先使用格式自带高亮器none关闭所有高亮default使用格式相关的默认方式。标准样式有pygments默认、kate、monochrome、breezeDark、espresso、zenburn、haddock、tango也可给 KDE 语法主题.theme文件的路径该文件内容须放入files。HTML、EPUB、Docx、Ms、Man、LaTeX 默认使用内部高亮器Typst 默认依赖 Typst 自身的高亮系统embed-resourcesboolean—用dataURI 把图片、脚本、样式等资源嵌入 HTML前提是外部资源内容都已放入fileshtml-q-tagsbooleanfalseHTML 中使用q元素而非字面引号asciibooleanfalse尽可能使用实体与转义避免输出非 ASCII 字符reference-linksbooleanfalseMarkdown 输出中使用引用式链接而非内联链接reference-locationdocument|section|block—决定链接引用与脚注放在文档末尾、章节末尾还是块如段落末尾见pandoc(1)的--reference-locationsetext-headersbooleanfalseMarkdown 输出中使用 Setext下划线式标题而非 ATX#前缀式section-divsbooleanfalse按标题将文档组织为嵌套的 section 层级email-obfuscationnone|references|javascript—HTML 中电子邮件地址的混淆方式identifier-prefixstring—加到所有自动生成标识符上的前缀LaTeX / 幻灯片 / 数学字段类型/取值默认值说明top-level-divisiondefault|part|chapter|section—LaTeX、ConTeXt、DocBook、TEI 中顶级标题的解释方式default基于启发式自动选择math-methodplain|webtex|gladtex|mathml|mathjax|katex—HTML 中数学公式的表示方式listingsbooleanfalseLaTeX 输出中使用listings宏包排版代码incrementalbooleanfalse为 true 时幻灯片中的列表默认逐条渐进显示slide-levelinteger—幻灯片分页的标题级别默认取存在正文文本之下的最高标题级别cite-methodciteproc|natbib|biblatex—LaTeX 输出中引文的排版方式EPUB 相关字段类型/取值默认值说明reference-docfile path—创建docx/odt/pptx使用的参考文档见pandoc(1)的--reference-doc文件内容必须放入filessplit-levelinteger1EPUB 或 chunked HTML 中拆分文档的标题级别epub-cover-imagefile path—EPUB 封面图文件内容必须放入filesepub-metadatafile path—含 Dublin core XML 元素的元数据文件路径文件内容必须放入filesepub-subdirectorystringEPUBEPUB 容器中内容子目录名epub-fontsarray of file paths—要嵌入 EPUB 的字体字体文件本身必须放入filesipynb 与引文字段类型/取值默认值说明ipynb-outputbest|all|none—ipynb 输出单元格的处理方式all保留原始全部数据格式none省略数据单元格内容best为每个输出单元格挑选与目标格式最兼容的最丰富数据块citeprocbooleanfalse使用 citeproc 处理引文见pandoc(1)的 Citations 章节bibliographyarray of file paths—含文献数据的文件文件内容必须放入filescslfile path—CSL 样式文件文件内容必须放入files文件资源字段类型/取值默认值说明filesJSON map文件路径 → base64 字符串—转换所需的全部文件包括文档源中引用的图片。二进制数据必须 base64 编码文本数据可保持原样——除非它本身恰好也是合法的 base64 数据此时会被当作 base64 解释3. 一个完整的调用示例把 Markdown 转为独立 HTML 并带目录curl -X POST http://localhost:3030/ \ -H Content-Type: application/json \ -H Accept: application/json \ -d { text: # 标题\n\n你好世界。\n\npython\nprint(1)\n\n, from: markdown, to: html, standalone: true, table-of-contents: true, number-sections: true, syntax-highlighting: kate }响应示例成功{ output: nav id\TOC\ role\doc-toc\.../navh1>curl -X POST http://localhost:3030/ \ -H Content-Type: application/json \ -d { text: 图, to: html, files: { img.png: 图片文件的 base64 编码 } }4. 二进制输入输出的处理两个方向的 base64 约定需要同时注意输入from为二进制格式时text字段放文档的 base64 编码输出to为二进制格式且响应走纯文本/JSON 通道时output字段返回 base64 编码并置base64: true。底层实现里二进制 reader 会先把 base64 解码再解析Server.hsByteStringReader r - r readeropts . BL.fromStrict . Base64.decodeLenient . UTF8.fromText二进制 writer 的结果则由bsHandler重新 base64 编码后交给客户端。六、其余端点/batch、/version、/babelmark根端点之外还有三个专用端点它们与根端点一起组成了 Servant 中声明的完整API类型Server.hs。1./batch批量转换/batch的行为与根端点一致只有两点区别请求体是一个 JSON数组每个元素都是根端点所需的 JSON 对象响应是一个 JSON 数组每个元素是对应请求的转换结果。源码中它直接复用convertJSON并以mapM批量执行batch : ReqBody [JSON] [Params] : Post [JSON] [Output]。适合在单次请求中转换一批小片段如富文本编辑器里的多个剪贴片段。2./version版本查询/version接受 GET 请求根据Accept头返回纯文本或 JSON 编码的 pandoc 版本字符串。响应内容直接来自pandocVersionText/pandocVersion。3./babelmarkBabelmark 兼容接口/babelmark接受 GET 请求查询参数如下参数类型默认值textstring必填—fromstringmarkdowntostringhtmlstandalonebooleanfalse它返回带html与version两个字段的 JSON 对象专为支持 Babelmark 类 Markdown 对比评测网站而设计。其实现内部其实就是一次简化版的 Markdown→HTML 转换Server.hs只是把结果包装成{html: ..., version: ...}。七、源码视角一次请求的完整处理流水线convert函数Server.hs串起了从请求到响应的完整流程理解它对排障很有帮助装载虚拟文件系统把files中的每个条目路径 → Blob插入FileTree写入纯状态stFiles使后续 reader/writer 能读到这些文件解析格式用parseFlavoredFormat解析from/to再经getReader/getWriter取得对应的 reader/writer 及其默认扩展集确定高亮方式optSyntaxHighlighting映射到NoHighlighting/IdiomaticHighlighting/DefaultHighlighting或具体 Skylighting 样式编译模板仅当standalone为真时编译模板——未指定template时用compileDefaultTemplate取内置默认模板指定时用compileCustomTemplate支持runWithPartials与自定义模板 partials读取缩写表abbreviations未指定时从内置数据文件读取默认缩写表指定时从虚拟文件系统中的文件读取组装 reader/writer 选项把 JSON 字段逐一映射到ReaderOptions与WriterOptions例如tab-stop→readerTabStop/writerTabStop、dpi→writerDpi、wrap→writerWrapText、columns→writerColumns等见 Server.hs执行转换reader 解析 → 应用文档变换标题平移headerShift、东亚换行过滤、ipynb 输出过滤→ 合并元数据bibliography/csl/abbreviations 会以setMeta方式写入→ 按需processCitations→ writer 输出 → 若embed-resources且目标是 HTML 则调用makeSelfContained嵌入资源错误处理纯文本/字节通道出错时返回 HTTP 500 并携带错误消息体JSON 通道出错则返回{error: ...}结构。从结构上看所有请求选项最终都落在同一个Opt记录Text.Pandoc.App.Opt上——Params内部就持有options :: Opt而FromJSON/ToJSON实例直接复用Opt的 JSON 编解码Server.hs这也是请求字段与 pandoc 命令行选项一一对应的根本原因。八、测试与文档资源如果你想进一步验证或深入完整 API 手册doc/pandoc-server.mdman page 源文件编译后的 man pagepandoc-cli/man/pandoc-server.1核心实现pandoc-server/src/Text/Pandoc/Server.hsCLI 集成pandoc-cli/server/PandocCLI/Server.hsHTTP/CGI 两种启动路径构建配置与依赖pandoc-server/pandoc-server.cabal。九、许可pandoc-server© 2006-2024 John MacFarlane以 GPL 2.0 或更高版本发布详见 pandoc-server/COPYING.md 与本仓库根目录的 COPYRIGHT不附带任何形式的担保。【免费下载链接】pandocUniversal markup converter项目地址: https://gitcode.com/gh_mirrors/pa/pandoc创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考