diff --git a/src/Text/Pandoc/Writers/Typst.hs b/src/Text/Pandoc/Writers/Typst.hs index 42ca465340a6..34c22c4ec3f7 100644 --- a/src/Text/Pandoc/Writers/Typst.hs +++ b/src/Text/Pandoc/Writers/Typst.hs @@ -33,7 +33,7 @@ import Control.Monad.State ( StateT, evalStateT, gets, modify ) import Text.Pandoc.Writers.Shared ( lookupMetaInlines, lookupMetaString, metaToContext, defField, resetField, setupTranslations ) -import Text.Pandoc.Shared (isTightList, orderedListMarkers, tshow) +import Text.Pandoc.Shared (isTightList, orderedListMarkers, stringify, tshow) import Text.Pandoc.Highlighting (highlight, formatTypstBlock, formatTypstInline, styleToTypst) import Text.Pandoc.Translations (Term(Abstract), translateTerm) @@ -365,10 +365,12 @@ blockToTypst block = opts <- gets stOptions contents <- case blocks of -- don't need #box around block-level image - [Para [Image attr _ (src, _)]] + [Para [Image attr altInlines (src, _)]] -> pure $ mkImage opts False src attr - [Plain [Image attr _ (src, _)]] + (imageAltText attr altInlines) + [Plain [Image attr altInlines (src, _)]] -> pure $ mkImage opts False src attr + (imageAltText attr altInlines) _ -> brackets <$> blocksToTypst blocks let lab = toLabel FreestandingLabel ident return $ "#figure(" <> nest 2 ((contents <> ",") @@ -529,16 +531,21 @@ inlineToTypst inline = (if inlines == [Str src] then mempty else nowrap $ brackets contents) - Image attr _inlines (src,_tit) -> do + Image attr inlines (src,_tit) -> do opts <- gets stOptions - pure $ mkImage opts True src attr + pure $ mkImage opts True src attr (imageAltText attr inlines) Note blocks -> do contents <- blocksToTypst blocks return $ "#footnote" <> brackets (chomp contents) +-- Extract alt text: prefer explicit alt attribute, fall back to caption +imageAltText :: Attr -> [Inline] -> Text +imageAltText (_, _, kvs) caption = + fromMaybe (stringify caption) (lookup "alt" kvs) + -- see #9104; need box or image is treated as block-level -mkImage :: WriterOptions -> Bool -> Text -> Attr -> Doc Text -mkImage opts useBox src attr +mkImage :: WriterOptions -> Bool -> Text -> Attr -> Text -> Doc Text +mkImage opts useBox src attr altText | useBox = "#box" <> parens coreImage | otherwise = coreImage where @@ -552,11 +559,14 @@ mkImage opts useBox src attr (case dimension Width attr of Nothing -> mempty Just dim -> ", width: " <> showDim dim) + altAttr + | T.null altText = mempty + | otherwise = ", alt: " <> doubleQuoted altText isData = "data:" `T.isPrefixOf` src' dataSvg = " src' <> "\" />" coreImage - | isData = "image.decode" <> parens(doubleQuoted dataSvg <> dimAttrs) - | otherwise = "image" <> parens (doubleQuoted src' <> dimAttrs) + | isData = "image.decode" <> parens(doubleQuoted dataSvg <> dimAttrs <> altAttr) + | otherwise = "image" <> parens (doubleQuoted src' <> dimAttrs <> altAttr) textstyle :: PandocMonad m => Doc Text -> [Inline] -> TW m (Doc Text) textstyle s inlines = do diff --git a/test/command/typst-image-alt.md b/test/command/typst-image-alt.md new file mode 100644 index 000000000000..eeb48259f6b1 --- /dev/null +++ b/test/command/typst-image-alt.md @@ -0,0 +1,83 @@ +Typst writer: image alt text support + +``` +% pandoc -f markdown -t typst +![A cat sleeping on a couch](cat.png) +^D +#figure(image("cat.png", alt: "A cat sleeping on a couch"), + caption: [ + A cat sleeping on a couch + ] +) + +``` + +Inline image with alt text: + +``` +% pandoc -f markdown -t typst +Here is an icon ![small logo](icon.png) in the text. +^D +Here is an icon #box(image("icon.png", alt: "small logo")) in the text. + +``` + +Image with explicit alt attribute (different from caption): + +``` +% pandoc -f markdown -t typst +![Figure caption](diagram.png){alt="Detailed description for accessibility"} +^D +#figure(image("diagram.png", alt: "Detailed description for accessibility"), + caption: [ + Figure caption + ] +) + +``` + +Image with no alt text (should omit alt parameter): + +``` +% pandoc -f markdown -t typst +![](empty.png) +^D +#box(image("empty.png")) + +``` + +Inline image with dimensions should preserve alt: + +``` +% pandoc -f markdown -t typst +Here is ![small icon](icon.png){width=20px height=20px} inline. +^D +Here is +#box(image("icon.png", height: 0.20833in, width: 0.20833in, alt: "small icon")) +inline. + +``` + +Alt text with special characters (quotes and backslashes): + +``` +% pandoc -f markdown -t typst +![Caption](test.png){alt="A \"quoted\" phrase and C:\\path\\file"} +^D +#figure(image("test.png", alt: "A \"quoted\" phrase and C:\\path\\file"), + caption: [ + Caption + ] +) + +``` + +Data URI image with alt text: + +``` +% pandoc -f html -t typst +A small red dot +^D +#box(image.decode("", alt: "A small red dot")) + +``` diff --git a/test/writer.typst b/test/writer.typst index 7ce5d05f1520..873628977e26 100644 --- a/test/writer.typst +++ b/test/writer.typst @@ -837,13 +837,13 @@ or here: From "Voyage dans la Lune" by Georges Melies (1902): -#figure(image("lalune.jpg"), +#figure(image("lalune.jpg", alt: "lalune"), caption: [ lalune ] ) -Here is a movie #box(image("movie.jpg")) icon. +Here is a movie #box(image("movie.jpg", alt: "movie")) icon. #horizontalrule