Skip to content

Commit 0a916c6

Browse files
cchalmersclaude
andcommitted
Port filter to the diagrams 2.0 API
Swap the 1.x diagrams-lib/diagrams-builder/diagrams-cairo stack for this workspace's 2.0 APIs on top of the upstream master merge: * Build diagrams through the rewritten diagrams-builder interface (Snippets + DiagramBuilder + diaSnippet), interpreting a 'saveDiagram <backend token>' expression instead of constructing per-backend BuildOpts. The SomeBuildOpts/MkImage existential machinery is no longer needed. * Replace the Cairo backend with Rasterific (diagrams-cairo is not part of the 2.0 cabal workspace). Rasterific writes .png and, for the latex/beamer output formats, .pdf (dispatched on file extension), so upstream's latex->pdf behaviour is preserved; the CLI backend flag now accepts 'Rasterific' instead of 'Cairo'. SVG behaviour is unchanged. * Keep upstream behaviour otherwise: hash-named image files in the output directory, skip-if-exists regeneration, '.'/'O' progress output, width/height/caption/alt attributes, echo=Above/Below, absolute-path switch, Figure support, and the 'pad 1.1 . centerXY' post-processing (now applied inside the interpreted expression). * Drop the snippet imports of Diagrams.TwoD.Types/Diagrams.Core.Points/ Data.Typeable (1.x workarounds) and Graphics.SVGFonts (SVGFonts has no 2.0-compatible release); Diagrams.Prelude plus the backend module are imported automatically by the builder. * Update dependencies: diagrams 2.0 + diagrams-builder 2.0 + diagrams-svg/diagrams-rasterific (runtime deps for the interpreter); drop diagrams-lib/-core/-cairo, linear, svg-builder and the test suite's SVGFonts. Tests (all 7 golden tests) pass; hint picks the workspace packages up from cabal's .ghc.environment file as before (build with --write-ghc-environment-files=always). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
1 parent f6f3bcc commit 0a916c6

5 files changed

Lines changed: 64 additions & 123 deletions

File tree

README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,5 +79,8 @@ rst reader does not attach classes to code blocks, only to Div elements.
7979
code block alone?
8080
* provide command-line flags to override default behavior
8181
* add Backends besides Cairo
82+
<!-- TODO(docs, human): the diagrams 2.0 port renders via Rasterific (default)
83+
and SVG instead of Cairo; update the README (usage, backend flag values,
84+
this TODO list) accordingly. -->
8285
* Support RST by handling `Div class=diagram [CodeBlock foo bar]` the same as `CodeBlock class=diagram bar`
8386
* Alternate install directions using `stack`

diagrams-pandoc.cabal

Lines changed: 9 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -31,15 +31,14 @@ common common-deps
3131

3232
library
3333
import: common-deps
34-
build-depends: diagrams-lib >= 1.3 && < 1.6,
35-
linear >= 1.10 && < 1.24,
36-
diagrams-builder >= 0.7 && < 0.9,
37-
diagrams-cairo >= 1.3 && < 1.6,
38-
diagrams-svg >= 1.4 && < 1.6,
39-
diagrams-core >= 1.4 && < 1.6,
34+
build-depends: diagrams >= 2.0 && < 2.1,
35+
diagrams-builder >= 2.0 && < 2.1,
4036
hashable >= 1.2 && < 1.6,
41-
svg-builder >= 0.1 && < 0.2,
42-
pandoc >= 3.0 && < 3.9
37+
pandoc >= 3.0 && < 3.9,
38+
39+
-- sandbox dependencies
40+
diagrams-svg >= 2.0 && < 2.1,
41+
diagrams-rasterific >= 2.0 && < 2.1
4342
exposed-modules: Text.Pandoc.Diagrams
4443
default-language: Haskell2010
4544
hs-source-dirs: src
@@ -55,20 +54,13 @@ test-suite test
5554
bytestring >= 0.10.10 && < 0.13,
5655
pandoc >= 3.0 && < 3.9,
5756
tasty >= 1.4.3 && < 1.6,
58-
tasty-golden >= 2.3.5 && < 2.4,
59-
60-
-- sandbox dependencies
61-
SVGFonts >= 1.8.0 && < 1.9
57+
tasty-golden >= 2.3.5 && < 2.4
6258

6359

6460
executable diagrams-pandoc
6561
import: common-deps
6662
main-is: src/Main.hs
6763
other-extensions: CPP
68-
build-depends: diagrams-lib >= 1.3 && < 1.6,
69-
linear >= 1.10 && < 1.24,
70-
diagrams-builder >= 0.7 && < 0.9,
71-
diagrams-cairo >= 1.3 && < 1.6,
72-
diagrams-pandoc,
64+
build-depends: diagrams-pandoc,
7365
optparse-applicative >= 0.11 && < 0.20
7466
default-language: Haskell2010

src/Main.hs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ optsParser = Opts
2727
value "example")
2828
<*> switch (long "absolute" <> short 'a' <>
2929
help "output the name of Diagram in Haskell snippet as absolute path")
30-
<*> option auto (long "backend" <> short 'b' <> metavar "BACKEND" <> value Cairo)
30+
<*> option auto (long "backend" <> short 'b' <> metavar "BACKEND" <> value Rasterific)
3131

3232
withHelp :: ParserInfo Opts
3333
withHelp = info

src/Text/Pandoc/Diagrams.hs

Lines changed: 50 additions & 104 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,3 @@
1-
{-# LANGUAGE ExistentialQuantification #-}
2-
{-# LANGUAGE FlexibleContexts #-}
3-
{-# LANGUAGE FlexibleInstances #-}
41
{-# LANGUAGE OverloadedStrings #-}
52
{-# LANGUAGE RecordWildCards #-}
63

@@ -10,43 +7,36 @@
107

118
module Text.Pandoc.Diagrams where
129

13-
import Data.Hashable (Hashable)
10+
import Control.Monad.IO.Class (liftIO)
11+
import Data.Foldable (fold)
12+
import Data.Hashable (hash)
1413
import Data.List (delete)
14+
import Data.Maybe (fromMaybe)
1515
import Data.Text (Text)
1616
import qualified Data.Text as T
17-
import Data.Typeable (Typeable)
18-
import qualified Diagrams.Backend.Cairo.Internal as BCairo
19-
import qualified Diagrams.Backend.SVG as BSvg
17+
import Diagrams.Backend (BackendInfo, rasterificInfo,
18+
svgInfo)
2019
import qualified Diagrams.Builder as DB
21-
import qualified Diagrams.Core as DC
22-
import Diagrams.Prelude (centerXY, pad, (&), (.~))
23-
import Diagrams.Size (dims)
24-
import qualified Graphics.Svg as Svg
25-
import Linear (V2 (..), zero)
26-
import System.Directory (createDirectoryIfMissing)
20+
import Diagrams.Builder.Opts (emptySnippet, snippets)
21+
import Diagrams.Prelude (dims2D, (&), (.~))
22+
import Numeric (showHex)
23+
import System.Directory (createDirectoryIfMissing,
24+
doesFileExist)
2725
import System.FilePath (pathSeparator, (<.>), (</>))
2826
import System.IO (hFlush, hPutStr, hPutStrLn, stderr)
2927
import Text.Pandoc.Definition (Block(..), Caption, Attr)
3028
import qualified Text.Pandoc.Builder as PB
3129
import qualified Text.Pandoc as Pandoc
3230
import qualified Text.Pandoc.Shared as Pandoc
3331
import Text.Pandoc.Options (def, readerExtensions, pandocExtensions)
34-
import Data.Maybe (fromMaybe)
35-
import Data.Foldable (fold)
3632

3733
backendExt :: Opts -> String
3834
backendExt Opts {_backend = SVG } = "svg"
39-
backendExt Opts {_backend = Cairo, ..} = case _outFormat of
35+
backendExt Opts {_backend = Rasterific, ..} = case _outFormat of
4036
"beamer" -> "pdf"
4137
"latex" -> "pdf"
4238
_ -> "png"
4339

44-
-- Return output type for a string
45-
findCairoOutputType :: String -> BCairo.OutputType
46-
findCairoOutputType "beamer" = BCairo.PDF
47-
findCairoOutputType "latex" = BCairo.PDF
48-
findCairoOutputType _ = BCairo.PNG
49-
5040
data Opts = Opts {
5141
_outFormat :: String,
5242
_outDir :: FilePath,
@@ -55,7 +45,7 @@ data Opts = Opts {
5545
_backend :: Backend
5646
}
5747

58-
data Backend = Cairo | SVG deriving (Read)
48+
data Backend = Rasterific | SVG deriving (Read)
5949

6050
data Echo = Above | Below
6151

@@ -113,100 +103,57 @@ parseCaption s = Pandoc.runIOorExplode $ do
113103
Pandoc.readMarkdown (def { readerExtensions = pandocExtensions }) s
114104
pure blks
115105

116-
-- Copied from https://github.com/diagrams/diagrams-doc/blob/master/doc/Xml2Html.hs
117-
-- With the CPP removed, thereby requiring Cairo
118-
-- TODO clean this up, move it into -builder somehow
119106
-- | Compile the literate source code of a diagram to a .png/.pdf file with
120107
-- a file name given by a hash of the source code contents
121108
compileDiagram :: Opts -> [(Text,Text)] -> Text -> IO (Either String Text)
122109
compileDiagram opts attrs src = do
123110
ensureDir $ _outDir opts
124-
case mkBuildOpts opts attrs src of
125-
SomeBuildOpts bo -> do
126-
res <- DB.buildDiagram bo
127-
case res of
128-
DB.ParseErr err -> do
129-
hPutStrLn stderr ("\nError while parsing\n" ++ T.unpack src)
130-
hPutStrLn stderr err
131-
return $ Left "Error while parsing"
132-
133-
DB.InterpErr ierr -> do
134-
hPutStrLn stderr ("\nError while interpreting\n" ++ T.unpack src)
135-
hPutStrLn stderr (DB.ppInterpError ierr)
136-
return $ Left "Error while interpreting"
137-
138-
DB.Skipped hash -> do
111+
case DB.diaSnippet (emptySnippet & snippets .~ [T.unpack src]) (mkBuilder opts attrs) of
112+
Left err -> do
113+
hPutStrLn stderr ("\nError while parsing\n" ++ T.unpack src)
114+
hPutStrLn stderr err
115+
return $ Left "Error while parsing"
116+
117+
Right i -> do
118+
let path = mkFile opts (hashToHexStr (hash i))
119+
alreadyBuilt <- doesFileExist path
120+
if alreadyBuilt
121+
then do
139122
hPutStr stderr "."
140123
hFlush stderr
141-
return $ Right (T.pack $ mkFile opts (DB.hashToHexStr hash))
142-
143-
DB.OK hash out -> do
144-
hPutStr stderr "O"
145-
hFlush stderr
146-
let path = mkFile opts (DB.hashToHexStr hash)
147-
handleResult path $ SomeResult out
148124
return $ Right (T.pack path)
125+
else do
126+
res <- DB.runSandboxInterpreter $ DB.runInterpret i >>= liftIO . ($ path)
127+
case res of
128+
Left ierr -> do
129+
hPutStrLn stderr ("\nError while interpreting\n" ++ T.unpack src)
130+
hPutStrLn stderr (DB.ppInterpError ierr)
131+
return $ Left "Error while interpreting"
132+
Right () -> do
133+
hPutStr stderr "O"
134+
hFlush stderr
135+
return $ Right (T.pack path)
149136
where
150137
ensureDir = createDirectoryIfMissing True
151-
handleResult path (SomeResult a) = mkImage path a
152138

153139
mkFile :: Opts -> FilePath -> FilePath
154140
mkFile opts base = _outDir opts </> base <.> backendExt opts
155141

156-
data SomeResult = forall r. (MkImage r) => SomeResult r
157-
158-
data SomeBuildOpts v n =
159-
forall a. (Typeable a, DC.Backend a v n, Hashable (DC.Options a v n), MkImage (DC.Result a v n))
160-
=> SomeBuildOpts (DB.BuildOpts a v n)
161-
162-
class MkImage a where
163-
mkImage :: FilePath -> a -> IO ()
164-
165-
instance MkImage (IO (), r) where
166-
mkImage _ = fst
167-
168-
instance MkImage Svg.Element where
169-
mkImage path e = writeFile path $ show e
170-
171-
mkBuildOpts :: Opts -> [(Text, Text)] -> Text -> SomeBuildOpts V2 Double
172-
mkBuildOpts opts attrs src = case _backend opts of
173-
Cairo -> SomeBuildOpts $ DB.mkBuildOpts BCairo.Cairo zero
174-
( BCairo.CairoOptions "default.png"
175-
(dims $ V2 (widthAttribute attrs) (heightAttribute attrs))
176-
(findCairoOutputType $ _outFormat opts)
177-
False
178-
)
179-
& DB.snippets .~ [T.unpack src]
180-
& DB.imports .~
181-
[ "Diagrams.TwoD.Types" -- WHY IS THIS NECESSARY =(
182-
, "Diagrams.Core.Points" -- GHC 7.2 bug? need V (Point R2) = R2 (see #65)
183-
, "Diagrams.Backend.Cairo"
184-
, "Diagrams.Backend.Cairo.Internal"
185-
, "Graphics.SVGFonts"
186-
, "Data.Typeable"
187-
]
188-
& DB.pragmas .~ ["DeriveDataTypeable"]
189-
& DB.diaExpr .~ _expression opts
190-
& DB.postProcess .~ postProcess
191-
& DB.decideRegen .~
192-
DB.hashedRegenerate
193-
(\hash opts' -> opts' { BCairo._cairoFileName = mkFile opts hash })
194-
(_outDir opts)
195-
SVG -> SomeBuildOpts $ DB.mkBuildOpts BSvg.SVG zero
196-
(BSvg.SVGOptions (dims $ V2 (widthAttribute attrs) (heightAttribute attrs)) Nothing "" [] True)
197-
& DB.snippets .~ [T.unpack src]
198-
& DB.imports .~
199-
[ "Diagrams.TwoD.Types"
200-
, "Diagrams.Core.Points"
201-
, "Diagrams.Backend.SVG"
202-
, "Graphics.SVGFonts"
203-
, "Data.Typeable"
204-
]
205-
& DB.pragmas .~ ["DeriveDataTypeable"]
206-
& DB.diaExpr .~ _expression opts
207-
& DB.postProcess .~ postProcess
208-
where
209-
postProcess = pad 1.1 . centerXY
142+
mkBuilder :: Opts -> [(Text, Text)] -> DB.DiagramBuilder
143+
mkBuilder opts attrs = DB.DiagramBuilder
144+
{ DB._diaInfo = backendInfo (_backend opts)
145+
, DB._diaExpr = "pad 1.1 (centerXY (" ++ _expression opts ++ "))"
146+
, DB._diaOutSize = dims2D (round $ widthAttribute attrs) (round $ heightAttribute attrs)
147+
}
148+
149+
backendInfo :: Backend -> BackendInfo
150+
backendInfo Rasterific = rasterificInfo
151+
backendInfo SVG = svgInfo
152+
153+
-- Turn a Hash into a hex with no leading 0x. Hash is converted to a
154+
-- word to avoid negative values.
155+
hashToHexStr :: Int -> String
156+
hashToHexStr h = showHex (fromIntegral h :: Word) ""
210157

211158
widthAttribute :: [(Text,Text)] -> Double
212159
widthAttribute attrs =
@@ -226,4 +173,3 @@ readEcho attrs = case lookup "echo" attrs of
226173
Just v -> case T.toLower v of
227174
"above" -> Above
228175
_ -> Below
229-

test/Main.hs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ defaultOpts = Opts
3838
, _outDir = "images"
3939
, _expression = "example"
4040
, _absolutePath = False
41-
, _backend = Cairo
41+
, _backend = Rasterific
4242
}
4343

4444
diagramsFilter :: Pandoc.Pandoc -> IO Pandoc.Pandoc

0 commit comments

Comments
 (0)