git.iko.soy/typeable/pandoc
on master help sign in
Add `rebase_relative_paths` extension.

- Add manual entry for (non-default) extension `rebase_relative_paths`. - Add constructor `Ext_rebase_relative_paths` to `Extensions` in Text.Pandoc.Extensions [API change]. When enabled, this extension rewrites relative image and link paths by prepending the (relative) directory of the containing file. - Make Markdown reader sensitive to the new extension. - Add tests for #3752. Closes #3752. NB. currently the extension applies to markdown and associated readers but not commonmark/gfm.

commit834da53058069fe50da510fa86e0807a7ff7868f authorJohn MacFarlane <jgm@berkeley.edu> dateMon 24 May 2021, 10:17 (5 years ago) parent0226d23
9 files changed+111−9
MANUAL.txt+26−2 pandoc.cabal+4 src/Text/Pandoc/Extensions.hs+3 src/Text/Pandoc/Readers/Markdown.hs+29−7 test/command/3752.md+35 test/command/chap1/spider.png test/command/chap1/text.md+11 test/command/chap2/spider.png test/command/chap2/text.md+3
MANUAL.txt+26−2
@@ -3755,7 +3755,7 @@ definition:
37553755Note that space between items in a definition list is required.
37563756(A variant that loosens this requirement, but disallows "lazy"
37573757hard wrapping, can be activated with `compact_definition_lists`: see
3758−[Non-pandoc extensions], below.)
3758+[Non-default extensions], below.)
37593759
37603760[^3]: I have been influenced by the suggestions of [David
37613761 Wheeler](https://justatheory.com/2009/02/modest-markdown-proposal/).
@@ -5051,13 +5051,37 @@ author-in-text style inside notes when using a note style.
50515051[finding and editing styles]: https://citationstyles.org/authors/
50525052[CSL locale files]: https://github.com/citation-style-language/locales
50535053
5054−## Non-pandoc extensions
5054+## Non-default extensions
50555055
50565056The following Markdown syntax extensions are not enabled by default
50575057in pandoc, but may be enabled by adding `+EXTENSION` to the format
50585058name, where `EXTENSION` is the name of the extension. Thus, for
50595059example, `markdown+hard_line_breaks` is Markdown with hard line breaks.
50605060
5061+#### Extension: `rebase_relative_paths` ####
5062+
5063+Rewrite relative paths for Markdown links and images, depending
5064+on the path of the file containing the link or image link. For
5065+each link or image, pandoc will compute the directory of the
5066+containing file, relative to the working directory, and prepend
5067+the resulting path to the link or image path.
5068+
5069+The use of this extension is best understood by example.
5070+Suppose you have a a subdirectory for each chapter of a book,
5071+`chap1`, `chap2`, `chap3`. Each contains a file `text.md` and a
5072+number of images used in the chapter. You would like to have
5073+`![image](spider.jpg)` in `chap1/text.md` refer to
5074+`chap1/spider.jpg` and `![image](spider.jpg)` in `chap2/text.md`
5075+refer to `chap2/spider.jpg`. To do this, use
5076+
5077+ pandoc chap*/*.md -f markdown+rebase_relative_paths
5078+
5079+Without this extension, you would have to use
5080+`![image](chap1/spider.jpg)` in `chap1/text.md` and
5081+`![image](chap2/spider.jpg)` in `chap2/text.md`. Links with
5082+relative paths will be rewritten in the same way as images.
5083+*This option currently only affects Markdown input.*
5084+
50615085#### Extension: `attributes` ####
50625086
50635087Allows attributes to be attached to any inline or block-level
pandoc.cabal+4
@@ -214,6 +214,10 @@ extra-source-files:
214214 test/command/C.txt
215215 test/command/D.txt
216216 test/command/01.csv
217+ test/command/chap1/spider.png
218+ test/command/chap2/spider.png
219+ test/command/chap1/text.md
220+ test/command/chap2/text.md
217221 test/command/defaults1.yaml
218222 test/command/defaults2.yaml
219223 test/command/defaults3.yaml
src/Text/Pandoc/Extensions.hs+3
@@ -136,6 +136,8 @@ data Extension =
136136 | Ext_raw_html -- ^ Allow raw HTML
137137 | Ext_raw_tex -- ^ Allow raw TeX (other than math)
138138 | Ext_raw_markdown -- ^ Parse markdown in ipynb as raw markdown
139+ | Ext_rebase_relative_paths -- ^ Rebase relative image and link paths,
140+ -- relative to directory of containing file
139141 | Ext_shortcut_reference_links -- ^ Shortcut reference links
140142 | Ext_simple_tables -- ^ Pandoc-style simple tables
141143 | Ext_smart -- ^ "Smart" quotes, apostrophes, ellipses, dashes
@@ -462,6 +464,7 @@ getAllExtensions f = universalExtensions <> getAll f
462464 , Ext_gutenberg
463465 , Ext_smart
464466 , Ext_literate_haskell
467+ , Ext_rebase_relative_paths
465468 ]
466469 getAll "markdown_strict" = allMarkdownExtensions
467470 getAll "markdown_phpextra" = allMarkdownExtensions
src/Text/Pandoc/Readers/Markdown.hs+29−7
@@ -29,7 +29,7 @@ import qualified Data.Set as Set
2929import Data.Text (Text)
3030import qualified Data.Text as T
3131import qualified Data.ByteString.Lazy as BL
32−import System.FilePath (addExtension, takeExtension)
32+import System.FilePath (addExtension, takeExtension, isAbsolute, takeDirectory)
3333import Text.HTML.TagSoup hiding (Row)
3434import Text.Pandoc.Builder (Blocks, Inlines)
3535import qualified Text.Pandoc.Builder as B
@@ -1836,9 +1836,12 @@ regLink :: PandocMonad m
18361836 -> MarkdownParser m (F Inlines)
18371837regLink constructor lab = try $ do
18381838 (src, tit) <- source
1839+ rebase <- option False (True <$ guardEnabled Ext_rebase_relative_paths)
1840+ pos <- getPosition
1841+ let src' = if rebase then rebasePath pos src else src
18391842 attr <- option nullAttr $
18401843 guardEnabled Ext_link_attributes >> attributes
1841− return $ constructor attr src tit <$> lab
1844+ return $ constructor attr src' tit <$> lab
18421845
18431846-- a link like [this][ref] or [this][] or [this]
18441847referenceLink :: PandocMonad m
@@ -1854,6 +1857,8 @@ referenceLink constructor (lab, raw) = do
18541857 return (mempty, "")))
18551858 <|>
18561859 try ((guardDisabled Ext_spaced_reference_links <|> spnl) >> reference)
1860+ rebase <- option False (True <$ guardEnabled Ext_rebase_relative_paths)
1861+ pos <- getPosition
18571862 when (raw' == "") $ guardEnabled Ext_shortcut_reference_links
18581863 let labIsRef = raw' == "" || raw' == "[]"
18591864 let key = toKey $ if labIsRef then raw else raw'
@@ -1878,7 +1883,9 @@ referenceLink constructor (lab, raw) = do
18781883 Just ((src, tit), _) -> constructor nullAttr src tit <$> lab
18791884 Nothing -> makeFallback
18801885 else makeFallback
1881− Just ((src,tit), attr) -> constructor attr src tit <$> lab
1886+ Just ((src,tit), attr) ->
1887+ let src' = if rebase then rebasePath pos src else src
1888+ in constructor attr src' tit <$> lab
18821889
18831890dropBrackets :: Text -> Text
18841891dropBrackets = dropRB . dropLB
@@ -1911,15 +1918,30 @@ autoLink = try $ do
19111918 return $ return $ B.linkWith attr (src <> escapeURI extra) ""
19121919 (B.str $ orig <> extra)
19131920
1921+-- | Rebase a relative path, by adding the (relative) directory
1922+-- of the containing source position. Absolute links and URLs
1923+-- are untouched.
1924+rebasePath :: SourcePos -> Text -> Text
1925+rebasePath pos path = do
1926+ let fp = sourceName pos
1927+ in if isAbsolute (T.unpack path) || isURI path
1928+ then path
1929+ else
1930+ case takeDirectory fp of
1931+ "" -> path
1932+ "." -> path
1933+ d -> T.pack d <> "/" <> path
1934+
19141935image :: PandocMonad m => MarkdownParser m (F Inlines)
19151936image = try $ do
19161937 char '!'
19171938 (lab,raw) <- reference
19181939 defaultExt <- getOption readerDefaultImageExtension
1919− let constructor attr' src = case takeExtension (T.unpack src) of
1920− "" -> B.imageWith attr' (T.pack $ addExtension (T.unpack src)
1921− $ T.unpack defaultExt)
1922− _ -> B.imageWith attr' src
1940+ let constructor attr' src =
1941+ case takeExtension (T.unpack src) of
1942+ "" -> B.imageWith attr' (T.pack $ addExtension (T.unpack src)
1943+ $ T.unpack defaultExt)
1944+ _ -> B.imageWith attr' src
19231945 regLink constructor lab <|> referenceLink constructor (lab,raw)
19241946
19251947note :: PandocMonad m => MarkdownParser m (F Inlines)
test/command/3752.mdnew file+35
@@ -0,0 +1,35 @@
1+```
2+% pandoc command/chap1/text.md command/chap2/text.md -f markdown+rebase_relative_paths --verbose -t docx | pandoc -f docx -t plain
3+^D
4+[INFO] Loaded command/chap1/spider.png from ./command/chap1/spider.png
5+[INFO] Loaded command/chap1/../../lalune.jpg from ./command/chap1/../../lalune.jpg
6+[INFO] Loaded command/chap2/spider.png from ./command/chap2/spider.png
7+Chapter one
8+
9+A spider: [spider]
10+
11+The moon: [moon]
12+
13+Link to spider picture.
14+
15+URL left alone: manual.
16+
17+Absolute path left alone: absolute.
18+
19+Chapter two
20+
21+A spider: [spider]
22+```
23+
24+```
25+% pandoc command/chap1/text.md command/chap2/text.md -f markdown+rebase_relative_paths -t html
26+^D
27+<h1 id="chapter-one">Chapter one</h1>
28+<p>A spider: <img src="command/chap1/spider.png" alt="spider" /></p>
29+<p>The moon: <img src="command/chap1/../../lalune.jpg" alt="moon" /></p>
30+<p>Link to <a href="command/chap1/spider.png">spider picture</a>.</p>
31+<p>URL left alone: <a href="https://pandoc.org/MANUAL.html">manual</a>.</p>
32+<p>Absolute path left alone: <a href="/foo/bar/baz.png">absolute</a>.</p>
33+<h1 id="chapter-two">Chapter two</h1>
34+<p>A spider: <img src="command/chap2/spider.png" alt="spider" /></p>
35+```
test/command/chap1/spider.pngnew file

Binary file not shown.

test/command/chap1/text.mdnew file+11
@@ -0,0 +1,11 @@
1+# Chapter one
2+
3+A spider: ![spider](spider.png)
4+
5+The moon: ![moon](../../lalune.jpg)
6+
7+Link to [spider picture](spider.png).
8+
9+URL left alone: [manual](https://pandoc.org/MANUAL.html).
10+
11+Absolute path left alone: [absolute](/foo/bar/baz.png).
test/command/chap2/spider.pngnew file

Binary file not shown.

test/command/chap2/text.mdnew file+3
@@ -0,0 +1,3 @@
1+# Chapter two
2+
3+A spider: ![spider](spider.png)