Skip to content

Commit dd4fa65

Browse files
committed
Use the Figure Block constructor.
* It provides a specific representation for figures in the pandoc's AST. * It uses the `SimpleFigure` pattern synonym to replace the previous construction: [Para [Image ("",[],[]) [Str "CAP2"] ("../media/rId25.jpg","fig:")]]
1 parent 1f7c230 commit dd4fa65

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

58 files changed

+727
-157
lines changed

MANUAL.txt

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3171,6 +3171,17 @@ In the `context` output format this enables the use of [Natural Tables
31713171
Natural tables allow more fine-grained global customization but come
31723172
at a performance penalty compared to extreme tables.
31733173

3174+
#### Extension: `native_figures` ####
3175+
3176+
Use pandoc's native `Figure` element for content inside `<figure>` tags, in the
3177+
case of HTML, or `figure` environments, in case of LaTeX. This, in turn, allows
3178+
some writers to produce more accurate representations of figures. It also
3179+
allows the use of the `Figure` element in filters, for custom figure output.
3180+
3181+
This extension can be enabled/disabled for the following formats:
3182+
3183+
input formats
3184+
: `latex` `html`
31743185

31753186
# Pandoc's Markdown
31763187

data/pandoc.lua

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -469,6 +469,22 @@ M.Div = M.Block:create_constructor(
469469
{{attr = {"identifier", "classes", "attributes"}}, "content"}
470470
)
471471

472+
473+
--- Creates a figure element.
474+
-- @function Figure
475+
-- @tparam {Block,...} content figure block contents
476+
-- @tparam Caption caption figure caption
477+
-- @tparam[opt] Attr attr element attributes
478+
-- @treturn Block figure element
479+
M.Figure = M.Block:create_constructor(
480+
"Figure",
481+
function(content, caption, attr)
482+
return {c = {ensureAttr(attr), caption, ensureList(content)}}
483+
end,
484+
{{attr = {"identifier", "classes", "attributes"}}, "caption", "content"}
485+
)
486+
487+
472488
--- Creates a header element.
473489
-- @function Header
474490
-- @tparam int level header level

pandoc.cabal

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -206,6 +206,7 @@ extra-source-files:
206206
test/bodybg.gif
207207
test/*.native
208208
test/command/*.md
209+
test/command/figures/*.md
209210
test/command/*.csl
210211
test/command/biblio.bib
211212
test/command/averroes.bib

src/Text/Pandoc/Extensions.hs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,7 @@ data Extension =
126126
| Ext_mmd_title_block -- ^ Multimarkdown metadata block
127127
| Ext_multiline_tables -- ^ Pandoc-style multiline tables
128128
| Ext_native_divs -- ^ Use Div blocks for contents of <div> tags
129+
| Ext_native_figures -- ^ Use Figure blocks for contenst of <figure> tags.
129130
| Ext_native_spans -- ^ Use Span inlines for contents of <span>
130131
| Ext_native_numbering -- ^ Use output format's native numbering for figures and tables
131132
| Ext_ntb -- ^ ConTeXt Natural Tables
@@ -527,6 +528,7 @@ getAllExtensions f = universalExtensions <> getAll f
527528
getAll "html" = autoIdExtensions <>
528529
extensionsFromList
529530
[ Ext_native_divs
531+
, Ext_native_figures
530532
, Ext_line_blocks
531533
, Ext_native_spans
532534
, Ext_empty_paragraphs
@@ -552,6 +554,7 @@ getAllExtensions f = universalExtensions <> getAll f
552554
, Ext_raw_tex
553555
, Ext_task_lists
554556
, Ext_literate_haskell
557+
, Ext_native_figures
555558
]
556559
getAll "beamer" = getAll "latex"
557560
getAll "context" = autoIdExtensions <>

src/Text/Pandoc/Lua/Marshaling/AST.hs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,7 @@ pushBlock = \case
160160
CodeBlock attr code -> pushViaConstructor "CodeBlock" code (LuaAttr attr)
161161
DefinitionList items -> pushViaConstructor "DefinitionList" items
162162
Div attr blcks -> pushViaConstructor "Div" blcks (LuaAttr attr)
163+
Figure attr capt blcks -> pushViaConstructor "Figure" blcks capt (LuaAttr attr)
163164
Header lvl attr inlns -> pushViaConstructor "Header" lvl inlns (LuaAttr attr)
164165
HorizontalRule -> pushViaConstructor "HorizontalRule"
165166
LineBlock blcks -> pushViaConstructor "LineBlock" blcks
@@ -182,6 +183,8 @@ peekBlock idx = defineHowTo "get Block value" $! do
182183
"CodeBlock" -> withAttr CodeBlock <$!> elementContent
183184
"DefinitionList" -> DefinitionList <$!> elementContent
184185
"Div" -> withAttr Div <$!> elementContent
186+
"Figure" -> (\(LuaAttr attr, capt, bs) -> Figure attr capt bs)
187+
<$!> elementContent
185188
"Header" -> (\(lvl, LuaAttr attr, lst) -> Header lvl attr lst)
186189
<$!> elementContent
187190
"HorizontalRule" -> return HorizontalRule

src/Text/Pandoc/Readers/HTML.hs

Lines changed: 40 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ import Data.List.Split (splitWhen)
3535
import Data.List (foldl')
3636
import qualified Data.Map as M
3737
import Data.Maybe (fromMaybe, isJust, isNothing)
38+
import Data.Either (partitionEithers)
3839
import Data.Monoid (First (..))
3940
import qualified Data.Set as Set
4041
import Data.Text (Text)
@@ -57,7 +58,8 @@ import Text.Pandoc.Error
5758
import Text.Pandoc.Logging
5859
import Text.Pandoc.Options (
5960
Extension (Ext_epub_html_exts, Ext_empty_paragraphs, Ext_native_divs,
60-
Ext_native_spans, Ext_raw_html, Ext_line_blocks, Ext_raw_tex),
61+
Ext_native_spans, Ext_raw_html, Ext_line_blocks, Ext_raw_tex,
62+
Ext_native_figures),
6163
ReaderOptions (readerExtensions, readerStripComments),
6264
extensionEnabled)
6365
import Text.Pandoc.Parsing hiding ((<|>))
@@ -535,24 +537,43 @@ pPara = do
535537
<|> return (B.para contents)
536538

537539
pFigure :: PandocMonad m => TagParser m Blocks
538-
pFigure = try $ do
539-
TagOpen _ _ <- pSatisfy (matchTagOpen "figure" [])
540-
skipMany pBlank
541-
let pImg = (\x -> (Just x, Nothing)) <$>
542-
(pInTag TagsOmittable "p" pImage <* skipMany pBlank)
543-
pCapt = (\x -> (Nothing, Just x)) <$> do
544-
bs <- pInTags "figcaption" block
545-
return $ blocksToInlines' $ B.toList bs
546-
pSkip = (Nothing, Nothing) <$ pSatisfy (not . matchTagClose "figure")
547-
res <- many (pImg <|> pCapt <|> pSkip)
548-
let mbimg = msum $ map fst res
549-
let mbcap = msum $ map snd res
550-
TagClose _ <- pSatisfy (matchTagClose "figure")
551-
let caption = fromMaybe mempty mbcap
552-
case B.toList <$> mbimg of
553-
Just [Image attr _ (url, tit)] ->
554-
return $ B.para $ B.imageWith attr url ("fig:" <> tit) caption
555-
_ -> mzero
540+
pFigure = do
541+
has_native_figures <-
542+
extensionEnabled Ext_native_figures <$> getOption readerExtensions
543+
if has_native_figures
544+
then pNativeFigure
545+
else try $ do
546+
TagOpen _ _ <- pSatisfy (matchTagOpen "figure" [])
547+
skipMany pBlank
548+
let pImg = (\x -> (Just x, Nothing)) <$>
549+
(pInTag TagsOmittable "p" pImage <* skipMany pBlank)
550+
pCapt = (\x -> (Nothing, Just x)) <$> do
551+
bs <- pInTags "figcaption" block
552+
return $ blocksToInlines' $ B.toList bs
553+
pSkip = (Nothing, Nothing) <$ pSatisfy (not . matchTagClose "figure")
554+
-- res :: [(Maybe Inlines, Maybe Inlines)]
555+
-- [(Just img, Nothing), (Nothing, Just caption), ...]
556+
res <- many (pImg <|> pCapt <|> pSkip)
557+
-- Takes the first image and the first caption, if any, drop the rest.
558+
let mbimg = msum $ map fst res
559+
let mbcap = msum $ map snd res -- mbcap :: Maybe Inlines
560+
TagClose _ <- pSatisfy (matchTagClose "figure")
561+
let caption = fromMaybe mempty mbcap
562+
-- only process one image
563+
case B.toList <$> mbimg of
564+
Just [Image attr _ (url, tit)] ->
565+
return $ B.simpleFigureWith attr caption url tit
566+
_ -> mzero
567+
568+
pNativeFigure :: PandocMonad m => TagParser m Blocks
569+
pNativeFigure = try $ do
570+
TagOpen tag attrList <- lookAhead $ pSatisfy (matchTagOpen "figure" [])
571+
--let (ident, classes, kvs) = toAttr attr
572+
contents <- pInTags tag (many $ Left <$> pInTags "figcaption" block <|> (Right <$> block))
573+
574+
let (captions, rest) = partitionEithers contents
575+
-- I should capture the caption
576+
return $ B.figureWith (toAttr attrList) (Caption Nothing (B.toList (mconcat captions))) $ mconcat rest
556577

557578
pCodeBlock :: PandocMonad m => TagParser m Blocks
558579
pCodeBlock = try $ do

src/Text/Pandoc/Readers/LaTeX.hs

Lines changed: 46 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ import Data.Maybe (fromMaybe, maybeToList)
3232
import qualified Data.Set as Set
3333
import Data.Text (Text)
3434
import qualified Data.Text as T
35+
import Data.Either (partitionEithers)
3536
import Skylighting (defaultSyntaxMap)
3637
import System.FilePath (addExtension, replaceExtension, takeExtension)
3738
import Text.Collate.Lang (renderLang)
@@ -935,8 +936,8 @@ environments = M.union (tableEnvironments blocks inline) $
935936
, ("letter", env "letter" letterContents)
936937
, ("minipage", env "minipage" $
937938
skipopts *> spaces *> optional braced *> spaces *> blocks)
938-
, ("figure", env "figure" $ skipopts *> figure)
939-
, ("subfigure", env "subfigure" $ skipopts *> tok *> figure)
939+
, ("figure", env "figure" $ skipopts *> Text.Pandoc.Readers.LaTeX.figure)
940+
, ("subfigure", env "subfigure" $ skipopts *> tok *> Text.Pandoc.Readers.LaTeX.figure)
940941
, ("center", divWith ("", ["center"], []) <$> env "center" blocks)
941942
, ("quote", blockQuote <$> env "quote" blocks)
942943
, ("quotation", blockQuote <$> env "quotation" blocks)
@@ -1088,30 +1089,55 @@ letterContents = do
10881089
return $ addr <> bs -- sig added by \closing
10891090

10901091
figure :: PandocMonad m => LP m Blocks
1091-
figure = try $ do
1092+
figure = do
1093+
has_native_figures <-
1094+
extensionEnabled Ext_native_figures <$> getOption readerExtensions
1095+
if has_native_figures
1096+
then nativeFigure
1097+
else try $ do
1098+
resetCaption
1099+
blocks >>= addImageCaption
1100+
1101+
nativeFigure :: PandocMonad m => LP m Blocks
1102+
nativeFigure = try $ do
10921103
resetCaption
1093-
blocks >>= addImageCaption
1104+
innerContent <- many $ try (Left <$> label) <|> (Right <$> block)
1105+
let content = walk go $ mconcat $ snd $ partitionEithers innerContent
1106+
labelResult <- sLastLabel <$> getState
1107+
let attr = case labelResult of
1108+
Just lab -> (lab, [], [])
1109+
_ -> nullAttr
1110+
captResult <- sCaption <$> getState
1111+
case captResult of
1112+
Nothing -> return $ B.figureWith attr (Caption Nothing []) content
1113+
Just capt -> return $ B.figureWith attr (B.caption Nothing $ B.plain capt) content
1114+
1115+
where
1116+
-- Remove the `Image` caption b.c. it's on the `Figure`
1117+
go (Para [Image attr _ target]) = Plain [Image attr [] target]
1118+
go x = x
10941119

10951120
addImageCaption :: PandocMonad m => Blocks -> LP m Blocks
10961121
addImageCaption = walkM go
1097-
where go (Image attr@(_, cls, kvs) alt (src,tit))
1122+
where go p@(Para [Image attr@(_, cls, kvs) _ (src, tit)])
10981123
| not ("fig:" `T.isPrefixOf` tit) = do
10991124
st <- getState
1100-
let (alt', tit') = case sCaption st of
1101-
Just ils -> (toList ils, "fig:" <> tit)
1102-
Nothing -> (alt, tit)
1103-
attr' = case sLastLabel st of
1104-
Just lab -> (lab, cls, kvs)
1105-
Nothing -> attr
1106-
case attr' of
1107-
("", _, _) -> return ()
1108-
(ident, _, _) -> do
1109-
num <- getNextNumber sLastFigureNum
1110-
setState
1111-
st{ sLastFigureNum = num
1112-
, sLabels = M.insert ident
1113-
[Str (renderDottedNum num)] (sLabels st) }
1114-
return $ Image attr' alt' (src, tit')
1125+
case sCaption st of
1126+
Nothing -> return p
1127+
Just figureCaption -> do
1128+
let attr' = case sLastLabel st of
1129+
Just lab -> (lab, cls, kvs)
1130+
Nothing -> attr
1131+
case attr' of
1132+
("", _, _) -> return ()
1133+
(ident, _, _) -> do
1134+
num <- getNextNumber sLastFigureNum
1135+
setState
1136+
st{ sLastFigureNum = num
1137+
, sLabels = M.insert ident
1138+
[Str (renderDottedNum num)] (sLabels st) }
1139+
1140+
return $ SimpleFigure attr' (B.toList figureCaption) (src, tit)
11151141
go x = return x
11161142

11171143
coloredBlock :: PandocMonad m => Text -> LP m Blocks

src/Text/Pandoc/Readers/Markdown.hs

Lines changed: 13 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1013,19 +1013,18 @@ normalDefinitionList = do
10131013
para :: PandocMonad m => MarkdownParser m (F Blocks)
10141014
para = try $ do
10151015
exts <- getOption readerExtensions
1016-
let implicitFigures x
1017-
| extensionEnabled Ext_implicit_figures exts = do
1018-
x' <- x
1019-
case B.toList x' of
1020-
[Image attr alt (src,tit)]
1021-
| not (null alt) ->
1022-
-- the fig: at beginning of title indicates a figure
1023-
return $ B.singleton
1024-
$ Image attr alt (src, "fig:" <> tit)
1025-
_ -> return x'
1026-
| otherwise = x
1027-
result <- implicitFigures . trimInlinesF <$> inlines1
1028-
option (B.plain <$> result)
1016+
1017+
result <- trimInlinesF <$> inlines1
1018+
let figureOr constr inlns =
1019+
case B.toList inlns of
1020+
[Image attr figCaption (src, tit)]
1021+
| extensionEnabled Ext_implicit_figures exts
1022+
, not (null figCaption) -> do
1023+
B.simpleFigureWith attr (B.fromList figCaption) src tit
1024+
1025+
_ -> constr inlns
1026+
1027+
option (figureOr B.plain <$> result)
10291028
$ try $ do
10301029
newline
10311030
(mempty <$ blanklines)
@@ -1047,7 +1046,7 @@ para = try $ do
10471046
if divLevel > 0
10481047
then lookAhead divFenceEnd
10491048
else mzero
1050-
return $ B.para <$> result
1049+
return $ figureOr B.para <$> result
10511050

10521051
plain :: PandocMonad m => MarkdownParser m (F Blocks)
10531052
plain = fmap B.plain . trimInlinesF <$> inlines1

src/Text/Pandoc/Readers/MediaWiki.hs

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -201,7 +201,12 @@ para = do
201201
contents <- trimInlines . mconcat <$> many1 inline
202202
if F.all (==Space) contents
203203
then return mempty
204-
else return $ B.para contents
204+
else case B.toList contents of
205+
-- For the MediaWiki format all images are considered figures
206+
[Image attr figureCaption (src, title)] ->
207+
return $ B.simpleFigureWith
208+
attr (B.fromList figureCaption) src title
209+
_ -> return $ B.para contents
205210

206211
table :: PandocMonad m => MWParser m Blocks
207212
table = do
@@ -631,7 +636,7 @@ image = try $ do
631636
let attr = ("", [], kvs)
632637
caption <- (B.str fname <$ sym "]]")
633638
<|> try (char '|' *> (mconcat <$> manyTill inline (sym "]]")))
634-
return $ B.imageWith attr fname ("fig:" <> stringify caption) caption
639+
return $ B.imageWith attr fname (stringify caption) caption
635640

636641
imageOption :: PandocMonad m => MWParser m Text
637642
imageOption = try $ char '|' *> opt

src/Text/Pandoc/Readers/Org/Blocks.hs

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -474,15 +474,16 @@ figure = try $ do
474474
figCaption = fromMaybe mempty $ blockAttrCaption figAttrs
475475
figKeyVals = blockAttrKeyValues figAttrs
476476
attr = (figLabel, mempty, figKeyVals)
477-
figTitle = (if isFigure then withFigPrefix else id) figName
478-
in
479-
B.para . B.imageWith attr imgSrc figTitle <$> figCaption
480-
481-
withFigPrefix :: Text -> Text
482-
withFigPrefix cs =
483-
if "fig:" `T.isPrefixOf` cs
484-
then cs
485-
else "fig:" <> cs
477+
in if isFigure
478+
then (\c ->
479+
B.simpleFigureWith
480+
attr c imgSrc (unstackFig figName)) <$> figCaption
481+
else B.para . B.imageWith attr imgSrc figName <$> figCaption
482+
unstackFig :: Text -> Text
483+
unstackFig figName =
484+
if "fig:" `T.isPrefixOf` figName
485+
then T.drop 4 figName
486+
else figName
486487

487488
-- | Succeeds if looking at the end of the current paragraph
488489
endOfParagraph :: Monad m => OrgParser m ()

src/Text/Pandoc/Readers/RST.hs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -725,8 +725,8 @@ directive' = do
725725
"figure" -> do
726726
(caption, legend) <- parseFromString' extractCaption body'
727727
let src = escapeURI $ trim top
728-
return $ B.para (B.imageWith (imgAttr "figclass") src "fig:"
729-
caption) <> legend
728+
return $ B.simpleFigureWith
729+
(imgAttr "figclass") caption src "" <> legend
730730
"image" -> do
731731
let src = escapeURI $ trim top
732732
let alt = B.str $ maybe "image" trim $ lookup "alt" fields

src/Text/Pandoc/Shared.hs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -949,6 +949,7 @@ blockToInlines (Table _ _ _ (TableHead _ hbd) bodies (TableFoot _ fbd)) =
949949
unTableBodies = concatMap unTableBody
950950
blockToInlines (Div _ blks) = blocksToInlines' blks
951951
blockToInlines Null = mempty
952+
blockToInlines (Figure _ _ body) = blocksToInlines' body
952953

953954
blocksToInlinesWithSep :: Inlines -> [Block] -> Inlines
954955
blocksToInlinesWithSep sep =

src/Text/Pandoc/Writers/AsciiDoc.hs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -149,9 +149,8 @@ blockToAsciiDoc opts (Div (id',"section":_,_)
149149
blockToAsciiDoc opts (Plain inlines) = do
150150
contents <- inlineListToAsciiDoc opts inlines
151151
return $ contents <> blankline
152-
blockToAsciiDoc opts (Para [Image attr alternate (src,tgt)])
152+
blockToAsciiDoc opts (SimpleFigure attr alternate (src, tit))
153153
-- image::images/logo.png[Company logo, title="blah"]
154-
| Just tit <- T.stripPrefix "fig:" tgt
155154
= (\args -> "image::" <> args <> blankline) <$>
156155
imageArguments opts attr alternate src tit
157156
blockToAsciiDoc opts (Para inlines) = do
@@ -187,7 +186,7 @@ blockToAsciiDoc opts (Header level (ident,_,_) inlines) = do
187186
return $ identifier $$
188187
nowrap (text (replicate (level + 1) '=') <> space <> contents) <>
189188
blankline
190-
189+
blockToAsciiDoc opts (Figure attr _ body) = blockToAsciiDoc opts $ Div attr body
191190
blockToAsciiDoc _ (CodeBlock (_,classes,_) str) = return $ flush (
192191
if null classes
193192
then "...." $$ literal str $$ "...."

0 commit comments

Comments
 (0)