{"id":13591824,"url":"https://github.com/mjul/docjure","last_synced_at":"2025-05-14T00:04:47.925Z","repository":{"id":855101,"uuid":"586817","full_name":"mjul/docjure","owner":"mjul","description":"Read and write Office documents from Clojure","archived":false,"fork":false,"pushed_at":"2025-01-04T20:56:03.000Z","size":642,"stargazers_count":633,"open_issues_count":36,"forks_count":130,"subscribers_count":21,"default_branch":"master","last_synced_at":"2025-04-13T01:53:46.801Z","etag":null,"topics":["clojure","excel","spreadsheet","xls","xlsx"],"latest_commit_sha":null,"homepage":"","language":"Clojure","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/mjul.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2010-03-30T16:07:09.000Z","updated_at":"2025-03-20T20:50:20.000Z","dependencies_parsed_at":"2024-10-24T16:09:50.226Z","dependency_job_id":"985466aa-83a8-4caf-9200-b0294acff2a1","html_url":"https://github.com/mjul/docjure","commit_stats":{"total_commits":242,"total_committers":34,"mean_commits":7.117647058823529,"dds":"0.35123966942148765","last_synced_commit":"f2c0204e3722ea253780ade2a20b65dfb95197e1"},"previous_names":[],"tags_count":14,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mjul%2Fdocjure","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mjul%2Fdocjure/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mjul%2Fdocjure/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mjul%2Fdocjure/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/mjul","download_url":"https://codeload.github.com/mjul/docjure/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254043298,"owners_count":22004917,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["clojure","excel","spreadsheet","xls","xlsx"],"created_at":"2024-08-01T16:01:02.594Z","updated_at":"2025-05-14T00:04:47.892Z","avatar_url":"https://github.com/mjul.png","language":"Clojure","funding_links":[],"categories":["Clojure"],"sub_categories":[],"readme":"# Docjure\n[![Clojars Project](https://img.shields.io/clojars/v/dk.ative/docjure.svg)](https://clojars.org/dk.ative/docjure)\n[![cljdoc documentation](https://cljdoc.org/badge/dk.ative/docjure)](https://cljdoc.org/d/dk.ative/docjure)\n[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT)\n\nDocjure makes reading and writing Office Excel spreadsheet documents\nin Clojure easy.\n\n## Who is this for?\nDocjure is aimed at making the basic use case of reading and writing\nspreadsheets easy.\n\nIf you need advanced charting, pivot tables *etc.*, the easiest way is\nto build template spreadsheets with Excel and populate them with\nDocjure.\n\nIf you want to manipulate advanced features programatically,\nyou are probably better off using the underlying Apache POI\nspreadsheet library directly or looking for another tool.\n\nDocjure has low churn. It is very stable library with a history going\nback to 2009 (open-sourced in 2010).\n\n\n## Usage\n\nThe complete documentation is available on [cljdoc](https://cljdoc.org/d/dk.ative/docjure/).\n\n\n### Example: Read a Price List spreadsheet\n\n```clj\n(use 'dk.ative.docjure.spreadsheet)\n\n;; Load a spreadsheet and read the first two columns from the\n;; price list sheet:\n(-\u003e\u003e (load-workbook \"spreadsheet.xlsx\")\n     (select-sheet \"Price List\")\n     (select-columns {:A :name, :B :price}))\n\n;=\u003e [{:name \"Foo Widget\", :price 100}, {:name \"Bar Widget\", :price 200}]\n```\n\n### Example: Read a single cell\n\nIf you want to read a single cell value, you can use the `select-cell` function which\ntakes an Excel-style cell reference (A2) and returns the cell. In order to get the\nactual value, use `read-cell`\n\n```clj\n(use 'dk.ative.docjure.spreadsheet)\n(read-cell\n (-\u003e\u003e (load-workbook \"spreadsheet.xlsx\")\n      (select-sheet \"Price List\")\n      (select-cell \"A1\")))\n```\n\n### Example: Load a Workbook from a Resource\nThis example loads a workbook from a named file. In the case of running\nin the application server, the file typically resides in the resources directory,\nand it's not on the caller's path. To cover this scenario, we provide\nthe function 'load-workbook-from-resource' that takes a named resource\nas the parameter. After a minor modification, the same example will look like:\n\n```clj\n(-\u003e\u003e (load-workbook-from-resource \"spreadsheet.xlsx\")\n     (select-sheet \"Price List\")\n     (select-columns {:A :name, :B :price}))\n```\n\n### Example: Load a Workbook from a Stream\nThe function 'load-workbook' is a multimethod, and the first example takes\na file name as a parameter. The overloaded version of 'load-workbook'\ntakes an InputStream. This may be useful when uploading a workbook to the server\nover HTTP connection as multipart form data. In this case, the web framework\npasses a byte buffer, and the example should be modified as (note that you have\nto use 'with-open' to ensure that the stream will be closed):\n\n```clj\n\n(with-open [stream (clojure.java.io/input-stream bytes)]\n  (-\u003e\u003e (load-workbook stream)\n       (select-sheet \"Price List\")\n       (select-columns {:A :name, :B :price})))\n```\n\n### Example: Create a spreadsheet\nThis example creates a spreadsheet with a single sheet named \"Price List\".\nIt has three rows. We apply a style of yellow background colour and bold font\nto the top header row, then save the spreadsheet.\n\n```clj\n(use 'dk.ative.docjure.spreadsheet)\n\n;; Create a spreadsheet and save it\n(let [wb (create-workbook \"Price List\"\n                          [[\"Name\" \"Price\"]\n                           [\"Foo Widget\" 100]\n                           [\"Bar Widget\" 200]])\n      sheet (select-sheet \"Price List\" wb)\n      header-row (first (row-seq sheet))]\n  (set-row-style! header-row (create-cell-style! wb {:background :yellow,\n                                                     :font {:bold true}}))\n  (save-workbook! \"spreadsheet.xlsx\" wb))\n```\n\n### Example: Create a workbook with multiple sheets\nThis example creates a spreadsheet with multiple sheets. Simply add more\nsheet-name and data pairs. To create a sheet with no data, pass `nil` as\nthe data argument.\n\n```clj\n(use 'dk.ative.docjure.spreadsheet)\n\n;; Create a spreadsheet and save it\n(let [wb (create-workbook \"Squares\"\n                          [[\"N\" \"N^2\"]\n                           [1 1]\n                           [2 4]\n                           [3 9]]\n                          \"Cubes\"\n                          [[\"N\" \"N^3\"]\n                           [1 1]\n                           [2 8]\n                           [3 27]])]\n   (save-workbook! \"exponents.xlsx\" wb))\n```\n\n### Example: Use Excel Formulas in Clojure\n\nDocjure allows you not only to evaluate a formula cell in a speadsheet, it also\nprovides a way of exposing a formula in a cell as a Clojure function using the\n`cell-fn` function.\n\n    (use 'dk.ative.docjure.spreadsheet)\n    ;; Load a speadsheet and take the first sheet, construct a function from cell A2, taking\n    ;; A1 as input.\n    (def formula-from-a2 (cell-fn \"A2\"\n                                      (first (sheet-seq (load-workbook \"spreadsheet.xlsx\")))\n                                      \"A1\"))\n\n    ;; Returns value of cell A2, as if value in cell A1 were 1.0\n    (formula-from-a2 1.0)\n\n### Example: Handling Error Cells\n\nIf the spreadsheet being read contains cells with errors the default\nbehaviour of the library is to return a keyword representing the\nerror as the cell value.\n\nFor example, given a spreadsheet with errors:\n\n```clj\n(use 'dk.ative.docjure.spreadsheet)\n\n(def sample-cells (-\u003e\u003e (load-workbook \"spreadsheet.xlsx\")\n                       (sheet-seq)\n                       (mapcat cell-seq)))\n\nsample-cells\n\n;=\u003e (#\u003cXSSFCell 15.0\u003e #\u003cXSSFCell NA()\u003e #\u003cXSSFCell 35.0\u003e #\u003cXSSFCell 13/0\u003e #\u003cXSSFCell 33.0\u003e #\u003cXSSFCell 96.0\u003e)\n```\n\nReading error cells, or cells that evaluate to an error (e.g. divide by\nzero) returns a keyword representing the type of error from\n`read-cell`.\n\n```clj\n(-\u003e\u003e sample-cells\n     (map read-cell))\n\n;=\u003e (15.0 :NA 35.0 :DIV0 33.0 96.0)\n```\n\nHow you handle errors will depend on your application. You may want to\nreplace specific errors with a default value and remove others for\nexample:\n\n```clj\n(-\u003e\u003e sample-cells\n     (map read-cell)\n     (replace {:DIV0 0.0})\n     (remove keyword?))\n\n;=\u003e (15.0 35.0 0.0 33.0 96.0)\n```\n\nThe following is a list of all possible [error values](https://poi.apache.org/apidocs/org/apache/poi/ss/usermodel/FormulaError.html#enum_constant_summary):\n\n```clj\n#{:VALUE :DIV0 :CIRCULAR_REF :REF :NUM :NULL :FUNCTION_NOT_IMPLEMENTED :NAME :NA}\n```\n\n### Example: Iterating over spreadsheet data\n\n#### A note on sparse data\n\nIt's worth understanding a bit about the underlying structure of a spreadsheet before you\nstart iterating over the contents.\n\nSpreadsheets are designed to be sparse - not all rows in the spreadsheet must physically exist,\nand not all cells in a row must physically exist.  This is how you can create data at ZZ:65535 without\nusing huge amounts of storage.\n\nThus each cell can be in 3 states - with data, blank, or nonexistent (null).  There's a special type [CellType.BLANK](https://poi.apache.org/apidocs/org/apache/poi/ss/usermodel/CellType.html#BLANK) for blank cells, but missing cells are just returned as nil.\n\nSimilarly rows can exist with cells, or exist but be empty, or they can not exist at all.\n\nPrior to Docjure 1.11 the iteration functions wrapped the underlying Apache POI iterators, which skipped over missing data - this could cause surprising behaviour, especially when there were missing cells inside tabular data.\n\nSince Docjure 1.11 iteration now returns `nil` values for missing rows and cells - this is a *breaking change* - any code that calls `row-seq` or `cell-seq` now needs to deal with possible nil values.\n\n#### Iterating over rows\n\nYou can iterate over all the rows in a worksheet with `row-seq`:\n\n```clj\n(-\u003e\u003e (load-workbook \"test.xls\")\n     (select-sheet \"first\")\n     row-seq)\n```\n\nThis will return a sequence of `org.apache.poi.usermodel.Row` objects, or `nil` for any missing rows.  You can use `(remove nil? (row-seq ...) )` if you are happy to ignore missing rows, but then be aware the nth result in the sequence might not match the nth row in the spreadsheet.\n\n\n#### Iterating over cells\n\nYou can iterate over all the cells in a row with `cell-seq` - this returns a sequence of `org.apache.poi.usermodel.Cell` objects, or `nil` for missing cells.  Note that `(read-cell nil)` returns `nil` so it's safe to apply `read-cell` to the results of `cell-seq`\n\n```clj\n(-\u003e\u003e (load-workbook \"test.xls\")\n     (select-sheet \"first\")\n     row-seq\n     (remove nil?)\n     (map cell-seq)\n     (map #(map read-cell %)))\n```\n\nFor example, if you run the above snippet on a sparse spreadsheet like:\n\n| First Name | Middle Name | Last Name |\n| --- | --- | --- |\n| Edger | Allen | Poe |\n| `(missing row)` |\n| John | `(missing)` | Smith |\n\nThen it will return:\n\n```clj\n((\"First Name\" \"Middle Name\" \"Last Name\")\n (\"Edger\" \"Allen\" \"Poe\")\n (\"John\" nil \"Smith\"))\n```\n\n### Formatting: Adjust Column Width to Contents\n\nYou can adjust the column widths to they fit the contents.\nThis makes generated workbooks look nicer.\n\nFor example, to auto-size all the columns in all the sheets\nin a workbook, use this:\n\n```clj\n;; wb is a workbook\n(dorun (for [sheet (sheet-seq wb)]\n             (auto-size-all-columns! sheet)))\n```\n\nTo apply auto-width to individual columns in a sheet, use the\n`auto-size-column!` function.\n\n\n\n### Automatically get the Docjure jar from Clojars\n\nThe Docjure jar is distributed on\n[Clojars](http://clojars.org/dk.ative/docjure). Here you can find both\nrelease builds and snapshot builds of pre-release versions.\n\n#### Using Leiningen\nIf you are using the Leiningen build tool just add this line to the\n`:dependencies` list in `project.clj` to use it:\n\n```clj\n[dk.ative/docjure \"1.21.0\"]\n```\n\n##### Example project.clj for using Docjure 1.21.0\n\n```clj\n(defproject some.cool/project \"1.0.0-SNAPSHOT\"\n      :description \"Spreadsheet magic using Docjure\"\n      :dependencies [[org.clojure/clojure \"1.12.0\"]\n                     [dk.ative/docjure \"1.21.0\"]])\n```\n\n#### Using `deps.edn` with the Clojure CLI\n\nTo add Docjure to a project using the Clojure CLI tools, add the dependency to your `deps.edn` file:\n\n```clj\n{:deps {dk.ative/docjure {:mvn/version \"1.21.0\"}}}\n```\n\n## Installation for Contributors\nYou need to install the Leiningen build tool to build the library.\nYou can get it here: [Leiningen](http://github.com/technomancy/leiningen)\n\nThe library uses the Apache POI library which will be downloaded by\nthe \"lein deps\" command.\n\nThen build the library:\n\n     lein deps\n     lein compile\n     lein test\n\nTo run the tests on all supported Clojure versions use:\n\n    lein all test\n\nTo check for security issues use:\n\n    lein nvd check\n\nTo check for new versions of dependencies:\n\n    lein ancient\n\nTo run the static analysis of the code:\n\n    lein clj-kondo\n\n\n### Releasing to Clojars\nWhen releasing a version to Clojars you must provide your user-name. The password is a deployment token, not your normal password. You can generate this by logging into Clojars. These tokens have an expiration date so if it does not work, log in a check if you need a new token.\n\n\n```\n    lein deploy clojars\n```\n\nYou also need a GPG key to sign the releases.\n\nThese also expire, and must be periodically renewed. You can check your keys and their status like this:\n\n\n```\n    gpg --list-keys\n```\n\nSee also `lein help deploy` and `lein help gpg`.\n\nRemember to tag releases in git. You can list the tags with `git tag -n` and tag with `git tag -a TAGNAME -m 'Tag comment'`.\n\n\n## Build Status\n[![Build Status](https://travis-ci.org/mjul/docjure.svg?branch=master)](https://travis-ci.org/mjul/docjure)\n\n## License\n\nCopyright (c) 2009-2025 Martin Jul\n\nDocjure is licensed under the MIT License. See the LICENSE file for\nthe license terms.\n\nDocjure uses the Apache POI library, which is licensed under the\n[Apache License v2.0](http://www.apache.org/licenses/LICENSE-2.0).\n\nFor more information on Apache POI refer to the\n[Apache POI web site](http://poi.apache.org/).\n\n\n## Contact information\n\n* [Docjure on GitHub](https://github.com/mjul/docjure)\n\nMartin Jul\n\n* Email: martin@.....com\n* Twitter: mjul\n* GitHub: [mjul](https://github.com/mjul)\n\n\n## Contributors\nThis library includes great contributions from\n\n* [Carl Baatz](https://github.com/cbaatz) (cbaatz)\n* [Michael van Acken](https://github.com/mva) (mva)\n* [Ragnar Dahlén](https://github.com/ragnard) (ragnard)\n* [Vijay Kiran](https://github.com/vijaykiran) (vijaykiran)\n* [Jon Neale](https://github.com/jonneale) (jonneale)\n* [\"Naipmoro\"](https://github.com/naipmoro) (naipmoro)\n* [Nikolay Durygin](https://github.com/nidu) (nidu)\n* [Oliver Holworthy](https://github.com/oholworthy) (oholworthy)\n* [\"rakhra\"](https://github.com/rakhra) (rakhra)\n* [Igor Tovstopyat-Nelip](https://github.com/igortn) (igortn)\n* [Dino Kovač](https://github.com/reisub) (reisub)\n* [Lars Trieloff](https://github.com/trieloff) (trieloff)\n* [Jens Bendisposto](https://github.com/bendisposto) (bendisposto)\n* [Stuart Hinson](https://github.com/stuarth) (stuarth)\n* [Dan Petranek](https://github.com/dpetranek) (dpetranek)\n* [Aleksander Madland Stapnes](https://github.com/madstap) (madstap)\n* [Korny Sietsma](https://github.com/kornysietsma) (kornysietsma)\n* [Antti Virtanen](https://github.com/lokori) (lokori)\n* [alephyud](https://github.com/alephyud) (alephyud)\n* [Markku Rontu](https://github.com/Macroz) (macroz)\n* [Harold](https://github.com/harold) (harold)\n* [Alex Scott](https://github.com/axrs) (axrs)\n* [Maurício Szabo](https://github.com/mauricioszabo) (mauricioszabo)\n* [Mic Sokoli](https://github.com/MicSokoli) (MicSokoli)\n* [Ekaitz Zárraga](https://github.com/ekaitz-zarraga) (ekaitz-zarraga)\n* [Ross Gibb](https://github.com/rossgibb) (rossgibb)\n* [Manuel Herzog](https://github.com/manuelherzog) (manuelherzog)\n* [Francisco Vides Fernández](https://github.com/fvides) (fvides)\n* [WonJun Lee](https://github.com/Lee-WonJun) (Lee-WonJun)\n* [Jari Hanhela](https://github.com/Jarzka) (Jarzka)\n* [Maxim Penzin](https://github.com/maxp) (maxp)\n* [Christopher Miles](https://github.com/cmiles74) (cmiles74)\n* [Danny Freeman](https://github.com/dannyfreeman) (dannyfreeman)\n* [Orestis Markou](https://github.com/orestis) (orestis)\n\nThank you very much!\n\n## Honorary Mention\n\nA special thank you also goes out to people that did not contribute code\nbut shared their ideas, reported security issues or bugs and otherwise\ninspired the continuing work on the project.\n\n\n* [vemv](https://github.com/vemv) (vemv)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmjul%2Fdocjure","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmjul%2Fdocjure","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmjul%2Fdocjure/lists"}