harmon25
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.
- 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?
- 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?
- 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.
- 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/Libof the mix project, should it be inpriv/with my compiled .so?
- Need to do something like this
Thanks in advance for taking the time to reply!
Trending in Discussions
As the title says, please share what you’ve been up to with Elixir. Whether that’s been learning it, looking into it, making stuff with i...
New
The obligatory hello world thread!
Who are you and where are you from? :stuck_out_tongue:
New
@chrismccord : I just saw the Extract AGENTS.md from Phoenix.new into phx.new generator commit to the phoenix project.
My initial shotgu...
New
I was working on an Ecto migration and I needed a timestamp. So, for the nth time, I looked up the different data types for timestamps, a...
New
Fly’s CEO posted this recently - Turn And Face The Strange · The Fly Blog
It says that Fly is going all-in on sprites, which is a worry ...
New
We’re evaluating API mocking tools for OpenAPI-based projects and would love to hear what other teams are using.
We’re particularly inte...
New
Is there a word for the ~> symbol used in Version strings?
Do you also just call it a Squiggle Arrow™ ?!
New
Other Trending Topics
Hey, I’m Jesse and I’m the main contributor behind Dexter, a full-featured, lightning-fast Elixir LSP optimized for large codebases. It s...
New
Beam Bots (or just BB for short) is a framework for building fault-tolerant robotics applications in Elixir using familiar OTP patterns. ...
New
ExRatatui lets you cook up rich terminal UIs in Elixir, powered by Rust’s ratatui via Rustler NIFs. Build interactive terminal applicatio...
New
Corex is an accessible, unstyled UI component library for Phoenix that integrates Zag.js state machines using Vanilla JavaScript and Live...
New
Hello everyone. After busy few months I am happy to announce v0.1.0 of Emerge & Solve.
They are GUI (Emerge) and State management (S...
New
Emily is an Elixir library that runs Nx computations on Apple’s MLX. Install it as the default Nx backend and Nx, defn, Axon, Nx.Serving,...
New
Chat & Discussions>Discussions
Categories:
Sub Categories:
Forums
Popular Tags
- #ecto
- #liveview
- #troubleshooting
- #learning-elixir
- #deployment
- #library
- #erlang
- #testing
- #genserver
- #mix
- #absinthe
- #remote-other
- #otp
- #plug
- #how-to-question
- #macros
- #postgres
- #channels
- #elixirconf
- #exunit
- #discussion
- #code-sync
- #javascript
- #podcasts
- #onsite
- #dialyzer
- #docker
- #authentication
- #umbrella
- #full-time-contract
- #podcasts-by-brainlid
- #ecto-query
- #elixir-ls
- #blog-post
- #phoenix_html
- #iex
- #ai
- #graphql
- #genstage
- #elixirconf-us
- #websockets
- #supervisor
- #advent-of-code
- #distillery
- #processes
- #api
- #forms
- #metaprogramming
- #security
- #hex











Showing Posts 1 to 5- Show Best Posts
- Show All (oldest first)
- Show All (newest first)
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 .
ityonemo
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.
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)
harmon25
Thanks!
I might setup a script that can download the c dependencies into priv directory over https before building for the first time, so as to better keep them up to date…
harmon25
Thanks @ityonemo,
I have seen some of your Zig related posts - pretty neat stuff!
I like the idea of wrapping it in a thread, cannot seem to find any simple sample of
enif_thread_create, any good examples you have seen? I think I will post an example of my nif code to help the discussion.If the nif call is wrapped in a thread I presume that helps ensure if it were to crash it is isolated and the beam can keep ticking without much issue? also maybe the vms’ memory space is not as exposed? I presume the trade off between nif vs port becomes less relevant when adding the overhead of a new thread.
If I had it running in a thread, would you still suggest a port? (to continue the thought from above)
I am sharing/passing full pdf files as arguments to the functions, which can be optimized better in a nif i would think? as the memory can just be shared via pointer as apposed to being copied around, or at-least more efficiently copied around? I may be wrong…
In the case of the pdf blob to the nif, it is un-serialized I think, and can be read only. Would create a new binary for the response, am not really benefiting from marshaling the data - just really a pointer to some bytes in memory
Yea priv seems to be the right place, will play around with setting the
LD_LIBRARY_PATHenvironment variable before loading the nif to see how that works. ..ityonemo
Spawning in a thread offers you no protection whatsoever; a segfault will still crash the entire BEAM.
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.
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.
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