{"id":13994301,"url":"https://github.com/htdebeer/paru","last_synced_at":"2025-10-04T10:53:24.844Z","repository":{"id":28149118,"uuid":"31649367","full_name":"htdebeer/paru","owner":"htdebeer","description":"Control pandoc with Ruby and write pandoc filters in Ruby","archived":false,"fork":false,"pushed_at":"2025-09-07T09:29:20.000Z","size":5943,"stargazers_count":39,"open_issues_count":1,"forks_count":13,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-09-07T23:33:59.647Z","etag":null,"topics":["pandoc","pandoc-filter","ruby"],"latest_commit_sha":null,"homepage":"https://heerdebeer.org/Software/markdown/paru/","language":"Ruby","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":"crazyxuehu/Knowledge-Exploration","license":"gpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/htdebeer.png","metadata":{"files":{"readme":"README.md","changelog":null,"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,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2015-03-04T09:42:09.000Z","updated_at":"2025-09-07T09:24:39.000Z","dependencies_parsed_at":"2025-07-22T19:32:14.212Z","dependency_job_id":"a3d1a13d-2beb-435f-b908-28b3312dbebe","html_url":"https://github.com/htdebeer/paru","commit_stats":{"total_commits":272,"total_committers":8,"mean_commits":34.0,"dds":"0.11029411764705888","last_synced_commit":"f944df1070360740b404609509534d4555784473"},"previous_names":[],"tags_count":31,"template":false,"template_full_name":null,"purl":"pkg:github/htdebeer/paru","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/htdebeer%2Fparu","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/htdebeer%2Fparu/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/htdebeer%2Fparu/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/htdebeer%2Fparu/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/htdebeer","download_url":"https://codeload.github.com/htdebeer/paru/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/htdebeer%2Fparu/sbom","scorecard":{"id":471083,"data":{"date":"2025-08-11","repo":{"name":"github.com/htdebeer/paru","commit":"a92fdca73ab3c769cf19b78544aff024700375ae"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":3.3,"checks":[{"name":"Packaging","score":-1,"reason":"packaging workflow not detected","details":["Warn: no GitHub/GitLab publishing workflow detected."],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#packaging"}},{"name":"Maintained","score":2,"reason":"2 commit(s) and 1 issue activity found in the last 90 days -- score normalized to 2","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"name":"Token-Permissions","score":0,"reason":"detected GitHub workflow tokens with excessive permissions","details":["Warn: no topLevel permission defined: .github/workflows/run_build.yml:1","Info: no jobLevel write permissions found"],"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#token-permissions"}},{"name":"Code-Review","score":0,"reason":"Found 2/28 approved changesets -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#code-review"}},{"name":"Dangerous-Workflow","score":10,"reason":"no dangerous workflow patterns detected","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#dangerous-workflow"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#cii-best-practices"}},{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#binary-artifacts"}},{"name":"Vulnerabilities","score":10,"reason":"0 existing vulnerabilities detected","details":null,"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}},{"name":"Security-Policy","score":0,"reason":"security policy file not detected","details":["Warn: no security policy file detected","Warn: no security file to analyze","Warn: no security file to analyze","Warn: no security file to analyze"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#security-policy"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#fuzzing"}},{"name":"Pinned-Dependencies","score":0,"reason":"dependency not pinned by hash detected -- score normalized to 0","details":["Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/run_build.yml:20: update your workflow using https://app.stepsecurity.io/secureworkflow/htdebeer/paru/run_build.yml/master?enable=pin","Warn: third-party GitHubAction not pinned by hash: .github/workflows/run_build.yml:23: update your workflow using https://app.stepsecurity.io/secureworkflow/htdebeer/paru/run_build.yml/master?enable=pin","Warn: third-party GitHubAction not pinned by hash: .github/workflows/run_build.yml:29: update your workflow using https://app.stepsecurity.io/secureworkflow/htdebeer/paru/run_build.yml/master?enable=pin","Warn: containerImage not pinned by hash: Dockerfile:1: pin your Docker image by updating ruby:3.3 to ruby:3.3@sha256:ee90a2c2273af8f8205b891bb2acfa45506a17b8031ba918c2d6f58213713cc2","Info:   0 out of   1 GitHub-owned GitHubAction dependencies pinned","Info:   0 out of   2 third-party GitHubAction dependencies pinned","Info:   0 out of   1 containerImage dependencies pinned"],"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#pinned-dependencies"}},{"name":"License","score":10,"reason":"license file detected","details":["Info: project has a license file: LICENSE:0","Info: FSF or OSI recognized license: GNU General Public License v3.0: LICENSE:0"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"name":"Branch-Protection","score":0,"reason":"branch protection not enabled on development/release branches","details":["Warn: branch protection not enabled for branch 'master'"],"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#branch-protection"}},{"name":"Signed-Releases","score":0,"reason":"Project has not signed or included provenance with any releases.","details":["Warn: release artifact 1.4.2 not signed: https://api.github.com/repos/htdebeer/paru/releases/222348863","Warn: release artifact 1.4.1 not signed: https://api.github.com/repos/htdebeer/paru/releases/178749984","Warn: release artifact 1.4.0 not signed: https://api.github.com/repos/htdebeer/paru/releases/174700336","Warn: release artifact 1.3 not signed: https://api.github.com/repos/htdebeer/paru/releases/167577053","Warn: release artifact 1.2.1 not signed: https://api.github.com/repos/htdebeer/paru/releases/163638260","Warn: release artifact 1.4.2 does not have provenance: https://api.github.com/repos/htdebeer/paru/releases/222348863","Warn: release artifact 1.4.1 does not have provenance: https://api.github.com/repos/htdebeer/paru/releases/178749984","Warn: release artifact 1.4.0 does not have provenance: https://api.github.com/repos/htdebeer/paru/releases/174700336","Warn: release artifact 1.3 does not have provenance: https://api.github.com/repos/htdebeer/paru/releases/167577053","Warn: release artifact 1.2.1 does not have provenance: https://api.github.com/repos/htdebeer/paru/releases/163638260"],"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#signed-releases"}},{"name":"SAST","score":0,"reason":"SAST tool is not run on all commits -- score normalized to 0","details":["Warn: 0 commits out of 4 are checked with a SAST tool"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}}]},"last_synced_at":"2025-08-19T13:54:16.164Z","repository_id":28149118,"created_at":"2025-08-19T13:54:16.164Z","updated_at":"2025-08-19T13:54:16.164Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":278302555,"owners_count":25964520,"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","status":"online","status_checked_at":"2025-10-04T02:00:05.491Z","response_time":63,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"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":["pandoc","pandoc-filter","ruby"],"created_at":"2024-08-09T14:02:48.729Z","updated_at":"2025-10-04T10:53:24.839Z","avatar_url":"https://github.com/htdebeer.png","language":"Ruby","funding_links":[],"categories":["Ruby"],"sub_categories":[],"readme":"# Paru—Pandoc wrapped around in Ruby\n\n## Contents\n\n- [Introduction](#introduction)\n  - [Licence](#licence)\n  - [Acknowledgements](#acknowledgements)\n  - [Installation](#installation)\n- [Paru says hello to pandoc](#paru-says-hello-to-pandoc)\n- [Writing and using pandoc filters with\n  paru](#writing-and-using-pandoc-filters-with-paru)\n- [Documentation](#documentation)\n  - [Manual](#manual)\n  - [API documentation](#api-documentation)\n  - [Frequently asked questions](#frequently-asked-questions)\n\n[![Gem\nVersion](https://badge.fury.io/rb/paru.svg)](https://badge.fury.io/rb/paru)\n\n## Introduction\n\nParu is a simple Ruby wrapper around [pandoc](https://www.pandoc.org),\nthe great multi-format document converter. Paru supports automating\npandoc by writing Ruby programs and using pandoc in your Ruby programs\n(see [Chapter 2 in the\nmanual](https://heerdebeer.org/Software/markdown/paru/#automating-the-use-of-pandoc-with-paru)).\nParu also supports writing pandoc filters in Ruby (see [Chapter 3 in the\nmanual](https://heerdebeer.org/Software/markdown/paru/#writing-and-using-pandoc-filters-with-paru)).\nIn [paru’s manual](https://heerdebeer.org/Software/markdown/paru/) the\nuse of paru is explained in detail, from explaining how to install and\nuse paru, creating and using filters, to putting it all together in a\nreal-world use case: generating the manual!\n\nSee also the [paru API\ndocumentation](https://heerdebeer.org/Software/markdown/paru/documentation/api-doc/).\n\n**Note** If you’re using pandoc 3, use paru version 1.1.x or higher;\nparu 1.0.x doesn’t work with pandoc 3. If you’re still using pandoc\nversion 2, use paru version 1.0.x instead.\n\nThis README is a brief overview of paru’s features and usages.\n\n### Licence\n\nParu is [free sofware](https://www.gnu.org/philosophy/free-sw.en.html);\nparu is released under the\n[GPLv3](https://www.gnu.org/licenses/gpl-3.0.en.html). You find paru’s\nsource code on [github](https://github.com/htdebeer/paru).\n\n### Acknowledgements\n\nI would like to thank the following users for their contributions of\npatches, bug reports, fixes, and suggestions. With your help paru is\ngrowing beyond a simple tool for personal use into a useful addition to\nthe pandoc ecosystem.\n\n- [Ian](https://github.com/iandol)\n- [Jonathan Raphaelson](https://github.com/lygaret)\n- [Michael Kussmaul](https://github.com/kusmi)\n- [Xavier Belanche Alonso](https://github.com/xbelanch)\n- [Robert Riemann](https://github.com/rriemann)\n- [Ulysses Zhan](https://github.com/UlyssesZh)\n\n### Installation\n\nParu is installed through rubygems as follows:\n\n``` bash\ngem install paru\n```\n\nYou can also build and install the latest version gem yourself by\nrunning the following commands:\n\n``` bash\ncd /path/to/paru/repository\nbundle install\nrake build\ngem install pkg/paru-1.5.1.gem\n```\n\nParu, obviously, requires pandoc. See\n\u003chttps://pandoc.org/installing.html\u003e about how to install pandoc on your\nsystem and [pandoc’s manual](https://pandoc.org/README.html) on how to\nuse pandoc.\n\nYou can generate the [API documentation for\nparu](https://heerdebeer.org/Software/markdown/paru/documentation/api-doc/)\nby cloning the repository and running `rake yard`. It’ll put it in\n`documentation/api-doc`.\n\n## Paru says hello to pandoc\n\nUsing paru is straightforward. It is a thin “rubyesque” layer around the\npandoc executable. After requiring paru in your ruby program, you create\na new paru pandoc converter as follows:\n\n``` ruby\nrequire \"paru/pandoc\"\n\nconverter = Paru::Pandoc.new\n```\n\nThe various [command-line options of\npandoc](https://pandoc.org/README.html#options) map to methods on this\nnewly created instance. When you want to use a pandoc command-line\noption that contains dashes, replace all dashes with an underscore to\nget the corresponding paru method. For example, the pandoc command-line\noption `--pdf-engine` becomes the paru method `pdf_engine`. Knowing this\nconvention, you can convert from markdown to pdf using the lualatex\nengine by calling the `from`, `to`, and `pdf_engine` methods to\nconfigure the converter. There is a convenience `configure` method that\ntakes a block to configure multiple options at once:\n\n``` ruby\nrequire \"paru/pandoc\"\n\nconverter = Paru::Pandoc.new\nconverter.configure do\n    from \"markdown\"\n    to \"latex\"\n    pdf_engine \"lualatex\"\n    output \"my_first_pdf_file.pdf\"\nend\n```\n\nAs creating and immediately configuring a converter is a common pattern,\nthe constructor takes a configuration block as well. Finally, when you\nhave configured the converter, you can use it to convert a string with\nthe `convert` method, which is aliased by The `\u003c\u003c` operator. You can\ncall `convert` multiple times and re-configure the converter in between.\n\nThis introductory section is ended by the obligatory “hello world”\nprogram, paru-style:\n\n``` ruby\n#!/usr/bin/env ruby\nrequire 'paru/pandoc'\n\ninput = 'Hello world, from **pandoc**'\n\noutput = Paru::Pandoc.new do\n  from 'markdown'\n  to 'html'\nend \u003c\u003c input\n\nputs output\n```\n\nRunning the above program results in the following output:\n\n``` html\n\u003cp\u003eHello world, from \u003cstrong\u003epandoc\u003c/strong\u003e\u003c/p\u003e\n```\n\nTo support converting files that cannot easily be represented by a\nsingle string, such as EPUB or docx, paru also has the `convert_file`\nmethod. It takes a path as argument, and when executed, it tells pandoc\nto convert that path using the current configured pandoc configuration.\n\nIn the next chapter, the development of *do-pandoc.rb* is presented as\nan example of real-world usage of paru.\n\n## Writing and using pandoc filters with paru\n\nOne of pandoc’s interesting capabilities are [custom\nfilters](https://pandoc.org/scripting.html). This is an extremely\npowerful feature that allows you to automate certain tasks, such as\nnumbering figures, using other command-line programs to pre or post\nprocess parts of the input, or change the structure of the input\ndocument before having pandoc writing it out. Paru allows you to write\npandoc filters in Ruby.\n\nFor a collection of paru filters, have a look at the\n[paru-filter-collection](https://github.com/htdebeer/paru-filter-collection).\n\nThe simplest paru pandoc filter is the *identity* filter that does do\nnothing:\n\n``` ruby\n#!/usr/bin/env ruby\n# Identity filter\nrequire 'paru/filter'\n\nParu::Filter.run do\n  # nothing\nend\n```\n\nNevertheless, it shows the structure of every paru pandoc filter: A\nfilter is an executable script (line 1), it uses the `paru/filter`\nmodule, and it executes a `Paru::Filter` object. Running the identity\nfilter is a good way to start writing your own filters. In the next\nsections several simple but useful filters are developed to showcase the\nuse of paru to write pandoc filters in Ruby.\n\nA more useful filter is to numbering figures. In some output formats,\nsuch as PDF, HTML + CSS, or ODT, figures can be automatically numbered.\nIn other formats, notably markdown itself, numbering has to be done\nmanually. However, it is very easy to create a filter that does this\nnumbering of figures automatically as well:\n\n``` ruby\n#!/usr/bin/env ruby\n# Number all figures in a document and prefix the caption with \"Figure\".\nrequire 'paru/filter'\n\nfigure_counter = 0\n\nParu::Filter.run do\n  with 'Image' do |image|\n    figure_counter += 1\n    image.inner_markdown = \"Figure #{figure_counter}. #{image.inner_markdown}\"\n  end\nend\n```\n\nThe filter `number_figures.rb` keeps track of the last figure’s sequence\nnumber in `counter`. Each time an\n[Image](https://heerdebeer.org/Software/markdown/paru/documentation/api-doc/Paru/PandocFilter/Image.html)\nis encountered while processing the input file, that counter is\nincremented and the image’s caption is prefixed with “Figure\n\\#{counter}.” by overwriting the image’s node’s inner markdown.\n\nFor more information about writing filters, please see [paru’s\nmanual](https://heerdebeer.org/Software/markdown/paru/) or the API\ndocumentation for the\n[Filter](https://heerdebeer.org/Software/markdown/paru/documentation/api-doc/Paru/Filter.html)\nclass. Furthermore, example filters can also be found in the [filters\nsub directory](examples/filters) of paru’s [examples](examples/). Feel\nfree to copy and adapt them to your needs.\n\n## Documentation\n\n### Manual\n\nFor more information on automatic the use of pandoc with paru or writing\npandoc filters in ruby, please see paru’s\n[manual](https://heerdebeer.org/Software/markdown/paru/).\n\n### API documentation\n\nThe [API\ndocumentation](https://heerdebeer.org/Software/markdown/paru/documentation/api-doc/)\ncovers the whole of paru. Where the manual just describes a couple of\nscenarios, the API documentation shows all available functionality. It\nalso give more examples of using paru and writing filters.\n\n### Frequently asked questions\n\nFeel free to ask me a question: [send me an\nemail](mailto:Huub@heerdebeer.org) or submit a new\n[issue](https://github.com/htdebeer/paru/issues) if you’ve found a bug!\n\n- *I get an error like “Erro: JSON parse error: Error in \\$:\n  Incompatible API versions: encoded with \\[1,20\\] but attempted to\n  decode with \\[1,21\\].”*\n\n  The versions of pandoc and paru you are using are incompatible. Please\n  install the latest versions of pandoc and paru.\n\n  Why does this happen? Internally pandoc uses\n  [pandoc-types](https://hackage.haskell.org/package/pandoc-types) to\n  represent documents its converts and filters. Documents represented by\n  one version of pandoc-types are slightly incompatible with documents\n  represented by another version of pandoc-types. This also means that\n  filters written in paru for one version of pandoc-types are not\n  guaranteed to work on documents represented by another version of\n  pandoc-types. As a result, not all paru versions work together with\n  all pandoc versions.\n\n  As a general rule: Use the latest versions of pandoc and paru.\n\n- *I get an error like “‘values_at’: no implicit conversion of String\n  into Integer (TypeError) from lib/paru/filter/document.rb:54:in\n  ‘from_JSON’”*\n\n  The most likely cause is that you’re using an old version of Pandoc.\n  Paru version 0.2.x only supports pandoc version 1.18 and up. In pandoc\n  version 1.18 there was a breaking API change in the way filters\n  worked. Please upgrade your pandoc installation.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhtdebeer%2Fparu","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fhtdebeer%2Fparu","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhtdebeer%2Fparu/lists"}