harmon25

harmon25

PDFTron + Elixir

Hi all,

I have been building a PDFTron nif for a work project, and it is pretty awesome.
We are leveraging both the webview javascript library, and the C++ code integrated as a nif (nifcpp) for server side pdf + office doc processing. PDFTron exposes possibly the best(not java) headless docx/pptx/xlsx to pdf conversion I have come across. Only downside, it is not free; however they offer a very generous trial/demo to use for development.

Has anyone tried it before?

I posted some questions to slack, but figured I would also ask them here for more exposure.

  1. Operations on a PDF (creation, modify, conversion) are these dirty? If so, is it CPU or IO bound?
    • Got some confirmation this is CPU bound on slack, which was also my hypothesis.
    • If reading/writing the PDF file from the nif, would that change to being IO bound?
      • Not sure exactly how one would determine if a nif is both IO and CPU bound, which scheduler option should be chosen; for example does CPU take precedent over IO, if they were equally dirty?
  2. Is a nif even the right tool for this? Would a port suffice?
    • I have a nif working, but a bit concerned about safety…
    • Would wrapping the C++ in some rust, and using rustler be of any benefit?
  3. We are passing strings from Elixir → C++ that represent the contents of a PDF file, and returning back contents as a string of a new pdf file.
    • would using a resource binary be better for the input/output (more performant/safer)?
    • was having some trouble getting this working, if this is the best way, might need some help.
  4. Having a bit of trouble dynamically linking the PDFTron compiled .so file in the context of a mix package (portable), tips?
    • Need to do something like this export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:"$(pwd)/c_src/Lib"
    • Could I just set something in the nif init function? System.put_env("LD_LIBRARY_PATH", ...)
    • Where should I put this .so file? have it in c_src/Lib of the mix project, should it be in priv/ with my compiled .so?

Thanks in advance for taking the time to reply!

Most Liked

ityonemo

ityonemo

  1. I would personally wrap this in an os thread, using enif_thread_create and friends.
    1b. If you’re actually reading/writing the file inside the nif (which i don’t think you should do unless you need mmap or O_DIRECT, which you shouldn’t for pdfs), then it probably depends on the contents of the pdf.
  2. I would probably pick a port instead of a nif, because pdf is inherently unsafe. You could also do something like set up a c_node.
    2a. The inherently unsafe parts of pdf have nothing to do with the forms of safety that rust gives you, so wrapping in rustler will do nothing for you except making your calls at the boundary safe. (I developed Zigler, which lets you wrap in zig; similarly, there would be no safety benefits except making sure your nif interface is correct and marshalling into C types and doing ArgumentError if you mess up)
  3. Resources are only useful if you are passing either a. mutable binaries or b. unserialized data between NIF functions. If your functions aren’t “in place” binary functions. then it’s much better to just marshal into erlang’s binaries.
  4. drop it into priv, and get the priv directory using :code.priv_dir, your life will be so much simpler. I believe Rustler and Zigler do this by default.
elcritch

elcritch

For finding the .so file, the most common pattern is using :code.priv_dir/1.

See Reading files from your priv dir in elixir - Mintcore

Though with the updated build setup in Elixir 1.9+ it’s more stable to put it in the _build folder. I just copy the Makefile from GitHub - elixir-circuits/circuits_gpio: Use GPIOs from Elixir · GitHub .

Last Post!

ityonemo

ityonemo

  1. Spawning in a thread offers you no protection whatsoever; a segfault will still crash the entire BEAM.

  2. That’s why ports are still safer; you probably aren’t going to run both a port and a nif. But even ports don’t give you supervision, so zombie processes can be a thing.

  3. That’s exactly the nif vs port trade-off. One last thing to remember is if you do read a binary in a separate thread, to read the binary from the environment of the new thread, otherwise the GC could take the binary away from under you and cause a segfault… I think. This is one of the reasons why nifs are hard.

  4. You don’t typically need to set LD_LIBRARY_PATH for nifs; you can give an absolute path to the nif load call, which I believe is instrumented in @after_compile

Where Next?

Popular in Discussions Top

ben-pr-p
In general I’ve been sticking to this community style guide GitHub - christopheradams/elixir_style_guide: A community driven style guide ...
New
New
chulkilee
Here are the list of HTTP client libraries/wrappers, and some thoughts on HTTP client in general. I’d like to hear from others how they w...
New
Nvim
Anybody knows a comprehensive comparison of Django and Phoenix, thanks for the help. Where are they similar? Where do they differ the m...
New
mikl
I wanted to capitalize a string, and tried using String.capitalize(). That generally works well, until you try to capitalize a word like...
New
owaisqayum
I have a sample string sentence = "Hello, world ... 123 *** ^%&*())^% %%:>" From this string, I want to only keep the integers, ...
New
pillaiindu
I want to convert a Phoenix LiveView CRUD website to a CRUD mobile app. What do you think is the easiest way to do so?
New

Other popular topics Top

vertexbuffer
Hello, can anybody help here..? I have a list of players and I what to delete an element, but every for loop the list is reverting to ori...
New
vonH
When I run the Plug and I recompile I wind up having to use Ctrl C to quit iex and start again. Witht the help of rlwrap I can use the cu...
New
jononomo
For some reason my phoenix channels are working for me in my local dev environment, but as soon as I deploy via Docker, I get a 403 error...
New
nsuchy
Hi. I’ve noticed that Windows Powershell has it’s own IEX command and you cannot access Elixir’s IEX due to the conflict. This isn’t a cr...
New
Darmani72
If I have a post route which an argument: post /my_post_route/:my_param1, MyController.my_post_handler How would get the post params ...
New
gausby
I asked this very same question on twitter and got some interesting feedback, but I thought it would be a good question to ask here as we...
1207 40082 209
New

We're in Beta

About us Mission Statement