addFormatting :: Formatting -> Formatted -> Formatted
addFormatting f =
  addDisplay . addLink . addSuffix . pref . quote . font . text_case . strip_periods
  where addLink i = case hyperlink f of
                         ""  -> i
                         url -> Formatted [Link nullAttr (unFormatted i) (url, "")]
        pref i = case prefix f of
                      "" -> i
                      x  -> formatString x <> i
        addSuffix i
          | T.null (suffix f)       = i
          | maybe False (isPunct . fst) (T.uncons (suffix f))
          , case lastInline (unFormatted i) of {Just c | isPunct c -> True; _ -> False}
                                  = i <> formatString (T.tail $ suffix f)
          | otherwise             = i <> formatString (suffix f)

        strip_periods (Formatted ils) = Formatted (walk removePeriod ils)
        removePeriod (Str xs) | stripPeriods f = Str (T.filter (/='.') xs)
        removePeriod x        = x

        quote (Formatted [])  = Formatted []
        quote (Formatted ils) =
                    case quotes f of
                         NoQuote     -> Formatted $ valign ils
                         NativeQuote -> Formatted
                                  [Span ("",["csl-inquote"],[]) ils]
                         _           -> Formatted [Quoted DoubleQuote $ valign ils]

        addDisplay (Formatted []) = Formatted []
        addDisplay (Formatted ils) =
                     case display f of
                          "block"    -> Formatted (LineBreak : ils ++
                                                       [LineBreak])
                          _          -> Formatted ils

        font (Formatted ils)
          | noDecor f    = Formatted [Span ("",["nodecor"],[]) ils]
          | otherwise    = Formatted $ font_variant . font_style .  font_weight $ ils
        font_variant ils =
          case fontVariant f of
               "small-caps" -> [SmallCaps ils]
               _            -> ils
        font_style ils =
          case fontStyle f of
               "italic"  -> [Emph ils]
               "oblique" -> [Emph ils]
               _         -> ils
        font_weight ils =
          case fontWeight f of
               "bold" -> [Strong ils]
               _      -> ils

        text_case (Formatted []) = Formatted []
        text_case (Formatted ils@(i:is'))
          | noCase f  = Formatted [Span ("",["nocase"],[]) ils]
          | otherwise = Formatted $
              case textCase f of
                   "lowercase"        -> walk lowercaseStr ils
                   "uppercase"        -> walk uppercaseStr ils
                   "capitalize-all"   -> walk capitalizeStr ils
                   "title"            -> titlecase ils
                   "capitalize-first"
                     -> case i of
                             Str cs -> Str (capitalize cs) : is'
                             _ -> unTitlecase [i] ++ is'
                   "sentence"         -> unTitlecase ils
                   _                  -> ils

        lowercaseStr (Str xs) = Str $ T.toLower xs
        lowercaseStr x        = x
        uppercaseStr (Str xs) = Str $ T.toUpper xs
        uppercaseStr x        = x
        capitalizeStr (Str xs) = Str $ capitalize xs
        capitalizeStr x        = x

        valign [] = []
        valign ils
          | "sup"      <- verticalAlign f = [Superscript ils]
          | "sub"      <- verticalAlign f = [Subscript   ils]
          | "baseline" <- verticalAlign f =
                              [Span ("",["csl-baseline"],[]) ils]
          | otherwise                     = ils
addFormatting :: Formatting -> Formatted -> Formatted
addFormatting f =
  addDisplay . addLink . addSuffix . pref . quote . font . text_case . strip_periods
  where addLink i = case hyperlink f of
                         ""  -> i
                         url -> Formatted [Link nullAttr (unFormatted i) (url, "")]
        pref i = case prefix f of
                      "" -> i
                      x  -> formatString x <> i
        addSuffix i
          | T.null (suffix f)       = i
          | maybe False (isPunct . fst) (T.uncons (suffix f))
          , case lastInline (unFormatted i) of {Just c | isPunct c -> True; _ -> False}
                                  = i <> formatString (T.tail $ suffix f)
          | otherwise             = i <> formatString (suffix f)

        strip_periods (Formatted ils) = Formatted (walk removePeriod ils)
        removePeriod (Str xs) | stripPeriods f = Str (T.filter (/='.') xs)
        removePeriod x        = x

        quote (Formatted [])  = Formatted []
        quote (Formatted ils) =
                    case quotes f of
                         NoQuote     -> Formatted $ valign ils
                         NativeQuote -> Formatted
                                  [Span ("",["csl-inquote"],[]) ils]
                         _           -> Formatted [Quoted DoubleQuote $ valign ils]

        addDisplay (Formatted []) = Formatted []
        addDisplay (Formatted ils) =
                     case display f of
                          "block"    -> Formatted (LineBreak : ils ++
                                                       [LineBreak])
                          _          -> Formatted ils

        font (Formatted ils)
          | noDecor f    = Formatted [Span ("",["nodecor"],[]) ils]
          | otherwise    = Formatted $ font_variant . font_style .  font_weight $ ils
        font_variant ils =
          case fontVariant f of
               "small-caps" -> [SmallCaps ils]
               _            -> ils
        font_style ils =
          case fontStyle f of
               "italic"  -> [Emph ils]
               "oblique" -> [Emph ils]
               _         -> ils
        font_weight ils =
          case fontWeight f of
               "bold" -> [Strong ils]
               _      -> ils

        text_case (Formatted []) = Formatted []
        text_case (Formatted ils@(i:is'))
          | noCase f  = Formatted [Span ("",["nocase"],[]) ils]
          | otherwise = Formatted $
              case textCase f of
                   "lowercase"        -> walk lowercaseStr ils
                   "uppercase"        -> walk uppercaseStr ils
                   "capitalize-all"   -> walk capitalizeStr ils
                   "title"            -> titlecase ils
                   "capitalize-first"
                     -> case i of
                             Str cs -> Str (capitalize cs) : is'
                             _ -> unTitlecase [i] ++ is'
                   "sentence"         -> unTitlecase ils
                   _                  -> ils

        lowercaseStr (Str xs) = Str $ T.toLower xs
        lowercaseStr x        = x
        uppercaseStr (Str xs) = Str $ T.toUpper xs
        uppercaseStr x        = x
        capitalizeStr (Str xs) = Str $ capitalize xs
        capitalizeStr x        = x

        valign [] = []
        valign ils
          | "sup"      <- verticalAlign f = [Superscript ils]
          | "sub"      <- verticalAlign f = [Subscript   ils]
          | "baseline" <- verticalAlign f =
                              [Span ("",["csl-baseline"],[]) ils]
          | otherwise                     = ils
addFormatting f =
  addDisplay . addLink . addSuffix . pref . quote . font . text_case . strip_periods
  where addLink i = case hyperlink f of
                         ""  -> i
                         url -> Formatted [Link nullAttr (unFormatted i) (url, "")]
        pref i = case prefix f of
                      "" -> i
                      x  -> formatString x <> i
        addSuffix i
          | T.null (suffix f)       = i
          | maybe False (isPunct . fst) (T.uncons (suffix f))
          , case lastInline (unFormatted i) of {Just c | isPunct c -> True; _ -> False}
                                  = i <> formatString (T.tail $ suffix f)
          | otherwise             = i <> formatString (suffix f)

        strip_periods (Formatted ils) = Formatted (walk removePeriod ils)
        removePeriod (Str xs) | stripPeriods f = Str (T.filter (/='.') xs)
        removePeriod x        = x

        quote (Formatted [])  = Formatted []
        quote (Formatted ils) =
                    case quotes f of
                         NoQuote     -> Formatted $ valign ils
                         NativeQuote -> Formatted
                                  [Span ("",["csl-inquote"],[]) ils]
                         _           -> Formatted [Quoted DoubleQuote $ valign ils]

        addDisplay (Formatted []) = Formatted []
        addDisplay (Formatted ils) =
                     case display f of
                          "block"    -> Formatted (LineBreak : ils ++
                                                       [LineBreak])
                          _          -> Formatted ils

        font (Formatted ils)
          | noDecor f    = Formatted [Span ("",["nodecor"],[]) ils]
          | otherwise    = Formatted $ font_variant . font_style .  font_weight $ ils
        font_variant ils =
          case fontVariant f of
               "small-caps" -> [SmallCaps ils]
               _            -> ils
        font_style ils =
          case fontStyle f of
               "italic"  -> [Emph ils]
               "oblique" -> [Emph ils]
               _         -> ils
        font_weight ils =
          case fontWeight f of
               "bold" -> [Strong ils]
               _      -> ils

        text_case (Formatted []) = Formatted []
        text_case (Formatted ils@(i:is'))
          | noCase f  = Formatted [Span ("",["nocase"],[]) ils]
          | otherwise = Formatted $
              case textCase f of
                   "lowercase"        -> walk lowercaseStr ils
                   "uppercase"        -> walk uppercaseStr ils
                   "capitalize-all"   -> walk capitalizeStr ils
                   "title"            -> titlecase ils
                   "capitalize-first"
                     -> case i of
                             Str cs -> Str (capitalize cs) : is'
                             _ -> unTitlecase [i] ++ is'
                   "sentence"         -> unTitlecase ils
                   _                  -> ils

        lowercaseStr (Str xs) = Str $ T.toLower xs
        lowercaseStr x        = x
        uppercaseStr (Str xs) = Str $ T.toUpper xs
        uppercaseStr x        = x
        capitalizeStr (Str xs) = Str $ capitalize xs
        capitalizeStr x        = x

        valign [] = []
        valign ils
          | "sup"      <- verticalAlign f = [Superscript ils]
          | "sub"      <- verticalAlign f = [Subscript   ils]
          | "baseline" <- verticalAlign f =
                              [Span ("",["csl-baseline"],[]) ils]
          | otherwise                     = ils