# How do I convert markdown files to pdf files

**URL:** <https://help.nextcloud.com/t/how-do-i-convert-markdown-files-to-pdf-files/158324>\
**Category:** ℹ️ Support\
**Tags:** flow-app\
**Created:** [March 22, 2023, 4:37pm UTC](https://help.nextcloud.com/t/how-do-i-convert-markdown-files-to-pdf-files/158324 "2023-03-22T16:37:22Z")\
**Posts on this page:** 11\
**Page:** 1

<div class="post-metadata">

**Author:** ![Wolfgang\_Moser](https://help.nextcloud.com/user_avatar/help.nextcloud.com/wolfgang_moser/32/49963_2.png) [@Wolfgang\_Moser](https://help.nextcloud.com/u/Wolfgang_Moser)\
**Post date:** [March 22, 2023, 4:37pm UTC](https://help.nextcloud.com/t/how-do-i-convert-markdown-files-to-pdf-files/158324/1 "2023-03-22T16:37:22Z")

</div>

I would like to automatically convert my markdown files to pdf files and use them to e.g. view the content offline. I configure flow and libre office for that and the conversion is working but the pdf content does not have the nice formatting and the images are missing in the pdf.

Is there any way to automatically create pdf files from markdown files with the same formatting and without dropping the images? I am aware that I could use the browser print-to-pdf feature as a workaround but that is not a viable solution for my use case.

Thanks, Wolfgang

---

<div class="post-metadata">

**Author:** ![Daphne](https://help.nextcloud.com/user_avatar/help.nextcloud.com/daphne/32/47373_2.png) [@Daphne](https://help.nextcloud.com/u/Daphne)\
**Post date:** [March 22, 2023, 5:13pm UTC](https://help.nextcloud.com/t/how-do-i-convert-markdown-files-to-pdf-files/158324/2 "2023-03-22T17:13:58Z")

</div>

it’s a bit hacky, but what I did recently is to add them to the collectives app and use the print feature in collectives to print to pdf.

Text is also supposed to have a ‘print’ feature but last time I tried it did not work. but perhaps that bug is solved by now

---

<div class="post-metadata">

**Author:** ![Wolfgang\_Moser](https://help.nextcloud.com/user_avatar/help.nextcloud.com/wolfgang_moser/32/49963_2.png) [@Wolfgang\_Moser](https://help.nextcloud.com/u/Wolfgang_Moser)\
**Post date:** [March 22, 2023, 5:51pm UTC](https://help.nextcloud.com/t/how-do-i-convert-markdown-files-to-pdf-files/158324/3 "2023-03-22T17:51:37Z")

</div>

Hi Daphne, thanks for the hint. Took me a while to discover the print feature in the collectives app though. Print to pdf with nice formatting is working but the image is still missing - only a loading circle is shown in the pdf instead of the actual image.  
 ![Screenshot 2023-03-22 185026](https://help.nextcloud.com/uploads/default/original/3X/2/3/23a2e9c0700a7f8097585afe5bedd74ad9d7715d.png)

---

<div class="post-metadata">

**Author:** ![ernolf](https://help.nextcloud.com/user_avatar/help.nextcloud.com/ernolf/32/77874_2.png) [@ernolf](https://help.nextcloud.com/u/ernolf)\
**Post date:** [March 23, 2023, 3:17am UTC](https://help.nextcloud.com/t/how-do-i-convert-markdown-files-to-pdf-files/158324/4 "2023-03-23T03:17:43Z")

</div>

> [@Wolfgang\_Moser](#):
>
> Is there any way to automatically create pdf files from markdown files with the same formatting and without dropping the images?

I liked the question and it motivated me to find a solution.

I have tried everything and this gives the best result with the least effort:

You need the “File Actions app” `files_scripts` (build the lua php-module as described [→ **here** ←](https://help.nextcloud.com/t/nextcloud-28-with-php8-3-on-debian-ubuntu-missing-modules-solution/174766#lua-module-for-file-actions-app-5))

Then you need `pandoc` and `soffice` (Libre Office) on the host system.

Create a “New action” in the admin section of “File actions”, give it a name like “markdown 2 pdf”:

 ![image](https://help.nextcloud.com/uploads/default/original/3X/a/e/aeef9829587d04616ce21a2e2fe520c0333ca071.png)

and paste this code-block into the code-window:

```lua
local files = get_input_files()

-- finding out nextclouds installation dir with shell_command `pwd`
local current_dir = shell_command('pwd').output
-- Loop through input files
for _, input_file in ipairs(files) do
  local log_filename = input_file.name:gsub('%.[Mm][DdWwNnTtXx]+$', '.pdf') .. '.encoding.txt'
  local outfolder = get_parent(input_file)
  local out_meta = meta_data(outfolder)
-- Check if the logfile already exists
  if exists(outfolder, log_filename) then
    abort('Logfile "' .. outfolder.path .. "/" .. outfolder.name .. "/" .. log_filename .. '" already exists. Skipping...')
    goto continue
  end
  local arguments = {}
  if is_file(input_file) then
    local html_filename = input_file.name:gsub('%.[Mm][DdWwNnTtXx]+$', '.html')
    local out_filename = input_file.name:gsub('%.[Mm][DdWwNnTtXx]+$', '.pdf')
  -- Check if the output file already exists
    if exists(outfolder, out_filename) then
      abort('A file "' .. out_filename .. '" already exists in the target directory. Skipping...')
      goto continue
    end
-- meta_data part:
    local in_meta = meta_data(input_file)
    local metadata_args = {
-- obtained from shell_command('pwd')
      "\"nextcloud_dir = " .. current_dir .. "\"",
      "\"in_file = " .. input_file.name .. "\"",
      "\"in_mimetype = " .. in_meta.mimetype .. "\"",
      "\"local_path = " .. out_meta.local_path .. "\"",
      "\"html_filename = " .. html_filename .. "\"",
      "\"out_filename = " .. out_filename .. "\"",
      "\"out_storage_path = " .. out_meta.storage_path .. "/" .. out_filename .. "\"",
    }
-- Concatenate the metadata_args array into a single string with spaces between each argument
    local arguments_string = table.concat(metadata_args, " ")
    table.insert(arguments, arguments_string)
  end

  -- Construct the command string with quotes around each argument
  local command = "/usr/local/bin/nc-md-to-pdf " .. table.concat(arguments, " ")

  -- Run the command
  local result = shell_command(command)

  -- Create debug file with the same name as the output file but with extra .encoding.txt extension
  local output_file = new_file(outfolder, log_filename, result.output .. "\n" .. result.errors)
  if output_file == nil then
    abort("Could not create logfile: " .. log_filename)
  end
  ::continue::
end

```

Activate and store it.

Now create a bash-script  
`/usr/local/bin/nc-md-to-pdf`  
with this content:

```sh
#!/bin/bash

echo "$(date +"%Y-%m-%d %H:%M:%S %Z")"

if [$# -lt 10]; then digits=1
elif [$# -lt 100]; then digits=2
else digits=3
fi

# iterate over the arguments
for (( i=0; i<=$#; i++ )); do
  # format the index with leading zeros
  index=$(printf "%0*d" $digits $i)
  # get the i-th argument
  arg=${!i}
  # echo the argument with its index
  echo " - arg $index = $arg"
  echo "$arg" | grep -q "nextcloud_dir" && NCC="${arg##*= }/occ"
# in:
  echo "$arg" | grep -q "in_file" && in_file="${arg##*= }"
  echo "$arg" | grep -q "in_mimetype" && in_mt="${arg##*= }"
  echo "$arg" | grep -q "local_path" && local_path="${arg##*= }"
# out:
  echo "$arg" | grep -q "html_filename" && html_fn="${arg##*= }"
  echo "$arg" | grep -q "out_filename" && out_fn="${arg##*= }"
  echo "$arg" | grep -q "out_storage_path" && out_sp="${arg##*= /}"
done
echo

in_lp="$local_path/$in_file"
mime_type=$(file -bL --mime-type "$in_lp")
echo "information gathered by file:"
echo " file = $(file -bL "$in_lp")"
echo " --mime-type = $mime_type"
echo "NCC = $NCC"
echo "in_file = $in_file"
echo "in_mt = $in_mt"
echo "local_path = $local_path"
echo "html_fn = $html_fn"
echo "out_fn = $out_fn"
echo "out_sp = $out_sp"

#exit 0
# cd into infile dir to be as relative to images as the md file
cd "$local_path"

if ["$in_mt" = "text/markdown"]; then
  if pandoc -f markdown -t html "$in_file" -o "$html_fn" && soffice --headless --convert-to pdf "$html_fn"; then
    rm "$html_fn"
    $NCC files:scan --verbose --no-interaction --path="$out_sp"
  fi
else
  echo "$in_file seems not to be a markdown file, skipping"
fi

exit 0

```

_(last updated 2023.03.24 12:50 CET)_

and make it executable:  
`chmod +x /usr/local/bin/nc-md-to-pdf`

It creates the pdf file and a log file for debug purposes.

This is a quick sketch and can be fleshed out as you like. So did I not yet work on image resize templates.

 ![image](https://help.nextcloud.com/uploads/default/original/3X/e/0/e0bd902679ae846fe8b18090e517a842f7abb19a.png)

You can trigger it from the context menu but it is possible to make a background job with the flow mechanism, as described [here](https://github.com/Raudius/files_scripts/blob/master/docs/Admin.md#flow)

---

<div class="post-metadata">

**Author:** ![meonkeys](https://help.nextcloud.com/user_avatar/help.nextcloud.com/meonkeys/32/27937_2.png) [@meonkeys](https://help.nextcloud.com/u/meonkeys)\
**Post date:** [March 23, 2023, 3:33am UTC](https://help.nextcloud.com/t/how-do-i-convert-markdown-files-to-pdf-files/158324/5 "2023-03-23T03:33:37Z")

</div>

Wow! Great work.

Also, doing this locally is really easy with pandoc, just:

```bash
pandoc input.md -o output.pdf

```

Nice to have it done on the server, though.

For reference:

> <https://github.com/nextcloud/text/issues/112>
>
> Please add PDF export because people sometimes need print their notes.

---

<div class="post-metadata">

**Author:** ![ernolf](https://help.nextcloud.com/user_avatar/help.nextcloud.com/ernolf/32/77874_2.png) [@ernolf](https://help.nextcloud.com/u/ernolf)\
**Post date:** [March 23, 2023, 7:47am UTC](https://help.nextcloud.com/t/how-do-i-convert-markdown-files-to-pdf-files/158324/6 "2023-03-23T07:47:12Z")

</div>

> [@meonkeys](#):
>
> `pandoc input.md -o output.pdf`

Of course I have tried that too but it needs about 1 GB on LateX dependencies and then still not able to handle emojis.

---

<div class="post-metadata">

**Author:** ![ernolf](https://help.nextcloud.com/user_avatar/help.nextcloud.com/ernolf/32/77874_2.png) [@ernolf](https://help.nextcloud.com/u/ernolf)\
**Post date:** [March 23, 2023, 9:44am UTC](https://help.nextcloud.com/t/how-do-i-convert-markdown-files-to-pdf-files/158324/7 "2023-03-23T09:44:50Z")

</div>

Of course there is the integrated [html\_to\_pdf() function](https://github.com/Raudius/files_scripts/blob/master/docs/Functions.md#html_to_pdf) in the files\_script App but the complexity of the [mPDF configuration](https://mpdf.github.io/reference/mpdf-variables/overview.html) overwelmed me.  
But I’ll definitely try that solution when I get in the mood for it…

---

<div class="post-metadata">

**Author:** ![Wolfgang\_Moser](https://help.nextcloud.com/user_avatar/help.nextcloud.com/wolfgang_moser/32/49963_2.png) [@Wolfgang\_Moser](https://help.nextcloud.com/u/Wolfgang_Moser)\
**Post date:** [March 30, 2023, 2:27pm UTC](https://help.nextcloud.com/t/how-do-i-convert-markdown-files-to-pdf-files/158324/8 "2023-03-30T14:27:46Z")

</div>

Hi @ernolf ,

thanks for your effort. I tried the solution today but I’m always getting an error when I try to convert the file.

```auto
2023-03-30 16:25:33 CEST
  - arg 0 = /usr/local/bin/nc-md-to-pdf
  - arg 1 = nil

information gathered by file:
  file = directory
     --mime-type = inode/directory
NCC = 
in_file = 
in_mt = 
local_path = 
html_fn = 
out_fn = 
out_sp = 
 seems not to be a markdown file, skipping

```

Any idea what’s going wrong?

Thanks and br, Wolfgang

---

<div class="post-metadata">

**Author:** ![ernolf](https://help.nextcloud.com/user_avatar/help.nextcloud.com/ernolf/32/77874_2.png) [@ernolf](https://help.nextcloud.com/u/ernolf)\
**Post date:** [March 30, 2023, 3:26pm UTC](https://help.nextcloud.com/t/how-do-i-convert-markdown-files-to-pdf-files/158324/9 "2023-03-30T15:26:39Z")

</div>

> [@Wolfgang\_Moser](#):
>
> `arg 1 = nil`

There are not passed any arguments. The error arises in the “File actions” part.  
Double check that the lua-code is exactly as I stated above.

---

<div class="post-metadata">

**Author:** ![Wolfgang\_Moser](https://help.nextcloud.com/user_avatar/help.nextcloud.com/wolfgang_moser/32/49963_2.png) [@Wolfgang\_Moser](https://help.nextcloud.com/u/Wolfgang_Moser)\
**Post date:** [April 12, 2023, 1:21pm UTC](https://help.nextcloud.com/t/how-do-i-convert-markdown-files-to-pdf-files/158324/10 "2023-04-12T13:21:31Z")

</div>

Hi @ernolf,

I finally found some time to debug and fix the issue caused by a PHP version mismatch on the server.

Your solution is working but have still some issues. When converting a markdown file to pdf I am getting a permission error from the `files:scan` job and the pdf is not shown in the folder.

```auto
... using filter : writer_web_pdf_Export
/usr/local/bin/nc-md-to-pdf: line 52: /var/www/html/nextcloud/occ: Permission denied

```

This is probably caused by a racing condition because pdf generation has not been finished yet.  
Doing a manual rescan afterward (`occ files:scan --all`) reveals the generated pdf file in the folder.

The second issue is that the images in the pdf are not scaled to fit the page size as in the md viewer but get cropped on the right side.

Nevertheless, I think this is still a viable solution. Thanks a lot for the effort you have put into answering the question. I will mark the question as resolved.

Thanks and best regards,  
Wolfgang

---

<div class="post-metadata">

**Author:** ![wwe](https://help.nextcloud.com/user_avatar/help.nextcloud.com/wwe/32/72963_2.png) [@wwe](https://help.nextcloud.com/u/wwe)\
**Post date:** [December 14, 2024, 12:11pm UTC](https://help.nextcloud.com/t/how-do-i-convert-markdown-files-to-pdf-files/158324/11 "2024-12-14T12:11:05Z")

</div>


