Skip to content

Commit 7e27d6f

Browse files
committed
Make pdf_redlines a required dependency
Precompiled NIF, no Rust toolchain needed. Removes the runtime Code.ensure_loaded? detection in favour of direct calls.
1 parent d8857b5 commit 7e27d6f

6 files changed

Lines changed: 20 additions & 51 deletions

File tree

CHANGELOG.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@
22

33
## 0.5.0
44

5-
- Stable public release.
5+
- `pdf_redlines` is now a required dependency (precompiled NIF, no toolchain needed).
6+
- Removed runtime detection of `PDFRedlines` module availability.
7+
- Expanded README with badges, full API docs, and usage examples.
68

79
## 0.1.0
810

README.md

Lines changed: 3 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
Extract and normalize tracked changes ("redlines") from DOCX and PDF documents into a single unified shape.
88

9-
Redlines parses `<w:ins>` and `<w:del>` elements from DOCX files and integrates with the optional [`pdf_redlines`](https://hex.pm/packages/pdf_redlines) NIF for PDF extraction. All changes are normalized into `Redlines.Change` structs regardless of source format.
9+
Redlines parses `<w:ins>` and `<w:del>` elements from DOCX files and uses [`pdf_redlines`](https://hex.pm/packages/pdf_redlines) (precompiled Rust/MuPDF NIF) for PDF extraction. All changes are normalized into `Redlines.Change` structs regardless of source format.
1010

1111
## Installation
1212

@@ -20,15 +20,7 @@ def deps do
2020
end
2121
```
2222

23-
### PDF Support
24-
25-
PDF extraction requires the [`pdf_redlines`](https://hex.pm/packages/pdf_redlines) package (Rust/MuPDF NIF). Add it alongside `redlines` if you need PDF support:
26-
27-
```elixir
28-
{:pdf_redlines, "~> 0.6"}
29-
```
30-
31-
If `pdf_redlines` is not installed, DOCX extraction works standalone with no additional dependencies beyond `sweet_xml`.
23+
PDF support is included out of the box via the precompiled [`pdf_redlines`](https://hex.pm/packages/pdf_redlines) NIF -- no Rust toolchain required.
3224

3325
## Usage
3426

@@ -39,7 +31,7 @@ If `pdf_redlines` is not installed, DOCX extraction works standalone with no add
3931
{:ok, %Redlines.Result{changes: changes, source: :docx}} =
4032
Redlines.extract("contract_v2.docx")
4133

42-
# PDF - requires :pdf_redlines dependency
34+
# PDF
4335
{:ok, %Redlines.Result{changes: changes, source: :pdf}} =
4436
Redlines.extract("contract_v2.pdf")
4537

lib/redlines.ex

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,6 @@ defmodule Redlines do
66
77
- DOCX track changes (`<w:ins>`, `<w:del>`)
88
- PDFs with embedded tracked-changes markup (via [`pdf_redlines`](https://hex.pm/packages/pdf_redlines))
9-
10-
PDF support is optional: if you want PDF extraction, add `:pdf_redlines` to your
11-
application's dependencies.
129
"""
1310

1411
alias Redlines.{Change, DOCX, Format, PDF, Result}

lib/redlines/pdf.ex

Lines changed: 10 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,39 +1,29 @@
11
defmodule Redlines.PDF do
22
@moduledoc """
3-
PDF adapter.
4-
5-
This module integrates with the `pdf_redlines` package (Rust/MuPDF NIF) if it
6-
is available at runtime.
3+
PDF adapter using the `pdf_redlines` package (precompiled Rust/MuPDF NIF).
74
"""
85

96
alias Redlines.Change
107

118
@doc """
129
Extract redlines from a PDF file path.
13-
14-
Requires the `pdf_redlines` package to be present in the caller's dependency
15-
tree.
1610
"""
17-
@spec extract_redlines(Path.t(), keyword() | map()) :: {:ok, list()} | {:error, term()}
11+
@spec extract_redlines(Path.t(), keyword()) :: {:ok, list()} | {:error, term()}
1812
def extract_redlines(pdf_path, opts \\ []) when is_binary(pdf_path) do
19-
with {:ok, arity} <- ensure_pdf_redlines_loaded(:extract_redlines),
20-
{:ok, result} <- do_apply(PDFRedlines, :extract_redlines, arity, [pdf_path, opts]) do
21-
case result do
22-
%{redlines: redlines} when is_list(redlines) -> {:ok, redlines}
23-
%_{redlines: redlines} when is_list(redlines) -> {:ok, redlines}
24-
other -> {:error, {:unexpected_pdf_redlines_result, other}}
25-
end
13+
case PDFRedlines.extract_redlines(pdf_path, opts) do
14+
{:ok, %{redlines: redlines}} when is_list(redlines) -> {:ok, redlines}
15+
{:ok, %_{redlines: redlines}} when is_list(redlines) -> {:ok, redlines}
16+
{:ok, other} -> {:error, {:unexpected_pdf_redlines_result, other}}
17+
{:error, _} = error -> error
2618
end
2719
end
2820

2921
@doc """
30-
Fast redline presence check for PDFs (early-exit in `pdf_redlines`).
22+
Fast redline presence check for PDFs (early-exit).
3123
"""
32-
@spec has_redlines?(Path.t(), keyword() | map()) :: {:ok, boolean()} | {:error, term()}
24+
@spec has_redlines?(Path.t(), keyword()) :: {:ok, boolean()} | {:error, term()}
3325
def has_redlines?(pdf_path, opts \\ []) when is_binary(pdf_path) do
34-
with {:ok, arity} <- ensure_pdf_redlines_loaded(:has_redlines?) do
35-
do_apply(PDFRedlines, :has_redlines?, arity, [pdf_path, opts])
36-
end
26+
PDFRedlines.has_redlines?(pdf_path, opts)
3727
end
3828

3929
@doc """
@@ -59,20 +49,4 @@ defmodule Redlines.PDF do
5949
end
6050

6151
defp to_change(_), do: nil
62-
63-
defp ensure_pdf_redlines_loaded(fun) when is_atom(fun) do
64-
cond do
65-
Code.ensure_loaded?(PDFRedlines) and function_exported?(PDFRedlines, fun, 2) ->
66-
{:ok, 2}
67-
68-
Code.ensure_loaded?(PDFRedlines) and function_exported?(PDFRedlines, fun, 1) ->
69-
{:ok, 1}
70-
71-
true ->
72-
{:error, :pdf_redlines_not_available}
73-
end
74-
end
75-
76-
defp do_apply(mod, fun, 2, [path, opts]), do: apply(mod, fun, [path, opts])
77-
defp do_apply(mod, fun, 1, [path, _opts]), do: apply(mod, fun, [path])
7852
end

mix.exs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ defmodule Redlines.MixProject do
3333
defp deps do
3434
[
3535
{:sweet_xml, "~> 0.7"},
36+
{:pdf_redlines, "~> 0.6"},
3637
{:credo, "~> 1.7", only: [:dev, :test], runtime: false},
3738
{:dialyxir, "~> 1.4", only: [:dev, :test], runtime: false},
3839
{:ex_doc, "~> 0.39.3", only: :dev, runtime: false},

mix.lock

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
%{
22
"bunt": {:hex, :bunt, "1.0.0", "081c2c665f086849e6d57900292b3a161727ab40431219529f13c4ddcf3e7a44", [:mix], [], "hexpm", "dc5f86aa08a5f6fa6b8096f0735c4e76d54ae5c9fa2c143e5a1fc7c1cd9bb6b5"},
3+
"castore": {:hex, :castore, "1.0.17", "4f9770d2d45fbd91dcf6bd404cf64e7e58fed04fadda0923dc32acca0badffa2", [:mix], [], "hexpm", "12d24b9d80b910dd3953e165636d68f147a31db945d2dcb9365e441f8b5351e5"},
34
"credo": {:hex, :credo, "1.7.16", "a9f1389d13d19c631cb123c77a813dbf16449a2aebf602f590defa08953309d4", [:mix], [{:bunt, "~> 0.2.1 or ~> 1.0", [hex: :bunt, repo: "hexpm", optional: false]}, {:file_system, "~> 0.2 or ~> 1.0", [hex: :file_system, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "d0562af33756b21f248f066a9119e3890722031b6d199f22e3cf95550e4f1579"},
45
"dialyxir": {:hex, :dialyxir, "1.4.7", "dda948fcee52962e4b6c5b4b16b2d8fa7d50d8645bbae8b8685c3f9ecb7f5f4d", [:mix], [{:erlex, ">= 0.2.8", [hex: :erlex, repo: "hexpm", optional: false]}], "hexpm", "b34527202e6eb8cee198efec110996c25c5898f43a4094df157f8d28f27d9efe"},
56
"earmark_parser": {:hex, :earmark_parser, "1.4.44", "f20830dd6b5c77afe2b063777ddbbff09f9759396500cdbe7523efd58d7a339c", [:mix], [], "hexpm", "4778ac752b4701a5599215f7030989c989ffdc4f6df457c5f36938cc2d2a2750"},
@@ -11,6 +12,8 @@
1112
"makeup_elixir": {:hex, :makeup_elixir, "1.0.1", "e928a4f984e795e41e3abd27bfc09f51db16ab8ba1aebdba2b3a575437efafc2", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}, {:nimble_parsec, "~> 1.2.3 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "7284900d412a3e5cfd97fdaed4f5ed389b8f2b4cb49efc0eb3bd10e2febf9507"},
1213
"makeup_erlang": {:hex, :makeup_erlang, "1.0.3", "4252d5d4098da7415c390e847c814bad3764c94a814a0b4245176215615e1035", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "953297c02582a33411ac6208f2c6e55f0e870df7f80da724ed613f10e6706afd"},
1314
"nimble_parsec": {:hex, :nimble_parsec, "1.4.2", "8efba0122db06df95bfaa78f791344a89352ba04baedd3849593bfce4d0dc1c6", [:mix], [], "hexpm", "4b21398942dda052b403bbe1da991ccd03a053668d147d53fb8c4e0efe09c973"},
15+
"pdf_redlines": {:hex, :pdf_redlines, "0.6.2", "59b3d836be79880d988c4db4cbfe503ed4ecd572daa8dd4188da2f1417b07126", [:mix], [{:rustler, "~> 0.37.1", [hex: :rustler, repo: "hexpm", optional: true]}, {:rustler_precompiled, "~> 0.8.4", [hex: :rustler_precompiled, repo: "hexpm", optional: false]}], "hexpm", "b2eac630c8abc851c2d9a1726c1e812c6e1eeb1571d5dac1b1a6f949002e16c5"},
1416
"quokka": {:hex, :quokka, "2.11.2", "2856118154425f18547720d997199be54febed771a740ba3c988a17762328287", [:mix], [{:credo, "~> 1.7", [hex: :credo, repo: "hexpm", optional: false]}], "hexpm", "8208f5d814007cb35a2eb278462464d083fca8c463f62517ab94eef982f181cc"},
17+
"rustler_precompiled": {:hex, :rustler_precompiled, "0.8.4", "700a878312acfac79fb6c572bb8b57f5aae05fe1cf70d34b5974850bbf2c05bf", [:mix], [{:castore, "~> 0.1 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: false]}, {:rustler, "~> 0.23", [hex: :rustler, repo: "hexpm", optional: true]}], "hexpm", "3b33d99b540b15f142ba47944f7a163a25069f6d608783c321029bc1ffb09514"},
1518
"sweet_xml": {:hex, :sweet_xml, "0.7.5", "803a563113981aaac202a1dbd39771562d0ad31004ddbfc9b5090bdcd5605277", [:mix], [], "hexpm", "193b28a9b12891cae351d81a0cead165ffe67df1b73fe5866d10629f4faefb12"},
1619
}

0 commit comments

Comments
 (0)