【pandoc API python】

使用 pandoc API
Pandoc 可以用作 Haskell 库,用于编写您自己的转换工具或为 Web 应用程序提供支持。本文档介绍了如何使用 pandoc API。

各个函数和类型级别的详细 API 文档可在https://hackage.haskell.org/package/pandoc上找到。

Pandoc的架构
Pandoc 的结构为一组reader和一组 writer,它们将各种输入格式转换为表示结构化文档的抽象语法树(Pandoc AST),而一组 writer则将此 AST 渲染为各种输出格式。如图所示:

[input format] ==reader==> [Pandoc AST] ==writer==> [output format]

该架构允许 pandoc 使用M 个读取器和N 个写入器执行M  ×  N转换。

Pandoc AST 在pandoc-types包中定义 。您应该首先查看 Haddock 文档中的Text.Pandoc.Definition。正如您将看到的,aPandoc 由一些元数据和 s 列表组成Block。有多种类型Block,包括Para(段落)、Header(章节标题)和 BlockQuote。其中一些 Block(如BlockQuote)包含 s 列表 Block,而其他(如Para)包含 s 列表Inline,还有一些(如CodeBlock)包含纯文本或不包含任何内容。Inlines 是段落的基本元素。Block和之间的区别Inline例如,在类型系统中,无法表示Inline链接文本为块引用 ( Block) 的链接 ( )。这种表达上的限制主要是一种帮助而不是一种障碍,因为 pandoc 支持的许多格式都有类似的限制。

探索 pandoc AST 的最佳方法是使用pandoc -t native,它将显示与某些 Markdown 输入相对应的 AST:

% echo -e "1. *foo*\n2. bar" | pandoc -t native
[OrderedList (1,Decimal,Period)
 [[Plain [Emph [Str "foo"]]]
 ,[Plain [Str "bar"]]]]

一个简单的例子
以下是使用 pandoc reader 和 writer 执行转换的简单示例:

import Text.Pandoc
import qualified Data.Text as T
import qualified Data.Text.IO as TIO

main :: IO ()
main = do
  result <- runIO $ do
    doc <- readMarkdown def (T.pack "[testing](url)")
    writeRST def doc
  rst <- handleError result
  TIO.putStrLn rst

一些注意事项:

第一部分构造一个转换管道:输入字符串传递给readMarkdown,doc然后由 渲染生成的 Pandoc AST ( ) writeRST。转换管道是由runIO以下人员“运行”的。

result有类型Either PandocError Text. 我们可以手动对此进行模式匹配,但在这种情况下使用handleErrorText.Pandoc.Error 中的函数更简单。如果值为 a ,则退出并显示相应的错误代码和消息;如果值为 a ,则Left返回。TextRight

PandocMonad 类
readMarkdown让我们看看和的类型writeRST:

readMarkdown :: (PandocMonad m, ToSources a)
             => ReaderOptions
             -> a
             -> m Pandoc
writeRST     :: PandocMonad m
             => WriterOptions
             -> Pandoc
             -> m Text

该PandocMonad m =>部分是类型类约束。它说明了这一点readMarkdown,并writeRST定义了可以在PandocMonad类型类的任何实例中使用的计算。PandocMonad定义在模块Text.Pandoc.Class中。

提供了两个实例PandocMonad:PandocIO和PandocPure。不同之处在于,in 中的计算PandocIO允许进行 IO(例如,读取文件),而 in 中的计算PandocPure没有任何副作用。PandocPure当您想要防止用户执行任何恶意操作时,对于沙盒环境非常有用。要在 中运行转换PandocIO,请使用runIO(如上所述)。要运行它 PandocPure,请使用runPure.

正如您从 Haddocks 中看到的,Text.Pandoc.Class 导出了许多可以在 的任何实例中使用的辅助函数PandocMonad。例如:

-- | Get the verbosity level.
getVerbosity :: PandocMonad m => m Verbosity

-- | Set the verbosity level.
setVerbosity :: PandocMonad m => Verbosity -> m ()

-- Get the accumulated log messages (in temporal order).
getLog :: PandocMonad m => m [LogMessage]
getLog = reverse <$> getsCommonState stLog

-- | Log a message using 'logOutput'.  Note that 'logOutput' is
-- called only if the verbosity level exceeds the level of the
-- message, but the message is added to the list of log messages
-- that will be retrieved by 'getLog' regardless of its verbosity level.
report :: PandocMonad m => LogMessage -> m ()

-- | Fetch an image or other item from the local filesystem or the net.
-- Returns raw content and maybe mime type.
fetchItem :: PandocMonad m
          => Text
          -> m (B.ByteString, Maybe MimeType)

-- Set the resource path searched by 'fetchItem'.
setResourcePath :: PandocMonad m => [FilePath] -> m ()

如果我们在上一节中定义的转换过程中想要更详细的信息消息,我们可以这样做:

  result <- runIO $ do
    setVerbosity INFO
    doc <- readMarkdown def (T.pack "[testing](url)")
    writeRST def doc

请注意,这PandocIO是 的一个实例MonadIO,因此您可以使用它liftIO在 pandoc 转换链内执行任意 IO 操作。

readMarkdown它的第二个参数是多态的,它可以是作为类型类实例的任何类型ToSources。您可以使用Text,如上面的示例所示。[(FilePath, Text)]但如果输入来自多个文件并且您希望准确跟踪源位置,您也可以使用。

选项
每个读者或作者的第一个参数是控制读者或作者行为的选项:ReaderOptions对于读者和 WriterOptions作者。这些在Text.Pandoc.Options中定义。研究这些选项以了解可以调整哪些内容是个好主意。

def(来自 Data.Default)表示每种选项的默认值。(您也可以使用defaultWriterOptions和defaultReaderOptions。)通常您会希望使用默认值并仅在需要时修改它们,例如:

writeRST def{ writerReferenceLinks = True }

需要了解的一些特别重要的选项:

writerTemplate:默认为 ,Nothing表示将生成一个文档片段。如果您想要完整的文档,则需要指定Just template,其中是template包含 模板内容(而不是路径)的Text.Pandoc.Templates 。Template Text

readerExtensions 和writerExtensions:这些指定在解析和渲染中使用的扩展。扩展在Text.Pandoc.Extensions中定义。

建设者
有时以编程方式构建 Pandoc 文档很有用。为了使这更容易,我们提供了模块Text.Pandoc.Builder pandoc-types。

因为连接列表很慢,所以我们使用特殊类型Inlines并Blocks包装SequenceofInline和Block元素。这些是 Monoid 类型类的实例,可以轻松连接:

import Text.Pandoc.Builder

mydoc :: Pandoc
mydoc = doc $ header 1 (text (T.pack "Hello!"))
           <> para (emph (text (T.pack "hello world")) <> text (T.pack "."))

main :: IO ()
main = print mydoc

如果您使用OverloadedStringspragma,您可以进一步简化:

mydoc = doc $ header 1 "Hello!"
           <> para (emph "hello world" <> ".")

这是一个更现实的例子。假设你的老板说:用 Word 给我写一封信,列出芝加哥所有接受 Voyager 卡的加油站。您会发现一些采用以下格式的 JSON 数据 ( fuel.json):

[ {
  "state" : "IL",
  "city" : "Chicago",
  "fuel_type_code" : "CNG",
  "zip" : "60607",
  "station_name" : "Clean Energy - Yellow Cab",
  "cards_accepted" : "A D M V Voyager Wright_Exp CleanEnergy",
  "street_address" : "540 W Grenshaw"
}, ...

然后使用 aeson 和 pandoc 解析 JSON 并创建 Word 文档:

{-# LANGUAGE OverloadedStrings #-}
import Text.Pandoc.Builder
import Text.Pandoc
import Data.Monoid ((<>), mempty, mconcat)
import Data.Aeson
import Control.Applicative
import Control.Monad (mzero)
import qualified Data.ByteString.Lazy as BL
import qualified Data.Text as T
import Data.List (intersperse)

data Station = Station{
    address        :: T.Text
  , name           :: T.Text
  , cardsAccepted  :: [T.Text]
  } deriving Show

instance FromJSON Station where
    parseJSON (Object v) = Station <$>
       v .: "street_address" <*>
       v .: "station_name" <*>
       (T.words <$> (v .:? "cards_accepted" .!= ""))
    parseJSON _          = mzero

createLetter :: [Station] -> Pandoc
createLetter stations = doc $
    para "Dear Boss:" <>
    para "Here are the CNG stations that accept Voyager cards:" <>
    simpleTable [plain "Station", plain "Address", plain "Cards accepted"]
           (map stationToRow stations) <>
    para "Your loyal servant," <>
    plain (image "JohnHancock.png" "" mempty)
  where
    stationToRow station =
      [ plain (text $ name station)
      , plain (text $ address station)
      , plain (mconcat $ intersperse linebreak
                       $ map text $ cardsAccepted station)
      ]

main :: IO ()
main = do
  json <- BL.readFile "fuel.json"
  let letter = case decode json of
                    Just stations -> createLetter [s | s <- stations,
                                        "Voyager" `elem` cardsAccepted s]
                    Nothing       -> error "Could not decode JSON"
  docx <- runIO (writeDocx def letter) >>= handleError
  BL.writeFile "letter.docx" docx
  putStrLn "Created letter.docx"

瞧!您写这封信时没有使用 Word,也没有查看数据。

数据文件
Pandoc 有许多数据文件,可以在 data/存储库的子目录中找到。它们与 pandoc 一起安装(或者,如果 pandoc 是使用该embed_data_files标志编译的,则它们嵌入在二进制文件中)。您可以使用 Text.Pandoc.Class 检索数据文件readDataFile。首先会在“用户数据目录”( 、 )readDataFile中查找该文件,如果没有找到,则返回系统默认安装的文件。要强制使用默认值,.setUserDataDirgetUserDataDirsetUserDataDir Nothing

元数据文件
Pandoc 可以将元数据添加到文档中,如用户指南中所述。与数据文件类似,可以使用readMetadataFileText.Pandoc.Class 检索元数据 YAML 文件。首先会在工作目录中查找该文件,如果没有找到,则会在用户数据目录的子目录(, )readMetadataFile中查找。metadatasetUserDataDirgetUserDataDir

模板
Pandoc 有自己的模板系统,在用户指南中进行了描述。要检索系统的默认模板,请getDefaultTemplate使用Text.Pandoc.Templates。请注意,这首先会在templates用户数据目录的子目录中查找,从而允许用户覆盖系统默认值。如果您想禁用此行为,请使用setUserDataDir Nothing.

要呈现模板,请使用renderTemplate’,它接受两个参数:模板 (Text) 和上下文(ToJSON 的任何实例)。如果您想从 Pandoc 文档的元数据部分创建上下文,请metaToJSON’使用Text.Pandoc.Writers.Shared。如果您还想合并变量中的值,请改为使用metaToJSON,并确保writerVariables在 中设置WriterOptions。

处理错误和警告
runIO并runPure返回一个Either PandocError a. 运行计算时引发的所有错误PandocMonad都将被捕获并作为Left值返回,因此它们可以由调用程序处理。要查看 的构造函数,请参阅Text.Pandoc.ErrorPandocError的文档。

PandocError 要从计算内部引发 a PandocMonad ,请使用throwError。

除了停止执行转换管道的错误之外,还可以生成信息性消息。使用reportText.Pandoc.Class 发出. _ LogMessage有关 的构造函数列表LogMessage,请参阅Text.Pandoc.Logging。请注意,每种类型的日志消息都与详细级别相关联。详细级别 ( setVerbosity/ getVerbosity) 确定报告是否将打印到 stderr (在 中运行时 PandocIO),但无论详细级别如何,所有报告的消息都在内部存储,并且可以使用 检索getLog。

走 AST
遍历 Pandoc AST 来提取信息(例如,本文档中链接到的所有 URL 是什么?所有代码示例都编译了吗?)或转换文档(例如,增加每个部分的级别)通常很有用。标题、删除强调或用图像替换专门标记的代码块)。为了使这更容易、更高效,pandoc-types包含一个模块 Text.Pandoc.Walk。

这是必要的文档:

class Walkable a b where
  -- | @walk f x@ walks the structure @x@ (bottom up) and replaces every
  -- occurrence of an @a@ with the result of applying @f@ to it.
  walk  :: (a -> a) -> b -> b
  walk f = runIdentity . walkM (return . f)
  -- | A monadic version of 'walk'.
  walkM :: (Monad m, Functor m) => (a -> m a) -> b -> m b
  -- | @query f x@ walks the structure @x@ (bottom up) and applies @f@
  -- to every @a@, appending the results.
  query :: Monoid c => (a -> c) -> b -> c

Walkable实例是为 Pandoc 类型的大多数组合定义的。例如,该 Walkable Inline Block 实例允许您获取一个函数Inline -> Inline并将其应用于Block. 并Walkable [Inline] Pandoc允许您采用一个函数[Inline] -> [Inline]并将其应用于Inlinea 中的每个最大 s列表Pandoc。

下面是一个提升标题级别的函数的简单示例:

promoteHeaderLevels :: Pandoc -> Pandoc
promoteHeaderLevels = walk promote
  where promote :: Block -> Block
        promote (Header lev attr ils) = Header (lev + 1) attr ils
        promote x = x

walkM是 的一元版本walk;例如,当您需要转换来执行 IO 操作、使用 PandocMonad 操作或更新内部状态时,可以使用它。下面是一个使用 State monad 向每个代码块添加唯一标识符的示例:

addCodeIdentifiers :: Pandoc -> Pandoc
addCodeIdentifiers doc = evalState (walkM addCodeId doc) 1
  where addCodeId :: Block -> State Int Block
        addCodeId (CodeBlock (_,classes,kvs) code) = do
          curId <- get
          put (curId + 1)
          return $ CodeBlock (show curId,classes,kvs) code
        addCodeId x = return x

query用于从 AST 收集信息。它的参数是一个查询函数,它产生某种幺半群类型的结果(例如列表)。结果连接在一起。下面是一个返回文档中链接的 URL 列表的示例:

listURLs :: Pandoc -> [Text]
listURLs = query urls
  where urls (Link _ _ (src, _)) = [src]
        urls _                   = []

创建前端
命令行程序的所有功能pandoc都已抽象到Text.Pandoc.AppconvertWithOpts模块中。因此,为 pandoc 创建 GUI 前端只需填充 结构并调用此函数即可。Opts

在Web应用程序中使用pandoc的注意事项
Pandoc 的解析器可能会对某些输入表现出病态行为。System.Timeout.timeout因此,将 pandoc 的使用包装在超时函数(例如from base)中以防止 DoS 攻击始终是一个好主意 。

如果 pandoc 从不受信任的用户输入生成 HTML,那么通过清理程序(例如 )过滤生成的 HTML 始终是一个好主意,xss-sanitize以避免安全问题。

使用runPure 而不是runIO确保pandoc的函数不执行IO操作(例如写入文件)。如果需要使某些资源可用,则在可用状态内提供“假环境” runPure(请参阅​​ Text.Pandoc.ClassPureState及其相关函数)。还可以编写一个自定义实例,例如,使 wiki 资源在虚假环境中作为文件可用,同时将 pandoc 与系统的其余部分隔离。PandocMonad

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值