@@ -31,6 +31,7 @@ import Data.Char (isSpace, toUpper)
3131import Data.Functor
3232import Data.List (dropWhileEnd, intersperse)
3333import Data.List.Split (splitOn)
34+import qualified Data.Map as M
3435import Data.Text (Text)
3536import Data.Default
3637import Data.Maybe
@@ -48,12 +49,14 @@ type FB2 m = StateT FB2State m
4849data FB2State = FB2State{ fb2SectionLevel :: Int
4950 , fb2Meta :: Meta
5051 , fb2Authors :: [String]
52+ , fb2Notes :: M.Map String Blocks
5153 } deriving Show
5254
5355instance Default FB2State where
5456 def = FB2State{ fb2SectionLevel = 1
5557 , fb2Meta = mempty
5658 , fb2Authors = []
59+ , fb2Notes = M.empty
5760 }
5861
5962instance HasMeta FB2State where
@@ -107,16 +110,56 @@ parseSubtitle e = headerWith ("", ["unnumbered"], []) <$> gets fb2SectionLevel <
107110parseRootElement :: PandocMonad m => Element -> FB2 m Blocks
108111parseRootElement e =
109112 case qName $ elName e of
110− "FictionBook" -> mconcat <$> mapM parseFictionBookChild (elChildren e)
113+ "FictionBook" -> do
114+ -- Parse notes before parsing the rest of the content.
115+ case filterChild isNotesBody e of
116+ Nothing -> pure ()
117+ Just notesBody -> parseNotesBody notesBody
118+ -- Parse metadata and content
119+ mconcat <$> mapM parseFictionBookChild (elChildren e)
111120 name -> report (UnexpectedXmlElement name "root") $> mempty
112121
122+-- | Parse notes
123+parseNotesBody :: PandocMonad m => Element -> FB2 m ()
124+parseNotesBody e = mempty <$ mapM parseNotesBodyChild (elChildren e)
125+
126+-- | Parse a child of @\<body name="notes">@ element.
127+parseNotesBodyChild :: PandocMonad m => Element -> FB2 m ()
128+parseNotesBodyChild e =
129+ case qName $ elName e of
130+ "section" -> parseNote e
131+ _ -> pure ()
132+
133+isNotesBody :: Element -> Bool
134+isNotesBody e =
135+ qName (elName e) == "body" &&
136+ findAttr (unqual "name") e == Just "notes"
137+
138+parseNote :: PandocMonad m => Element -> FB2 m ()
139+parseNote e =
140+ case findAttr (unqual "id") e of
141+ Nothing -> pure ()
142+ Just sectionId -> do
143+ content <- mconcat <$> mapM parseSectionChild (dropTitle $ elChildren e)
144+ oldNotes <- gets fb2Notes
145+ modify $ \s -> s { fb2Notes = M.insert ("#" ++ sectionId) content oldNotes }
146+ pure ()
147+ where
148+ isTitle x = qName (elName x) == "title"
149+ dropTitle (x:xs) = if isTitle x
150+ then xs -- Drop note section <title> if present
151+ else (x:xs)
152+ dropTitle [] = []
153+
113154-- | Parse a child of @\<FictionBook>@ element.
114155parseFictionBookChild :: PandocMonad m => Element -> FB2 m Blocks
115156parseFictionBookChild e =
116157 case qName $ elName e of
117158 "stylesheet" -> pure mempty -- stylesheet is ignored
118159 "description" -> mempty <$ mapM_ parseDescriptionChild (elChildren e)
119− "body" -> mconcat <$> mapM parseBodyChild (elChildren e)
160+ "body" -> if isNotesBody e
161+ then pure mempty
162+ else mconcat <$> mapM parseBodyChild (elChildren e)
120163 "binary" -> mempty <$ parseBinaryElement e
121164 name -> report (UnexpectedXmlElement name "FictionBook") $> mempty
122165
@@ -333,8 +376,13 @@ parseNamedStyleChild x = parseInline x
333376parseLinkType :: PandocMonad m => Element -> FB2 m Inlines
334377parseLinkType e = do
335378 content <- mconcat <$> mapM parseStyleLinkType (elContent e)
379+ notes <- gets fb2Notes
336380 case findAttr (QName "href" (Just "http://www.w3.org/1999/xlink") Nothing) e of
337− Just href -> pure $ link href "" content
381+ Just href -> case findAttr (QName "type" Nothing Nothing) e of
382+ Just "note" -> case M.lookup href notes of
383+ Nothing -> pure $ link href "" content
384+ Just contents -> pure $ note contents
385+ _ -> pure $ link href "" content
338386 Nothing -> throwError $ PandocParseError "Couldn't parse FB2 file: link without required href."
339387
340388-- | Parse @styleLinkType@