diff --git a/hledger-lib/Hledger/Write/Ods.hs b/hledger-lib/Hledger/Write/Ods.hs
new file mode 100644
index 000000000..0b93e4d13
--- /dev/null
+++ b/hledger-lib/Hledger/Write/Ods.hs
@@ -0,0 +1,109 @@
+{- |
+Export table data as OpenDocument Spreadsheet
+.
+This format supports character encodings, fixed header rows and columns,
+number formatting, text styles, merged cells, formulas, hyperlinks.
+Currently we support Flat ODS, a plain uncompressed XML format.
+
+This is derived from
+-}
+module Hledger.Write.Ods where
+
+import qualified Data.Text.Lazy as TL
+import qualified Data.Text as T
+import Data.Text (Text)
+
+import qualified Data.Map as Map
+import Data.Foldable (fold)
+import Data.Map (Map)
+
+import qualified System.IO as IO
+import Text.Printf (printf)
+
+
+printFods ::
+ IO.TextEncoding -> Map Text ((Maybe Int, Maybe Int), [[Text]]) -> TL.Text
+printFods encoding tables =
+ let fileOpen =
+ map (map (\c -> case c of '\'' -> '"'; _ -> c)) $
+ printf "" (show encoding) :
+ "" :
+ []
+
+ fileClose =
+ "" :
+ []
+
+ tableConfig tableNames =
+ " " :
+ " " :
+ " " :
+ " " :
+ " " :
+ (fold $
+ flip Map.mapWithKey tableNames $ \tableName (mTopRow,mLeftColumn) ->
+ printf " " tableName :
+ (flip foldMap mLeftColumn $ \leftColumn ->
+ " 2" :
+ printf " %d" leftColumn :
+ printf " %d" leftColumn :
+ []) ++
+ (flip foldMap mTopRow $ \topRow ->
+ " 2" :
+ printf " %d" topRow :
+ printf " %d" topRow :
+ []) ++
+ " " :
+ []) ++
+ " " :
+ " " :
+ " " :
+ " " :
+ " " :
+ []
+
+ tableOpen name =
+ "" :
+ "" :
+ printf "" name :
+ []
+
+ tableClose =
+ "" :
+ "" :
+ "" :
+ []
+
+ in TL.unlines $ map (TL.fromStrict . T.pack) $
+ fileOpen ++
+ tableConfig (fmap fst tables) ++
+ (Map.toAscList tables >>= \(name,(_,table)) ->
+ tableOpen name ++
+ (table >>= \row ->
+ "" :
+ (row >>= \cell ->
+ "" :
+ printf "%s" cell :
+ "" :
+ []) ++
+ "" :
+ []) ++
+ tableClose) ++
+ fileClose
diff --git a/hledger-lib/hledger-lib.cabal b/hledger-lib/hledger-lib.cabal
index 33b22247c..da78d3f65 100644
--- a/hledger-lib/hledger-lib.cabal
+++ b/hledger-lib/hledger-lib.cabal
@@ -86,6 +86,7 @@ library
Hledger.Read.TimedotReader
Hledger.Read.TimeclockReader
Hledger.Write.Csv
+ Hledger.Write.Ods
Hledger.Reports
Hledger.Reports.ReportOptions
Hledger.Reports.ReportTypes
diff --git a/hledger-lib/package.yaml b/hledger-lib/package.yaml
index 6fdf07245..a7cbef5c4 100644
--- a/hledger-lib/package.yaml
+++ b/hledger-lib/package.yaml
@@ -149,6 +149,7 @@ library:
- Hledger.Read.TimedotReader
- Hledger.Read.TimeclockReader
- Hledger.Write.Csv
+ - Hledger.Write.Ods
- Hledger.Reports
- Hledger.Reports.ReportOptions
- Hledger.Reports.ReportTypes
diff --git a/hledger/Hledger/Cli/CliOptions.hs b/hledger/Hledger/Cli/CliOptions.hs
index 7ba115893..d79a7cf4c 100644
--- a/hledger/Hledger/Cli/CliOptions.hs
+++ b/hledger/Hledger/Cli/CliOptions.hs
@@ -718,7 +718,7 @@ defaultOutputFormat = "txt"
-- | All the output formats known by any command, for outputFormatFromOpts.
-- To automatically infer it from -o/--output-file, it needs to be listed here.
outputFormats :: [String]
-outputFormats = [defaultOutputFormat, "beancount", "csv", "json", "html", "sql", "tsv"]
+outputFormats = [defaultOutputFormat, "beancount", "csv", "json", "html", "sql", "tsv", "fods"]
-- | Get the output format from the --output-format option,
-- otherwise from a recognised file extension in the --output-file option,
diff --git a/hledger/Hledger/Cli/Commands/Balance.hs b/hledger/Hledger/Cli/Commands/Balance.hs
index cb7df27c6..745be9a91 100644
--- a/hledger/Hledger/Cli/Commands/Balance.hs
+++ b/hledger/Hledger/Cli/Commands/Balance.hs
@@ -282,6 +282,7 @@ import Data.Decimal (roundTo)
import Data.Default (def)
import Data.Function (on)
import Data.List (find, transpose, foldl')
+import qualified Data.Map as Map
import qualified Data.Set as S
import Data.Maybe (catMaybes, fromMaybe)
import Data.Text (Text)
@@ -296,10 +297,13 @@ import Text.Tabular.AsciiWide
(Header(..), Align(..), Properties(..), Cell(..), Table(..), TableOpts(..),
cellWidth, concatTables, renderColumns, renderRowB, renderTableByRowsB, textCell)
+import qualified System.IO as IO
+
import Hledger
import Hledger.Cli.CliOptions
import Hledger.Cli.Utils
import Hledger.Write.Csv (CSV, printCSV, printTSV)
+import Hledger.Write.Ods (printFods)
-- | Command line options for this command.
@@ -354,7 +358,7 @@ balancemode = hledgerCommandMode
,"'tidy' : every attribute in its own column"
])
-- output:
- ,outputFormatFlag ["txt","html","csv","tsv","json"]
+ ,outputFormatFlag ["txt","html","csv","tsv","json","fods"]
,outputFileFlag
]
)
@@ -398,6 +402,7 @@ balance opts@CliOpts{reportspec_=rspec} j = case balancecalc_ of
"tsv" -> \ropts1 -> printTSV . balanceReportAsCsv ropts1
-- "html" -> \ropts -> (<>"\n") . L.renderText . multiBalanceReportAsHtml ropts . balanceReportAsMultiBalanceReport ropts
"json" -> const $ (<>"\n") . toJsonText
+ "fods" -> \ropts1 -> printFods IO.localeEncoding . Map.singleton "Hledger" . (,) (Just 1, Nothing) . balanceReportAsCsv ropts1
_ -> error' $ unsupportedOutputFormatError fmt -- PARTIAL:
writeOutputLazyText opts $ render ropts report
where