git.iko.soy/typeable/pandoc
on master help sign in
Ms writer: support for fake smallcaps.
commit1af6faf2d4fce2cfa30cd4ebc15c1613c8f2abd8 authorJohn MacFarlane <jgm@berkeley.edu> dateThu 23 Mar 2017, 12:45 (10 years ago) parent8cf5c55
1 file changed+52−28
src/Text/Pandoc/Writers/Ms.hs+52−28
src/Text/Pandoc/Writers/Ms.hs+52−28
@@ -31,11 +31,8 @@ TODO:
3131
3232[ ] warning for non-rendered raw content
3333[ ] is there a better way to do strikeout?
34−[x] strong + em doesn't seem to work
3534[ ] super + subscript don't seem to work
3635[ ] options for hyperlink rendering (currently footnote)
37−[x] avoid note-in-note (which we currently get easily with
38− links in footnotes)
3936[ ] can we get prettier output using .B, etc. instead of
4037 the inline forms?
4138[ ] tight/loose list distinction
@@ -57,11 +54,7 @@ TODO:
5754 A big advantage of gropdf: it supports the tag
5855 \X'pdf: pdfpic file alignment width height line-length'
5956 and also seems to support bookmarks.
60−[x] avoid blank line after footnote marker when footnote has a
61− paragraph
62−[ ] better smallcaps support, see below...
6357[ ] add via groff option to PDF module
64−[ ] better handling of autolinks?
6558[ ] better handling of images, perhaps converting to eps when
6659 going to PDF?
6760[ ] better template, with configurable page number, table of contents,
@@ -83,12 +76,13 @@ import Text.Pandoc.Pretty
8376import Text.Pandoc.Builder (deleteMeta)
8477import Text.Pandoc.Class (PandocMonad)
8578import Control.Monad.State
86−import Data.Char ( isDigit )
79+import Data.Char ( isDigit, isLower, isUpper, toUpper )
8780import Text.TeXMath (writeEqn)
8881
8982data WriterState = WriterState { stHasInlineMath :: Bool
9083 , stNotes :: [Note]
9184 , stInNote :: Bool
85+ , stSmallCaps :: Bool
9286 , stFontFeatures :: Map.Map Char Bool
9387 }
9488
@@ -96,6 +90,7 @@ defaultWriterState :: WriterState
9690defaultWriterState = WriterState{ stHasInlineMath = False
9791 , stNotes = []
9892 , stInNote = False
93+ , stSmallCaps = False
9994 , stFontFeatures = Map.fromList [
10095 ('I',False)
10196 , ('B',False)
@@ -152,15 +147,24 @@ pandocToMs opts (Pandoc meta blocks) = do
152147 Just tpl -> return $ renderTemplate' tpl context
153148
154149-- | Association list of characters to escape.
155−manEscapes :: [(Char, String)]
156−manEscapes = [ ('\160', "\\ ")
157− , ('\'', "\\[aq]")
158− , ('’', "'")
159− , ('\x2014', "\\[em]")
160− , ('\x2013', "\\[en]")
161− , ('\x2026', "\\&...")
162− , ('|', "\\[u007C]") -- because we use | for inline math
163− ] ++ backslashEscapes "-@\\"
150+manEscapes :: Map.Map Char String
151+manEscapes = Map.fromList $
152+ [ ('\160', "\\ ")
153+ , ('\'', "\\[aq]")
154+ , ('’', "'")
155+ , ('\x2014', "\\[em]")
156+ , ('\x2013', "\\[en]")
157+ , ('\x2026', "\\&...")
158+ , ('|', "\\[u007C]") -- because we use | for inline math
159+ , ('-', "\\-")
160+ , ('@', "\\@")
161+ , ('\\', "\\\\")
162+ ]
163+
164+escapeChar :: Char -> String
165+escapeChar c = case Map.lookup c manEscapes of
166+ Just s -> s
167+ Nothing -> [c]
164168
165169-- | Escape | character, used to mark inline math, inside math.
166170escapeBar :: String -> String
@@ -170,15 +174,28 @@ escapeBar = concatMap go
170174
171175-- | Escape special characters for Ms.
172176escapeString :: String -> String
173−escapeString = escapeStringUsing manEscapes
177+escapeString = concatMap escapeChar
178+
179+toSmallCaps :: String -> String
180+toSmallCaps [] = []
181+toSmallCaps (c:cs)
182+ | isLower c = let (lowers,rest) = span isLower (c:cs)
183+ in "\\s-2" ++ escapeString (map toUpper lowers) ++
184+ "\\s0" ++ toSmallCaps rest
185+ | isUpper c = let (uppers,rest) = span isUpper (c:cs)
186+ in escapeString uppers ++ toSmallCaps rest
187+ | otherwise = escapeChar c ++ toSmallCaps cs
174188
175189-- | Escape a literal (code) section for Ms.
176190escapeCode :: String -> String
177−escapeCode = concat . intersperse "\n" . map escapeLine . lines where
178− escapeLine codeline =
179− case escapeStringUsing (manEscapes ++ backslashEscapes "\t ") codeline of
180− a@('.':_) -> "\\&" ++ a
181− b -> b
191+escapeCode = concat . intersperse "\n" . map escapeLine . lines
192+ where escapeCodeChar ' ' = "\\ "
193+ escapeCodeChar '\t' = "\\\t"
194+ escapeCodeChar c = escapeChar c
195+ escapeLine codeline =
196+ case concatMap escapeCodeChar codeline of
197+ a@('.':_) -> "\\&" ++ a
198+ b -> b
182199
183200-- We split inline lists into sentences, and print one sentence per
184201-- line. groff/troff treats the line-ending period differently.
@@ -384,9 +401,12 @@ inlineToMs opts (Superscript lst) = do
384401inlineToMs opts (Subscript lst) = do
385402 contents <- inlineListToMs opts lst
386403 return $ char '~' <> contents <> char '~'
387−inlineToMs opts (SmallCaps lst) = inlineListToMs opts lst -- not supported
388−-- but see https://lists.gnu.org/archive/html/groff/2015-01/msg00016.html
389−-- for a way to fake them
404+inlineToMs opts (SmallCaps lst) = do
405+ -- see https://lists.gnu.org/archive/html/groff/2015-01/msg00016.html
406+ modify $ \st -> st{ stSmallCaps = not (stSmallCaps st) }
407+ res <- inlineListToMs opts lst
408+ modify $ \st -> st{ stSmallCaps = not (stSmallCaps st) }
409+ return res
390410inlineToMs opts (Quoted SingleQuote lst) = do
391411 contents <- inlineListToMs opts lst
392412 return $ char '`' <> contents <> char '\''
@@ -397,7 +417,11 @@ inlineToMs opts (Cite _ lst) =
397417 inlineListToMs opts lst
398418inlineToMs _ (Code _ str) =
399419 withFontFeature 'C' (return $ text $ escapeCode str)
400−inlineToMs _ (Str str) = return $ text $ escapeString str
420+inlineToMs _ (Str str) = do
421+ smallcaps <- gets stSmallCaps
422+ if smallcaps
423+ then return $ text $ toSmallCaps str
424+ else return $ text $ escapeString str
401425inlineToMs opts (Math InlineMath str) = do
402426 modify $ \st -> st{ stHasInlineMath = True }
403427 res <- convertMath writeEqn InlineMath str
@@ -425,7 +449,7 @@ inlineToMs opts (Link _ txt (src, _)) = do
425449 case txt of
426450 [Str s]
427451 | escapeURI s == srcSuffix ->
428− return $ char '<' <> text (escapeString srcSuffix) <> char '>'
452+ return $ text (escapeString srcSuffix)
429453 _ | inNote -> do
430454 -- avoid a note in a note!
431455 contents <- inlineListToMs opts txt