@@ -84,9 +84,10 @@ import Text.Pandoc.Walk
8484import Text.Pandoc.Readers.Docx.Parse
8585import Text.Pandoc.Readers.Docx.Lists
8686import Text.Pandoc.Readers.Docx.Reducible
87+import Text.Pandoc.Readers.Docx.TexChar
8788import Text.Pandoc.Shared
88−import Data.Maybe (mapMaybe)
89−import Data.List (delete, isPrefixOf, (\\))
89+import Data.Maybe (mapMaybe, fromMaybe)
90+import Data.List (delete, isPrefixOf, (\\), intercalate)
9091import qualified Data.ByteString as BS
9192import qualified Data.ByteString.Lazy as B
9293import Data.ByteString.Base64 (encode)
@@ -94,6 +95,7 @@ import System.FilePath (combine)
9495import qualified Data.Map as M
9596import Control.Monad.Reader
9697import Control.Monad.State
98+import Text.Printf (printf)
9799
98100readDocx :: ReaderOptions
99101 -> B.ByteString
@@ -103,7 +105,8 @@ readDocx opts bytes =
103105 Just docx -> Pandoc nullMeta (docxToBlocks opts docx)
104106 Nothing -> error $ "couldn't parse docx file"
105107
106−data DState = DState { docxAnchorMap :: M.Map String String }
108+data DState = DState { docxAnchorMap :: M.Map String String
109+ , docxInTexSubscript :: Bool }
107110
108111data DEnv = DEnv { docxOptions :: ReaderOptions
109112 , docxDocument :: Docx}
@@ -115,6 +118,14 @@ updateDState f = do
115118 st <- get
116119 put $ f st
117120
121+withDState :: DState -> DocxContext a -> DocxContext a
122+withDState ds dctx = do
123+ ds' <- get
124+ updateDState (\_ -> ds)
125+ dctx' <- dctx
126+ put ds'
127+ return dctx'
128+
118129evalDocxContext :: DocxContext a -> DEnv -> DState -> a
119130evalDocxContext ctx env st = evalState (runReaderT ctx env) st
120131
@@ -318,6 +329,158 @@ parPartToInlines (ExternalHyperLink relid runs) = do
318329 [Link rs (target, "")]
319330 Nothing ->
320331 [Link rs ("", "")]
332+parPartToInlines (PlainOMath omath) = do
333+ s <- oMathToTexString omath
334+ return [Math InlineMath s]
335+
336+oMathToTexString :: OMath -> DocxContext String
337+oMathToTexString (OMath omathElems) = do
338+ ss <- mapM oMathElemToTexString omathElems
339+ return $ intercalate " " ss
340+oMathElemToTexString :: OMathElem -> DocxContext String
341+oMathElemToTexString (Accent style base) | Just c <- accentChar style = do
342+ baseString <- baseToTexString base
343+ return $ case lookupTexChar c of
344+ s@('\\' : _) -> printf "%s{%s}" s baseString
345+ _ -> printf "\\acute{%s}" baseString -- we default.
346+oMathElemToTexString (Accent _ base) =
347+ baseToTexString base >>= (\s -> return $ printf "\\acute{%s}" s)
348+oMathElemToTexString (Bar style base) = do
349+ baseString <- baseToTexString base
350+ return $ case barPos style of
351+ Top -> printf "\\overline{%s}" baseString
352+ Bottom -> printf "\\underline{%s}" baseString
353+oMathElemToTexString (Box base) = baseToTexString base
354+oMathElemToTexString (BorderBox base) =
355+ baseToTexString base >>= (\s -> return $ printf "\\boxed{%s}" s)
356+oMathElemToTexString (Delimiter dPr bases) = do
357+ let beg = fromMaybe '(' (delimBegChar dPr)
358+ end = fromMaybe ')' (delimEndChar dPr)
359+ sep = fromMaybe '|' (delimSepChar dPr)
360+ left = "\\left" ++ lookupTexChar beg
361+ right = "\\right" ++ lookupTexChar end
362+ mid = "\\middle" ++ lookupTexChar sep
363+ baseStrings <- mapM baseToTexString bases
364+ return $ printf "%s %s %s"
365+ left
366+ (intercalate (" " ++ mid ++ " ") baseStrings)
367+ right
368+oMathElemToTexString (EquationArray bases) = do
369+ baseStrings <- mapM baseToTexString bases
370+ inSub <- gets docxInTexSubscript
371+ return $
372+ if inSub
373+ then
374+ printf "\\substack{%s}" (intercalate "\\\\ " baseStrings)
375+ else
376+ printf
377+ "\\begin{aligned}\n%s\n\\end{aligned}"
378+ (intercalate "\\\\\n" baseStrings)
379+oMathElemToTexString (Fraction num denom) = do
380+ numString <- concatMapM oMathElemToTexString num
381+ denString <- concatMapM oMathElemToTexString denom
382+ return $ printf "\\frac{%s}{%s}" numString denString
383+oMathElemToTexString (Function fname base) = do
384+ fnameString <- concatMapM oMathElemToTexString fname
385+ baseString <- baseToTexString base
386+ return $ printf "%s %s" fnameString baseString
387+oMathElemToTexString (Group style base)
388+ | Just c <- groupChr style
389+ , grouper <- lookupTexChar c
390+ , notElem grouper ["\\overbrace", "\\underbrace"]
391+ = do
392+ baseString <- baseToTexString base
393+ return $ case groupPos style of
394+ Just Top -> printf "\\overset{%s}{%s}" grouper baseString
395+ _ -> printf "\\underset{%s}{%s}" grouper baseString
396+oMathElemToTexString (Group style base) = do
397+ baseString <- baseToTexString base
398+ return $ case groupPos style of
399+ Just Top -> printf "\\overbrace{%s}" baseString
400+ _ -> printf "\\underbrace{%s}" baseString
401+oMathElemToTexString (LowerLimit base limElems) = do
402+ baseString <- baseToTexString base
403+ lim <- concatMapM oMathElemToTexString limElems
404+ -- we want to make sure to replace the `\rightarrow` with `\to`
405+ let arrowToTo :: String -> String
406+ arrowToTo "" = ""
407+ arrowToTo s | "\\rightarrow" `isPrefixOf` s =
408+ "\\to" ++ (arrowToTo $ drop (length "\\rightarrow") s)
409+ arrowToTo (c:cs) = c : arrowToTo cs
410+ lim' = arrowToTo lim
411+ return $ case baseString of
412+ "lim" -> printf "\\lim_{%s}" lim'
413+ "max" -> printf "\\max_{%s}" lim'
414+ "min" -> printf "\\min_{%s}" lim'
415+ _ -> printf "\\operatorname*{%s}_{%s}" baseString lim'
416+oMathElemToTexString (UpperLimit base limElems) = do
417+ baseString <- baseToTexString base
418+ lim <- concatMapM oMathElemToTexString limElems
419+ -- we want to make sure to replace the `\rightarrow` with `\to`
420+ let arrowToTo :: String -> String
421+ arrowToTo "" = ""
422+ arrowToTo s | "\\rightarrow" `isPrefixOf` s =
423+ "\\to" ++ (arrowToTo $ drop (length "\\rightarrow") s)
424+ arrowToTo (c:cs) = c : arrowToTo cs
425+ lim' = arrowToTo lim
426+ return $ case baseString of
427+ "lim" -> printf "\\lim^{%s}" lim'
428+ "max" -> printf "\\max^{%s}" lim'
429+ "min" -> printf "\\min^{%s}" lim'
430+ _ -> printf "\\operatorname*{%s}^{%s}" baseString lim'
431+oMathElemToTexString (Matrix bases) = do
432+ let rowString :: [Base] -> DocxContext String
433+ rowString bs = liftM (intercalate " & ") (mapM baseToTexString bs)
434+
435+ s <- liftM (intercalate " \\\\\n")(mapM rowString bases)
436+ return $ printf "\\begin{matrix}\n%s\n\\end{matrix}" s
437+oMathElemToTexString (NAry style sub sup base) | Just c <- nAryChar style = do
438+ ds <- gets (\s -> s{docxInTexSubscript = True})
439+ subString <- withDState ds $ concatMapM oMathElemToTexString sub
440+ supString <- concatMapM oMathElemToTexString sup
441+ baseString <- baseToTexString base
442+ return $ case M.lookup c uniconvMap of
443+ Just s@('\\':_) -> printf "%s_{%s}^{%s}{%s}"
444+ s subString supString baseString
445+ _ -> printf "\\operatorname*{%s}_{%s}^{%s}{%s}"
446+ [c] subString supString baseString
447+oMathElemToTexString (NAry _ sub sup base) = do
448+ subString <- concatMapM oMathElemToTexString sub
449+ supString <- concatMapM oMathElemToTexString sup
450+ baseString <- baseToTexString base
451+ return $ printf "\\int_{%s}^{%s}{%s}"
452+ subString supString baseString
453+oMathElemToTexString (Radical degree base) = do
454+ degString <- concatMapM oMathElemToTexString degree
455+ baseString <- baseToTexString base
456+ return $ case trim degString of
457+ "" -> printf "\\sqrt{%s}" baseString
458+ _ -> printf "\\sqrt[%s]{%s}" degString baseString
459+oMathElemToTexString (PreSubSuper sub sup base) = do
460+ subString <- concatMapM oMathElemToTexString sub
461+ supString <- concatMapM oMathElemToTexString sup
462+ baseString <- baseToTexString base
463+ return $ printf "_{%s}^{%s}%s" subString supString baseString
464+oMathElemToTexString (Sub base sub) = do
465+ baseString <- baseToTexString base
466+ subString <- concatMapM oMathElemToTexString sub
467+ return $ printf "%s_{%s}" baseString subString
468+oMathElemToTexString (SubSuper base sub sup) = do
469+ baseString <- baseToTexString base
470+ subString <- concatMapM oMathElemToTexString sub
471+ supString <- concatMapM oMathElemToTexString sup
472+ return $ printf "%s_{%s}^{%s}" baseString subString supString
473+oMathElemToTexString (Super base sup) = do
474+ baseString <- baseToTexString base
475+ supString <- concatMapM oMathElemToTexString sup
476+ return $ printf "%s^{%s}" baseString supString
477+oMathElemToTexString (OMathRun _ run) = return $ stringToTex $ runToString run
478+oMathElemToTexString _ = return "[NOT IMPLEMENTED]"
479+
480+baseToTexString :: Base -> DocxContext String
481+baseToTexString (Base mathElems) =
482+ concatMapM oMathElemToTexString mathElems
483+
321484
322485isAnchorSpan :: Inline -> Bool
323486isAnchorSpan (Span (ident, classes, kvs) ils) =
@@ -445,6 +608,10 @@ bodyPartToBlocks (Tbl cap _ look (r:rs)) = do
445608 widths = replicate size 0 :: [Double]
446609
447610 return [Table caption alignments widths hdrCells cells]
611+bodyPartToBlocks (OMathPara _ maths) = do
612+ omaths <- mapM oMathToTexString maths
613+ return [Para $ map (\s -> Math DisplayMath s) omaths]
614+
448615
449616-- replace targets with generated anchors.
450617rewriteLink :: Inline -> DocxContext Inline
@@ -480,7 +647,8 @@ bodyToBlocks (Body bps) = do
480647
481648docxToBlocks :: ReaderOptions -> Docx -> [Block]
482649docxToBlocks opts d@(Docx (Document _ body) _ _ _ _) =
483− let dState = DState { docxAnchorMap = M.empty }
650+ let dState = DState { docxAnchorMap = M.empty
651+ , docxInTexSubscript = False}
484652 dEnv = DEnv { docxOptions = opts
485653 , docxDocument = d}
486654 in