diff --git a/.claude/settings.local.json b/.claude/settings.local.json
new file mode 100644
index 0000000..f9eb3d6
--- /dev/null
+++ b/.claude/settings.local.json
@@ -0,0 +1,8 @@
+{
+ "permissions": {
+ "allow": [
+ "Bash(dotnet build *)",
+ "Bash(dotnet test *)"
+ ]
+ }
+}
diff --git a/.github/workflows/dotnet-linux.yml b/.github/workflows/dotnet-linux.yml
index 0fde9f2..06aee17 100644
--- a/.github/workflows/dotnet-linux.yml
+++ b/.github/workflows/dotnet-linux.yml
@@ -16,12 +16,12 @@ jobs:
- name: Setup .NET Core
uses: actions/setup-dotnet@v1
with:
- dotnet-version: 8.0.x
+ dotnet-version: 9.0.x
- name: Install mscorefonts
run: sudo apt-get -y install ttf-mscorefonts-installer
- name: Install dependencies
run: dotnet restore
- name: Build net8.0
- run: dotnet build --configuration Release --no-restore --framework net8.0
+ run: dotnet build --configuration Release --no-restore --framework net9.0
- name: Test net8.0
- run: dotnet test --no-restore --verbosity minimal --framework net8.0
+ run: dotnet test --no-restore --verbosity minimal --framework net9.0
diff --git a/.github/workflows/dotnet-mac.yml b/.github/workflows/dotnet-mac.yml
index 43a5d41..f2457c2 100644
--- a/.github/workflows/dotnet-mac.yml
+++ b/.github/workflows/dotnet-mac.yml
@@ -14,10 +14,10 @@ jobs:
- name: Setup .NET Core
uses: actions/setup-dotnet@v1
with:
- dotnet-version: 8.0.x
+ dotnet-version: 9.0.x
- name: Install dependencies
run: dotnet restore
- name: Build net8.0
- run: dotnet build --configuration Release --no-restore --framework net8.0
+ run: dotnet build --configuration Release --no-restore --framework net9.0
- name: Test net8.0
- run: dotnet test --no-restore --verbosity minimal --framework net8.0
+ run: dotnet test --no-restore --verbosity minimal --framework net9.0
diff --git a/.github/workflows/dotnet-windows.yml b/.github/workflows/dotnet-windows.yml
index e527dbc..bfc74d5 100644
--- a/.github/workflows/dotnet-windows.yml
+++ b/.github/workflows/dotnet-windows.yml
@@ -14,7 +14,7 @@ jobs:
- name: Setup .NET Core
uses: actions/setup-dotnet@v1
with:
- dotnet-version: 8.0.x
+ dotnet-version: 9.0.x
- name: Install dependencies
run: dotnet restore
- name: Build
diff --git a/README.md b/README.md
index 7a56f0c..ecfd7b2 100644
--- a/README.md
+++ b/README.md
@@ -5,7 +5,7 @@


-- Supports netstandard2.0, net462, net471, net6.0, net8.0
+- Supports netstandard2.0 / 2.1, net462, net471, net6.0, net8.0 and net9.0
- No java bindings
NuGet packages available on the [releases](https://github.com/BobLd/tabula-sharp/releases) page and on www.nuget.org:
diff --git a/Tabula.Csv/Tabula.Csv.csproj b/Tabula.Csv/Tabula.Csv.csproj
index 9f8ccae..603eaac 100644
--- a/Tabula.Csv/Tabula.Csv.csproj
+++ b/Tabula.Csv/Tabula.Csv.csproj
@@ -1,32 +1,32 @@
-
- netstandard2.0;net462;net471;net6.0;net8.0
- Extract tables from PDF files (port of tabula-java using PdfPig). Csv and Tsv writers.
- https://github.com/BobLd/tabula-sharp
- 0.1.5
- BobLd
- pdf, extract, table, tabula, pdfpig, parse, extraction, csv, tsv, excel, export
- MIT
- https://github.com/BobLd/tabula-sharp
- True
- True
- README.md
-
+
+ netstandard2.0;netstandard2.1;net462;net471;net6.0;net8.0;net9.0
+ Extract tables from PDF files (port of tabula-java using PdfPig). Csv and Tsv writers.
+ https://github.com/BobLd/tabula-sharp
+ 1.0.0
+ BobLd
+ pdf, extract, table, tabula, pdfpig, parse, extraction, csv, tsv, excel, export
+ MIT
+ https://github.com/BobLd/tabula-sharp
+ True
+ True
+ README.md
+
-
-
- True
- \
-
-
+
+
+ True
+ \
+
+
-
-
-
+
+
+
-
-
-
+
+
+
-
+
\ No newline at end of file
diff --git a/Tabula.Json/Tabula.Json.csproj b/Tabula.Json/Tabula.Json.csproj
index 06cb3aa..9e94d05 100644
--- a/Tabula.Json/Tabula.Json.csproj
+++ b/Tabula.Json/Tabula.Json.csproj
@@ -1,32 +1,32 @@
-
- netstandard2.0;net462;net471;net6.0;net8.0
- Extract tables from PDF files (port of tabula-java using PdfPig). Json writer.
- https://github.com/BobLd/tabula-sharp
- 0.1.5
- BobLd
- BobLd
- pdf, extract, table, tabula, pdfpig, parse, extraction, json, export
- MIT
- https://github.com/BobLd/tabula-sharp
- True
- README.md
-
+
+ netstandard2.0;netstandard2.1;net462;net471;net6.0;net8.0;net9.0
+ Extract tables from PDF files (port of tabula-java using PdfPig). Json writer.
+ https://github.com/BobLd/tabula-sharp
+ 1.0.0
+ BobLd
+ BobLd
+ pdf, extract, table, tabula, pdfpig, parse, extraction, json, export
+ MIT
+ https://github.com/BobLd/tabula-sharp
+ True
+ README.md
+
-
-
- True
- \
-
-
+
+
+ True
+ \
+
+
-
-
-
+
+
+
-
-
-
+
+
+
-
+
\ No newline at end of file
diff --git a/Tabula.Tests/GitHubIssues.cs b/Tabula.Tests/GitHubIssues.cs
new file mode 100644
index 0000000..c81a555
--- /dev/null
+++ b/Tabula.Tests/GitHubIssues.cs
@@ -0,0 +1,22 @@
+using Tabula.Detectors;
+using UglyToad.PdfPig;
+using Xunit;
+
+namespace Tabula.Tests
+{
+ public class GithubIssues
+ {
+ [Fact]
+ public void Issue30()
+ {
+ using (PdfDocument document = PdfDocument.Open("Resources/issue.pdf", new ParsingOptions() { ClipPaths = true }))
+ {
+ PageArea page = ObjectExtractor.Extract(document, 1);
+
+ SimpleNurminenDetectionAlgorithm detector = new SimpleNurminenDetectionAlgorithm();
+ var regions = detector.Detect(page);
+ Assert.Single(regions);
+ }
+ }
+ }
+}
diff --git a/Tabula.Tests/Resources/issue.pdf b/Tabula.Tests/Resources/issue.pdf
new file mode 100644
index 0000000..57b0cea
Binary files /dev/null and b/Tabula.Tests/Resources/issue.pdf differ
diff --git a/Tabula.Tests/Tabula.Tests.csproj b/Tabula.Tests/Tabula.Tests.csproj
index 5816e9b..c905667 100644
--- a/Tabula.Tests/Tabula.Tests.csproj
+++ b/Tabula.Tests/Tabula.Tests.csproj
@@ -1,1052 +1,1055 @@
-
- net8.0
+
+ net9.0
- false
+ false
- 0.1.2
-
+ 0.1.2
+
-
-
-
-
-
-
- all
- runtime; build; native; contentfiles; analyzers; buildtransitive
-
-
- all
- runtime; build; native; contentfiles; analyzers; buildtransitive
-
-
+
+
+
+
+
+
+ all
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+
+
+ all
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+
+
-
-
-
-
-
+
+
+
+
+
-
-
- Always
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- Always
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
- PreserveNewest
-
-
+
+
+ Always
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ Always
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
diff --git a/Tabula/Detectors/SimpleNurminenDetectionAlgorithm.cs b/Tabula/Detectors/SimpleNurminenDetectionAlgorithm.cs
index e7bef85..9006338 100644
--- a/Tabula/Detectors/SimpleNurminenDetectionAlgorithm.cs
+++ b/Tabula/Detectors/SimpleNurminenDetectionAlgorithm.cs
@@ -110,7 +110,7 @@ public IReadOnlyList Detect(PageArea page)
List allEdges = new List(horizontalRulings);
allEdges.AddRange(verticalRulings);
- List tableAreas = new List();
+ HashSet tableAreas = new HashSet();
// if we found some edges, try to find some tables based on them
if (allEdges.Count > 0)
@@ -236,13 +236,14 @@ public IReadOnlyList Detect(PageArea page)
// get rid of any text lines contained within existing tables, this allows us to find more tables
//for (Iterator iterator = lines.iterator(); iterator.hasNext();)
- foreach (var textRow in lines.ToList())
+ for (int i = lines.Count - 1; i >= 0; i--)
{
- foreach (TableRectangle table in tableAreas)
+ var textRow = lines[i];
+ foreach (var table in tableAreas)
{
if (table.Contains(textRow))
{
- lines.Remove(textRow);
+ lines.RemoveAt(i);
break;
}
}
@@ -278,8 +279,7 @@ public IReadOnlyList Detect(PageArea page)
if (table != null)
{
- foundTable = true;
- tableAreas.Add(table);
+ foundTable = tableAreas.Add(table);
}
}
} while (foundTable);
@@ -725,7 +725,7 @@ private TextEdges getTextEdges(List lines)
return new TextEdges(leftTextEdges, midTextEdges, rightTextEdges);
}
- private List getTableAreasFromCells(List cells)
+ private HashSet getTableAreasFromCells(List cells)
{
List> cellGroups = new List>();
foreach (TableRectangle cell in cells)
@@ -764,7 +764,7 @@ private List getTableAreasFromCells(List cells)
}
// create table areas based on cell group
- List tableAreas = new List();
+ HashSet tableAreas = new HashSet();
foreach (List cellGroup in cellGroups)
{
// less than four cells should not make a table
diff --git a/Tabula/Extractors/SpreadsheetExtractionAlgorithm.cs b/Tabula/Extractors/SpreadsheetExtractionAlgorithm.cs
index a24e9df..4cca586 100644
--- a/Tabula/Extractors/SpreadsheetExtractionAlgorithm.cs
+++ b/Tabula/Extractors/SpreadsheetExtractionAlgorithm.cs
@@ -287,7 +287,6 @@ public static List FindSpreadsheetsFromCells(List pointSet = new HashSet();
Dictionary edgesH = new Dictionary();
Dictionary edgesV = new Dictionary();
- int i = 0;
cells = new List(new HashSet(cells));
@@ -316,26 +315,27 @@ public static List FindSpreadsheetsFromCells(List pointsSortY = new List(pointSet);
pointsSortY.Sort(new POINT_COMPARER());
- while (i < pointSet.Count)
+ // Pair consecutive points sharing a Y / X coordinate. Using a for-loop with an
+ // unconditional increment (rather than the upstream tabula-java nested while)
+ // prevents an infinite loop when a coordinate is NaN or Infinity, since Feq
+ // returns false for any NaN comparison and would otherwise leave i unchanged.
+ for (int i = 0; i + 1 < pointSet.Count; i++)
{
- float currY = (float)pointsSortY[i].Y;
- while (i < pointSet.Count && Utils.Feq(pointsSortY[i].Y, currY))
+ if (Utils.Feq(pointsSortY[i].Y, pointsSortY[i + 1].Y))
{
edgesH[pointsSortY[i]] = pointsSortY[i + 1];
edgesH[pointsSortY[i + 1]] = pointsSortY[i];
- i += 2;
+ i++;
}
}
- i = 0;
- while (i < pointSet.Count)
+ for (int i = 0; i + 1 < pointSet.Count; i++)
{
- float currX = (float)pointsSortX[i].X;
- while (i < pointSet.Count && Utils.Feq(pointsSortX[i].X, currX))
+ if (Utils.Feq(pointsSortX[i].X, pointsSortX[i + 1].X))
{
edgesV[pointsSortX[i]] = pointsSortX[i + 1];
edgesV[pointsSortX[i + 1]] = pointsSortX[i];
- i += 2;
+ i++;
}
}
diff --git a/Tabula/ObjectExtractor.cs b/Tabula/ObjectExtractor.cs
index ab45c9e..7fd5dbb 100644
--- a/Tabula/ObjectExtractor.cs
+++ b/Tabula/ObjectExtractor.cs
@@ -75,7 +75,7 @@ public static PageArea ExtractPage(Page page)
// se.processPage(p);
var rulings = new List();
- foreach (var path in page.ExperimentalAccess.Paths)
+ foreach (var path in page.Paths)
{
if (!path.IsFilled && !path.IsStroked) continue; // strokeOrFillPath operator => filter stroke and filled
diff --git a/Tabula/PageIterator.cs b/Tabula/PageIterator.cs
index 9b3c0cd..334cfdb 100644
--- a/Tabula/PageIterator.cs
+++ b/Tabula/PageIterator.cs
@@ -28,20 +28,7 @@ public PageIterator(PdfDocument pdfDocument, IEnumerable pages) : base()
}
///
- public PageArea Current
- {
- get
- {
- try
- {
- return ObjectExtractor.ExtractPage(this.pdfDocument, pageIndexIterator.Current);
- }
- catch (Exception ex)
- {
- throw ex;
- }
- }
- }
+ public PageArea Current => ObjectExtractor.ExtractPage(this.pdfDocument, pageIndexIterator.Current);
///
object IEnumerator.Current => Current;
diff --git a/Tabula/TableRectangle.cs b/Tabula/TableRectangle.cs
index e1e1245..7687277 100644
--- a/Tabula/TableRectangle.cs
+++ b/Tabula/TableRectangle.cs
@@ -11,7 +11,7 @@ namespace Tabula
///
/// A tabula rectangle.
///
- public class TableRectangle : IComparable
+ public class TableRectangle : IComparable, IEquatable
{
///
/// Sort top to bottom (as in reading order).
@@ -212,30 +212,26 @@ public void SetBottom(double bottom)
public override int GetHashCode()
{
- int prime = 31;
- int result = base.GetHashCode();
- // need to implement hash and equal in PdfPig's PdfRectangle
- result = prime * result + BoundingBox.BottomLeft.GetHashCode();
- result = prime * result + BoundingBox.TopLeft.GetHashCode();
- result = prime * result + BoundingBox.TopRight.GetHashCode();
- result = prime * result + BoundingBox.BottomRight.GetHashCode();
- result = prime * result + BitConverter.ToInt32(BitConverter.GetBytes(BoundingBox.Rotation), 0);
- return result;
+ return BoundingBox.GetHashCode();
}
public override bool Equals(object obj)
{
if (obj is TableRectangle other)
{
- if (!this.BoundingBox.BottomLeft.Equals(other.BoundingBox.BottomLeft)) return false;
- if (!this.BoundingBox.TopLeft.Equals(other.BoundingBox.TopLeft)) return false;
- if (!this.BoundingBox.TopRight.Equals(other.BoundingBox.TopRight)) return false;
- if (!this.BoundingBox.BottomRight.Equals(other.BoundingBox.BottomRight)) return false;
- return true;
+ return Equals(other);
}
+
return false;
}
+ public bool Equals(TableRectangle other)
+ {
+ if (other is null) return false;
+ if (ReferenceEquals(this, other)) return true;
+ return BoundingBox.Equals(other.BoundingBox);
+ }
+
public override string ToString()
{
StringBuilder sb = new StringBuilder();
diff --git a/Tabula/Tabula.csproj b/Tabula/Tabula.csproj
index fa30e1e..6a3490e 100644
--- a/Tabula/Tabula.csproj
+++ b/Tabula/Tabula.csproj
@@ -1,31 +1,31 @@
-
- netstandard2.0;net462;net471;net6.0;net8.0
- Extract tables from PDF files (port of tabula-java using PdfPig).
- https://github.com/BobLd/tabula-sharp
- 0.1.5
- BobLd
- BobLd
- pdf, extract, table, tabula, pdfpig, parse, extraction, export
- MIT
- https://github.com/BobLd/tabula-sharp
- README.md
- True
- True
- True
- Tabula.snk
-
+
+ netstandard2.0;netstandard2.1;net462;net471;net6.0;net8.0;net9.0
+ Extract tables from PDF files (port of tabula-java using PdfPig).
+ https://github.com/BobLd/tabula-sharp
+ 1.0.0
+ BobLd
+ BobLd
+ pdf, extract, table, tabula, pdfpig, parse, extraction, export
+ MIT
+ https://github.com/BobLd/tabula-sharp
+ README.md
+ True
+ True
+ True
+ Tabula.snk
+
-
-
- True
- \
-
-
+
+
+ True
+ \
+
+
-
-
-
+
+
+
-
+
\ No newline at end of file
diff --git a/Tabula/TextElement.cs b/Tabula/TextElement.cs
index ca4c806..833b9ec 100644
--- a/Tabula/TextElement.cs
+++ b/Tabula/TextElement.cs
@@ -39,6 +39,11 @@ public TextElement(PdfRectangle pdfRectangle, FontDetails font, double fontSize,
}
public string GetText() => text;
+ ///
+ /// Gets the PdfPig letter associated.
+ ///
+ public Letter Letter => letter;
+
///
/// The direction of the text (0, 90, 180, or 270). Can be any number with PdfPig.
///
diff --git a/Tabula/TextStripper.cs b/Tabula/TextStripper.cs
index b449b0d..1a528aa 100644
--- a/Tabula/TextStripper.cs
+++ b/Tabula/TextStripper.cs
@@ -14,6 +14,8 @@ public sealed class TextStripper
{
private const string NBSP = "\u00A0";
private static float AVG_HEIGHT_MULT_THRESHOLD = 6.0f;
+ private static float MAX_BLANK_FONT_SIZE = 40.0f;
+ private static float MIN_BLANK_FONT_SIZE = 2.0f;
///
/// Process the page.
@@ -42,7 +44,7 @@ public static TextStripperResult Process(Page page)
double wos = GetExpectedWhitespaceSize(letter);
- TextElement te = new TextElement(GetBbox(letter), letter.Font, letter.PointSize, c, wos, letter.GlyphRectangle.Rotation)
+ TextElement te = new TextElement(GetBbox(letter), letter.FontDetails, letter.PointSize, c, wos, letter.BoundingBox.Rotation)
{
letter = letter
};
@@ -54,9 +56,17 @@ public static TextStripperResult Process(Page page)
totalHeight += Math.Max(te.Height, 1); // added by bobld: min height value to 1
double avgHeight = totalHeight / countHeight;
- if (avgHeight > 0 && te.Height >= (avgHeight * AVG_HEIGHT_MULT_THRESHOLD) && (te.GetText()?.Trim().Equals("") != false))
+ if (te.GetText()?.Trim().Equals("") != false)
{
- continue;
+ if (avgHeight > 0 && te.Height >= (avgHeight * AVG_HEIGHT_MULT_THRESHOLD))
+ {
+ continue;
+ }
+
+ if (letter.PointSize > MAX_BLANK_FONT_SIZE || letter.PointSize < MIN_BLANK_FONT_SIZE)
+ {
+ continue;
+ }
}
textElements.Add(te);
@@ -80,10 +90,10 @@ private static bool IsPrintable(string s)
private static bool IsSpecial(char c)
{
-#if NETCOREAPP3_1
- return c >= System.Text.Unicode.UnicodeRanges.Specials.FirstCodePoint && c < (System.Text.Unicode.UnicodeRanges.Specials.FirstCodePoint + System.Text.Unicode.UnicodeRanges.Specials.Length);
-#else
+#if NETSTANDARD || NETFRAMEWORK
return c >= '\uFFF0' && c <= '\uFFFF';
+#else
+ return c >= System.Text.Unicode.UnicodeRanges.Specials.FirstCodePoint && c < (System.Text.Unicode.UnicodeRanges.Specials.FirstCodePoint + System.Text.Unicode.UnicodeRanges.Specials.Length);
#endif
}
@@ -102,39 +112,39 @@ private static PdfRectangle GetBbox(Letter letter)
{
case TextOrientation.Horizontal:
double add = 0;
- if (letter.GlyphRectangle.Height == 0)
+ if (letter.BoundingBox.Height == 0)
{
add = 1; // force minimum height to be 1
}
else if (letter.Value == " ")
{
- add = -(letter.GlyphRectangle.Height - 1); // force height of space to be 1
+ add = -(letter.BoundingBox.Height - 1); // force height of space to be 1
}
return new PdfRectangle(Utils.Round(letter.StartBaseLine.X, 2),
Utils.Round(letter.StartBaseLine.Y, 2),
- Utils.Round(letter.StartBaseLine.X + Math.Max(letter.Width, letter.GlyphRectangle.Width), 2),
- Utils.Round(letter.GlyphRectangle.TopLeft.Y + add, 2));
+ Utils.Round(letter.StartBaseLine.X + Math.Max(letter.Width, letter.BoundingBox.Width), 2),
+ Utils.Round(letter.BoundingBox.TopLeft.Y + add, 2));
case TextOrientation.Rotate180:
// need to force min height = 1 and height of space to be 1
return new PdfRectangle(Utils.Round(letter.StartBaseLine.X, 2),
Utils.Round(letter.StartBaseLine.Y, 2),
- Utils.Round(letter.StartBaseLine.X - Math.Max(letter.Width, letter.GlyphRectangle.Width), 2),
- Utils.Round(letter.GlyphRectangle.TopRight.Y, 2));
+ Utils.Round(letter.StartBaseLine.X - Math.Max(letter.Width, letter.BoundingBox.Width), 2),
+ Utils.Round(letter.BoundingBox.TopRight.Y, 2));
case TextOrientation.Rotate90:
// need to force min height = 1 and height of space to be 1
- return new PdfRectangle(new PdfPoint(Utils.Round(letter.StartBaseLine.X + letter.GlyphRectangle.Height, 2), Utils.Round(letter.GlyphRectangle.BottomLeft.Y, 2)),
- new PdfPoint(Utils.Round(letter.StartBaseLine.X + letter.GlyphRectangle.Height, 2), Utils.Round(letter.EndBaseLine.Y, 2)),
- new PdfPoint(Utils.Round(letter.StartBaseLine.X, 2), Utils.Round(letter.GlyphRectangle.BottomLeft.Y, 2)),
+ return new PdfRectangle(new PdfPoint(Utils.Round(letter.StartBaseLine.X + letter.BoundingBox.Height, 2), Utils.Round(letter.BoundingBox.BottomLeft.Y, 2)),
+ new PdfPoint(Utils.Round(letter.StartBaseLine.X + letter.BoundingBox.Height, 2), Utils.Round(letter.EndBaseLine.Y, 2)),
+ new PdfPoint(Utils.Round(letter.StartBaseLine.X, 2), Utils.Round(letter.BoundingBox.BottomLeft.Y, 2)),
new PdfPoint(Utils.Round(letter.StartBaseLine.X, 2), Utils.Round(letter.EndBaseLine.Y, 2)));
case TextOrientation.Rotate270:
// need to force min height = 1 and height of space to be 1
- return new PdfRectangle(new PdfPoint(Utils.Round(letter.StartBaseLine.X - letter.GlyphRectangle.Height, 2), Utils.Round(letter.StartBaseLine.Y, 2)),
- new PdfPoint(Utils.Round(letter.StartBaseLine.X - letter.GlyphRectangle.Height, 2), Utils.Round(letter.GlyphRectangle.BottomRight.Y, 2)),
+ return new PdfRectangle(new PdfPoint(Utils.Round(letter.StartBaseLine.X - letter.BoundingBox.Height, 2), Utils.Round(letter.StartBaseLine.Y, 2)),
+ new PdfPoint(Utils.Round(letter.StartBaseLine.X - letter.BoundingBox.Height, 2), Utils.Round(letter.BoundingBox.BottomRight.Y, 2)),
new PdfPoint(Utils.Round(letter.StartBaseLine.X, 2), Utils.Round(letter.StartBaseLine.Y, 2)),
- new PdfPoint(Utils.Round(letter.StartBaseLine.X, 2), Utils.Round(letter.GlyphRectangle.BottomRight.Y, 2)));
+ new PdfPoint(Utils.Round(letter.StartBaseLine.X, 2), Utils.Round(letter.BoundingBox.BottomRight.Y, 2)));
case TextOrientation.Other:
default:
@@ -151,8 +161,8 @@ private static PdfRectangle GetBoundingBoxOther(Letter letter)
{
letter.StartBaseLine,
letter.EndBaseLine,
- letter.GlyphRectangle.TopLeft,
- letter.GlyphRectangle.TopRight
+ letter.BoundingBox.TopLeft,
+ letter.BoundingBox.TopRight
};
// Candidates bounding boxes
@@ -164,7 +174,7 @@ private static PdfRectangle GetBoundingBoxOther(Letter letter)
// Find the orientation of the OBB, using the baseline angle
// Assumes line order is correct
- var baseLineAngle = Distances.BoundAngle180(Distances.Angle(letter.GlyphRectangle.BottomLeft, letter.GlyphRectangle.BottomRight));
+ var baseLineAngle = Distances.BoundAngle180(Distances.Angle(letter.BoundingBox.BottomLeft, letter.BoundingBox.BottomRight));
double deltaAngle = Math.Abs(Distances.BoundAngle180(obb.Rotation - baseLineAngle));
double deltaAngle1 = Math.Abs(Distances.BoundAngle180(obb1.Rotation - baseLineAngle));