{"id":13508954,"url":"https://github.com/coletiv/puppeteer-pdf","last_synced_at":"2025-04-07T07:06:09.256Z","repository":{"id":53208963,"uuid":"130837245","full_name":"coletiv/puppeteer-pdf","owner":"coletiv","description":"PDF generation wrapper for Elixir using Puppeteer","archived":false,"fork":false,"pushed_at":"2019-08-20T17:51:16.000Z","size":50,"stargazers_count":99,"open_issues_count":5,"forks_count":18,"subscribers_count":4,"default_branch":"master","last_synced_at":"2025-03-31T06:04:30.588Z","etag":null,"topics":["elixir","pdf"],"latest_commit_sha":null,"homepage":null,"language":"Elixir","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/coletiv.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}},"created_at":"2018-04-24T10:30:34.000Z","updated_at":"2025-03-18T05:18:04.000Z","dependencies_parsed_at":"2022-09-15T03:50:56.098Z","dependency_job_id":null,"html_url":"https://github.com/coletiv/puppeteer-pdf","commit_stats":null,"previous_names":[],"tags_count":4,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/coletiv%2Fpuppeteer-pdf","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/coletiv%2Fpuppeteer-pdf/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/coletiv%2Fpuppeteer-pdf/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/coletiv%2Fpuppeteer-pdf/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/coletiv","download_url":"https://codeload.github.com/coletiv/puppeteer-pdf/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247608151,"owners_count":20965952,"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":["elixir","pdf"],"created_at":"2024-08-01T02:01:00.951Z","updated_at":"2025-04-07T07:06:09.231Z","avatar_url":"https://github.com/coletiv.png","language":"Elixir","funding_links":[],"categories":["PDF"],"sub_categories":[],"readme":"# PuppeteerPdf\n\nThis is a wrapper to NodeJS module [puppeteer-pdf](https://www.npmjs.com/package/puppeteer-pdf). After some attempts to use wkhtmltopdf using [pdf_generator](https://github.com/gutschilla/elixir-pdf-generator), I've decided to use other software to generate PDF and create a wrapper for it.\n\n## Puppeteer PDF vs wkhtmltopdf\n\nI've written a [small blog post](https://coletiv.com/blog/elixir-pdf-generation-puppeteer-wkhtmltopdf/) where I explain my reasons to create this extension. Here is the list of pros and cons compared with `pdf_generator` module.\n\n### Disadvantage\n\n* Bigger PDF file size\n* NodeJS 8+ needed\n* Chromium Browser needed\n\n### Advantages\n\n* Display independent render (for better testing how template will be).\n* Less render issues.\n\n## Installation\n\nInstall `puppeteer-pdf` via npm, with the following command:\n\n```\nnpm i puppeteer-pdf -g\n```\n\nIn some cases you will need to install this extra dependencies. Here is an example for Debian based distributions.\n\n```\nsudo apt-get install libxss1 lsof libasound2 libnss3\n```\n\nOn your elixir project, you just need to add the following dependency:\n\n```elixir\ndef deps do\n  [\n    {:puppeteer_pdf, \"~\u003e 1.0.3\"}\n  ]\nend\n```\n\nIf you have the older `applications` structure inside `mix.exs`, you need to add `:briefly` to it. If you have `extra_applications`, you don't need to do anything.\n\n### Troubleshooting\n\nIf for some reason it doesn't download automatically chromium, it will give you the following error:\n\n```\n(node:14878) UnhandledPromiseRejectionWarning: Error: Chromium revision is not downloaded. Run \"npm install\" or \"yarn install\"\nat Launcher.launch (/usr/local/lib/node_modules/puppeteer-pdf/node_modules/puppeteer/lib/Launcher.js:119:15)\nat \u003canonymous\u003e\n(node:14878) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). (rejection id: 1)\n(node:14878) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.\n```\n\nTo solve this, you execute the following commands to copy chromium from the `puppeteer` folder, to the `puppeteer` inside the `puppeteer-pdf`.\n\nOn OSX and Linux systems should be the following commands:\n\n```\nnpm i puppeteer -g # This should install chromium\ncp -R /usr/local/lib/node_modules/puppeteer/.local-chromium/ /usr/local/lib/node_modules/puppeteer-pdf/node_modules/puppeteer/\n```\n\nIf you have issues related to this, please comment on [this issue](https://github.com/coletiv/puppeteer-pdf/issues/13).\n\n\n## How to use\n\n### Initial\nThese are the options available right now:\n\n```elixir\noptions = [\n  margin_left: 40,\n  margin_right: 40,\n  margin_top: 40,\n  margin_bottom: 150,\n  format: \"A4\",\n  print_background: true,\n  header_template: header_html, # Support both file and html\n  footer_template: footer_html,\n  display_header_footer: true,\n  debug: true,\n  timeout: 10000 # value passed directly to Task.await/2. (Defaults to 5000)\n]\n```\n\nAnd to generate the PDF you can use the following code using Phoenix Template:\n\n```elixir\n# Get template to be rendered. Note that the full filename is \"invoice.html.eex\", the but \".eex\" is not needed here.\nhtml = Phoenix.View.render_to_string(\n  MyApp.View,\n  \"pdf/invoice.html\",\n  assigns\n)\n\n# Get full path to generated pdf file\npdf_path = Path.absname(\"invoice.pdf\")\n\ncase PuppeteerPdf.Generate.from_string(html, pdf_path, options) do\n  {:ok, _} -\u003e ...\n  {:error, message} -\u003e ...\nend\n```\n\nOr just with HTML file:\n\n```elixir\nhtml_path = Path.absname(\"random.html\")\ncase PuppeteerPdf.Generate.from_file(html_path, pdf_path, options) do\n  {:ok, _} -\u003e ...\n  {:error, message} -\u003e ...\nend\n```\n\n### Using header and footer templates\n\nYou can defined an HTML header and footer, using the `header_template` and `footer_template` options.\nTo use a file, use the following format: `file:///home/user/file.html`.\n\nDon't forget to also include `display_header_footer` to `true`.\n\n### Support special characters\n\nIf you see weird characters printed on a language that can have special (like Germam, Chinese, Russian, ...) define the charset as follows:\n\n```html\n\u003chead\u003e\n  \u003cmeta charset=\"UTF-8\"\u003e\n  ...\n```\n\n### Use images or fonts\n\nYou can use custom images or text fonts using the following Elixir code that defines the full path to the file. This should be passed in the `assings` variable when rendering the template, as explained above.\n\n```elixir\nfont1_path = \"#{:code.priv_dir(:myapp)}/static/fonts/font1.otf\"\n```\n\nOn template style:\n\n```css\n  @font-face {\n    font-family: 'GT-Haptik';\n    src: url(\u003c%= @font1_path %\u003e) format(\"opentype\");\n    font-weight: 100;\n  }\n```\n\n### Configure execution path\n\nIn order to configure this setting:\n\n```elixir\nconfig :puppeteer_pdf, exec_path: \"/usr/local/bin/puppeteer-pdf\"\n```\n\nOr you can use system environment variable:\n\n```\nexport PUPPETEER_PDF_PATH=/usr/local/bin/puppeteer-pdf\n```\n\nFor development purposes when working on this project, you can set the `PUPPETEER_PDF_PATH`\nenvironment variable to point to the `puppeteer-pdf` executable. **Do not attempt to use this env\nvar to set the path in production. Instead, use the application configuration, above.**\n\n## Continuous Integration / Continuous Deployment\n\nIf you use CI\n\n```\nbefore_script:\n- nvm install 8\n- npm i puppeteer-pdf -g\n```\n\n### Docker File\n\nIf you are deploying a project with Docker and using this module, this is a working Dockerfile configuration.\n\nYou can find instructions on how to deploy this with an `alpine` Docker image in [this issue](https://github.com/coletiv/puppeteer-pdf/issues/24).\n\nThis Docker file use a two stage building, with a Debian operative system.\n\n```\n#\n# Stage 1\n#\n\nFROM elixir:1.8.2-slim as builder\nENV MIX_ENV=prod\nWORKDIR /myapp\n\n# Umbrella\nCOPY mix.exs mix.lock ./\nCOPY config config\n\nRUN mix local.hex --force \u0026\u0026 \\\n    mix local.rebar --force\n\n# App\nCOPY lib lib\n# Image / Font files if you need for your PDF document\nCOPY priv priv\nRUN mix do deps.get, deps.compile\n\nWORKDIR /myapp\nCOPY rel rel\n\nRUN mix release --env=prod --verbose\n\n#\n# Stage 2\n#\n\nFROM node:10-slim\n\n# Install latest chrome dev package and fonts to support major charsets (Chinese, Japanese, Arabic, Hebrew, Thai and a few others)\n# Note: this installs the necessary libs to make the bundled version of Chromium that Puppeteer\n# installs, work.\nRUN wget -q -O - https://dl-ssl.google.com/linux/linux_signing_key.pub | apt-key add - \\\n    \u0026\u0026 sh -c 'echo \"deb [arch=amd64] http://dl.google.com/linux/chrome/deb/ stable main\" \u003e\u003e /etc/apt/sources.list.d/google.list' \\\n    \u0026\u0026 apt-get update \\\n    \u0026\u0026 apt-get install -y google-chrome-unstable fonts-ipafont-gothic fonts-wqy-zenhei fonts-thai-tlwg fonts-kacst ttf-freefont \\\n      --no-install-recommends \\\n    \u0026\u0026 rm -rf /var/lib/apt/lists/*\n\n# If running Docker \u003e= 1.13.0 use docker run's --init arg to reap zombie processes, otherwise\n# uncomment the following lines to have `dumb-init` as PID 1\n# ADD https://github.com/Yelp/dumb-init/releases/download/v1.2.0/dumb-init_1.2.0_amd64 /usr/local/bin/dumb-init\n# RUN chmod +x /usr/local/bin/dumb-init\n# ENTRYPOINT [\"dumb-init\", \"--\"]\n\n# Uncomment to skip the chromium download when installing puppeteer. If you do,\n# you'll need to launch puppeteer with:\n#     browser.launch({executablePath: 'google-chrome-unstable'})\n# ENV PUPPETEER_SKIP_CHROMIUM_DOWNLOAD true\n\nENV MIX_ENV=prod \\\n    SHELL=/bin/bash\n\n# Install puppeteer so it's available in the container.\nRUN npm i puppeteer-pdf \\\n    # Add user so we don't need --no-sandbox.\n    # same layer as npm install to keep re-chowned files from using up several hundred MBs more space\n    \u0026\u0026 groupadd -r pptruser \u0026\u0026 useradd -r -g pptruser -G audio,video pptruser \\\n    \u0026\u0026 mkdir -p /home/pptruser/Downloads \\\n    \u0026\u0026 chown -R pptruser:pptruser /home/pptruser \\\n    \u0026\u0026 chown -R pptruser:pptruser /node_modules \\\n    \u0026\u0026 mkdir /myapp \\\n    \u0026\u0026 chown -R pptruser:pptruser /myapp\n\n# Run everything after as non-privileged user.\nUSER pptruser\n\nWORKDIR /myapp\nCOPY --from=builder /myapp/_build/prod/rel/myapp/releases/0.1.0/myapp.tar.gz .\n\nRUN tar zxf myapp.tar.gz \u0026\u0026 rm myapp.tar.gz\nCMD [\"/myapp/bin/myapp\", \"foreground\"]\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcoletiv%2Fpuppeteer-pdf","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcoletiv%2Fpuppeteer-pdf","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcoletiv%2Fpuppeteer-pdf/lists"}