Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 2 additions & 5 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ mono_crash.*

# Build results
docs/build/
TestDocuments/_Output/
[Dd]ebug/
[Dd]ebugPublic/
[Rr]elease/
Expand All @@ -35,10 +36,9 @@ bld/

# Test-generated output (PDFs, markdown, etc.)
TestDocuments/MuPDF.NET.Test/_Output/
TestDocuments/PDF4LLM.Test/_Output/
TestDocuments/MuPDF.NET.PDF4LLM.Test/_Output/
TestDocuments/Demo/_Output/


# Visual Studio 2015/2017 cache/options directory
.vs/
# Uncomment if you have tasks that create the project's static files in wwwroot
Expand All @@ -47,9 +47,6 @@ TestDocuments/Demo/_Output/
# Visual Studio 2017 auto generated files
Generated\ Files/

# MuPDF.NativeAssets generation files
MuPDF/packages

# MSTest test Results
[Tt]est[Rr]esult*/
[Bb]uild[Ll]og.*
Expand Down
10 changes: 7 additions & 3 deletions Demo/Demo.csproj
Original file line number Diff line number Diff line change
@@ -1,16 +1,20 @@
<Project Sdk="Microsoft.NET.Sdk">
<!-- Demo: Support/, Samples/&lt;area&gt;/; PDF4LLM fixtures in Samples/Llm/Program.Llm.*.Fixtures.cs. -->
<!-- Demo: MuPDF.NET and MuPDF.NET.PDF4LLM samples under Samples/. -->

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<Platforms>AnyCPU;x64;x86</Platforms>
<NoWarn>$(NoWarn);0618;CA1416</NoWarn>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>annotations</Nullable>
<NoWarn>$(NoWarn);0618;CA1416;NU1603</NoWarn>
</PropertyGroup>

<ItemGroup>
<ProjectReference Include="..\PDF4LLM\PDF4LLM.csproj" />
<ProjectReference Include="..\MuPDF.NET\MuPDF.NET.csproj" />
<ProjectReference Include="..\MuPDF.NET.PDF4LLM\MuPDF.NET.PDF4LLM.csproj" />
</ItemGroup>

<Import Project="..\MuPDF.NET.PDF4LLM\build\MuPDF.NET.PDF4LLM.targets" Condition="Exists('..\MuPDF.NET.PDF4LLM\build\MuPDF.NET.PDF4LLM.targets')" />

</Project>
9 changes: 4 additions & 5 deletions Demo/GlobalUsings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,15 @@
global using System.Threading.Tasks;
global using mupdf;
global using MuPDF.NET;
global using PDF4LLM;
global using static global::PDF4LLM.PdfExtractor;
global using PDF4LLM.Helpers;
global using PDF4LLM.Llama;
global using PDF4LLM.Ocr;
global using SkiaSharp;
global using Box = MuPDF.NET.Box;
global using Encoding = System.Text.Encoding;
global using File = System.IO.File;
global using Font = MuPDF.NET.Font;
global using Morph = MuPDF.NET.Morph;
global using TextWriter = MuPDF.NET.TextWriter;
global using MuPDF.NET.PDF4LLM;
global using MuPDF.NET.PDF4LLM.Helpers;
global using MuPDF.NET.PDF4LLM.Llama;
global using MuPDF.NET.PDF4LLM.Ocr;
global using Utils = MuPDF.NET.Utils;
8 changes: 7 additions & 1 deletion Demo/Program.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
namespace Demo
using System;
using System.IO;
using System.Linq;
using MuPDF.NET;
using mupdf;

namespace Demo
{
/// <summary>
/// Demo entry point. With no arguments, every sample in <see cref="SampleMenu"/> runs (including <c>[diag]</c>).
Expand Down
13 changes: 5 additions & 8 deletions Demo/README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# MuPDF.NET Demo

Runnable samples for **MuPDF.NET** and **PDF4LLM**, grouped by API area under `Samples/`.
Runnable samples for **MuPDF.NET** and **MuPDF.NET.PDF4LLM**, grouped by API area under `Samples/`.

## Run

Expand All @@ -11,7 +11,8 @@ dotnet run # all samples in SampleMenu.cs (default, includes
dotnet run -- user # user-facing samples only (skips [diag])
dotnet run -- diagnostics # [diag] samples only
dotnet run -- help # list all samples
dotnet run -- hello-new-pdf # one sample by name
dotnet run -- hello-new-pdf # one MuPDF.NET sample by name
dotnet run -- rag-markdown # one MuPDF.NET.PDF4LLM sample by name
```

## Layout
Expand All @@ -24,11 +25,7 @@ dotnet run -- hello-new-pdf # one sample by name
| `Samples/PageContent/` | Images, recolor, widgets, OCR |
| `Samples/ImageFilters/` | Skia image filters |
| `Samples/Barcodes/` | Barcode read/write |
| `Samples/Llm/` | PDF4LLM markdown, tables, AI connector |
| `Samples/Regression/` | Issue repros (diagnostics only) |
| `Samples/Llm/` | MuPDF.NET.PDF4LLM: Markdown, tables, OCR, RAG |

Input PDFs and images live in `TestDocuments/Demo/`. Generated PDFs are written to `TestDocuments/Demo/_Output/` (gitignored).

## PDF4LLM AI sample

The `ai-connector` sample uses `PdfExtractor.LoadAiAsync`. Set `AZURE_OPENAI_ENDPOINT` and related variables for Azure OpenAI, or run without them to use the in-memory demo pipeline.
Input PDFs and images live in `TestDocuments/Demo/` (and `TestDocuments/Demo/Llm/`). Generated PDFs are written to `TestDocuments/Demo/_Output/` (gitignored).
36 changes: 18 additions & 18 deletions Demo/SampleMenu.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
namespace Demo
{
/// <summary>
/// Demo samples grouped by MuPDF.NET / PDF4LLM feature areas.
/// Demo samples grouped by MuPDF.NET feature areas.
/// Default run executes every entry in <see cref="Samples"/> (including <c>[diag]</c> samples).
/// Use <c>user</c> to skip diagnostics, or <c>diagnostics</c> to run only <c>[diag]</c> samples.
/// </summary>
Expand Down Expand Up @@ -61,30 +61,30 @@ private sealed record Sample(
new("Barcodes", "write-barcode", "Write many barcode types to PDF and PNG", a => Program.TestWriteBarcode(a)),
new("Barcodes", "write-barcode1", "Write CODE39/CODE128/DM with Units rects", _ => Program.TestWriteBarcode1()),

// —— PDF4LLM —— Samples/Llm
new("PDF4LLM", "rag-markdown", "PDF to Markdown (Magazine.pdf)", _ => Program.TestMuPdfRagToMarkdown()),
new("PDF4LLM", "table", "Detect tables and export markdown", _ => Program.TestTable()),
new("PDF4LLM", "table-extract-1", "Dump detected tables by page to console", _ => Program.TestTableExtract1()),
new("PDF4LLM", "table-extract-2", "Export detected tables to tables.csv", _ => Program.TestTableExtract2()),
new("PDF4LLM", "table-extract-3", "Merge continued table pages by column count", _ => Program.TestTableExtract3()),
new("PDF4LLM", "table-ocr", "Extract OCR text from Ocr.pdf", _ => Program.TestOcr()),
new("PDF4LLM", "llm-reader-save-pages", "Load markdown chunks and save per-page .md files", _ => Program.TestLLM2()),
new("PDF4LLM", "markdown-reader", "LlamaIndex PDFMarkdownReader", _ => Program.TestMarkdownReader()),
new("PDF4LLM", "ai-connector", "LoadAiAsync: Ask / Summarize / Search", a => Program.TestMicrosoftAiConnector(a).GetAwaiter().GetResult()),
new("PDF4LLM", "llm-to-markdown-fixture-370", "[diag] ToMarkdown vs test_370 expected output", a => Program.Test4LlmToMarkdownCompareExpected370(a), Diagnostic: true),
new("PDF4LLM", "llm-to-markdown-ocr-1", "[diag] ToMarkdown OCR fixture (FFFD)", a => Program.Test4LlmToMarkdownOcrFixture1(a), Diagnostic: true),
new("PDF4LLM", "llm-to-markdown-ocr-2", "[diag] ToMarkdown without OCR on FFFD fixture", a => Program.Test4LlmToMarkdownOcrFixture2(a), Diagnostic: true),
new("PDF4LLM", "llm-to-markdown-ocr-3", "[diag] ToMarkdown OCR on/off SVG fixture", a => Program.Test4LlmToMarkdownOcrFixture3(a), Diagnostic: true),
new("PDF4LLM", "llm-pdf-reader-empty", "[diag] PDFMarkdownReader empty page", a => Program.Test4LlmPdfMarkdownReaderEmptyPage(a), Diagnostic: true),
new("PDF4LLM", "llm-pdf-reader-missing-file", "[diag] PDFMarkdownReader missing file", a => Program.Test4LlmPdfMarkdownReaderMissingFile(a), Diagnostic: true),

// —— Regression & diagnostics —— Samples/Regression
new("Regression & diagnostics", "issue-213", "[diag] Drawing paths / line width", _ => Program.TestIssue213(), Diagnostic: true),
new("Regression & diagnostics", "issue-1880", "[diag] Read Data Matrix barcodes", _ => Program.TestIssue1880(), Diagnostic: true),
new("Regression & diagnostics", "issue-234", "[diag] Pixmap scale + insert image", _ => Program.TestIssue234(), Diagnostic: true),
new("Regression & diagnostics", "pixmap-parallel", "[diag] Parallel Pixmap.ToBytes", _ => Program.TestPixmapParallel(), Diagnostic: true),
new("Regression & diagnostics", "gettables-parallel", "[diag] Parallel Utils.GetTables", _ => Program.TestGetTablesParallel(), Diagnostic: true),
new("Regression & diagnostics", "jbig2", "[diag] JBIG2 image recompression", _ => Program.TestRecompressJBIG2(), Diagnostic: true),

// —— MuPDF.NET.PDF4LLM —— Samples/Llm
new("MuPDF.NET.PDF4LLM", "rag-markdown", "PDF to Markdown (Magazine.pdf)", _ => Program.TestMuPdfRagToMarkdown()),
new("MuPDF.NET.PDF4LLM", "table", "Detect tables and export markdown", _ => Program.TestTable()),
new("MuPDF.NET.PDF4LLM", "table-extract-1", "Dump detected tables by page to console", _ => Program.TestTableExtract1()),
new("MuPDF.NET.PDF4LLM", "table-extract-2", "Export detected tables to tables.csv", _ => Program.TestTableExtract2()),
new("MuPDF.NET.PDF4LLM", "table-extract-3", "Merge continued table pages by column count", _ => Program.TestTableExtract3()),
new("MuPDF.NET.PDF4LLM", "table-ocr", "Extract OCR text from Ocr.pdf", _ => Program.TestOcr()),
new("MuPDF.NET.PDF4LLM", "llm-reader-save-pages", "Load markdown chunks and save per-page .md files", _ => Program.TestLLM2()),
new("MuPDF.NET.PDF4LLM", "markdown-reader", "LlamaIndex PDFMarkdownReader", _ => Program.TestMarkdownReader()),
new("MuPDF.NET.PDF4LLM", "ai-connector", "LoadAiAsync: Ask / Summarize / Search", a => Program.TestMicrosoftAiConnector(a).GetAwaiter().GetResult()),
new("MuPDF.NET.PDF4LLM", "llm-to-markdown-fixture-370", "[diag] ToMarkdown vs test_370 expected output", a => Program.Test4LlmToMarkdownCompareExpected370(a), Diagnostic: true),
new("MuPDF.NET.PDF4LLM", "llm-to-markdown-ocr-1", "[diag] ToMarkdown OCR fixture (FFFD)", a => Program.Test4LlmToMarkdownOcrFixture1(a), Diagnostic: true),
new("MuPDF.NET.PDF4LLM", "llm-to-markdown-ocr-2", "[diag] ToMarkdown without OCR on FFFD fixture", a => Program.Test4LlmToMarkdownOcrFixture2(a), Diagnostic: true),
new("MuPDF.NET.PDF4LLM", "llm-to-markdown-ocr-3", "[diag] ToMarkdown OCR on/off SVG fixture", a => Program.Test4LlmToMarkdownOcrFixture3(a), Diagnostic: true),
new("MuPDF.NET.PDF4LLM", "llm-pdf-reader-empty", "[diag] PDFMarkdownReader empty page", a => Program.Test4LlmPdfMarkdownReaderEmptyPage(a), Diagnostic: true),
new("MuPDF.NET.PDF4LLM", "llm-pdf-reader-missing-file", "[diag] PDFMarkdownReader missing file", a => Program.Test4LlmPdfMarkdownReaderMissingFile(a), Diagnostic: true),
};

private static readonly Dictionary<string, Sample> ByName = BuildIndex();
Expand Down
13 changes: 7 additions & 6 deletions Demo/Samples/Llm/Program.Llm.AiConnector.cs
Original file line number Diff line number Diff line change
@@ -1,19 +1,20 @@
using Microsoft.Extensions.AI;
using PDF4LLM.AI;
using PDF4LLM.AI.Models;
using PDF4LLM.AI.Options;
using MuPDF.NET.PDF4LLM;
using MuPDF.NET.PDF4LLM.AI;
using MuPDF.NET.PDF4LLM.AI.Models;
using MuPDF.NET.PDF4LLM.AI.Options;

namespace Demo
{
/// <summary>
/// <see cref="PdfExtractor.LoadAiAsync"/> demo: index two PDFs, then Ask, Summarize, and Search.
/// <see cref="MuPDF.NET.PDF4LLM.MuPDF4LLM.LoadAiAsync"/> demo: index two PDFs, then Ask, Summarize, and Search.
/// Uses Azure OpenAI when <c>AZURE_OPENAI_*</c> env vars are set; otherwise an offline demo client.
/// </summary>
internal partial class Program
{
internal static async Task TestMicrosoftAiConnector(string[] args)
{
Console.WriteLine("\n=== TestMicrosoftAiConnector (PDF4LLM.AI) =======================");
Console.WriteLine("\n=== TestMicrosoftAiConnector (MuPDF.NET.PDF4LLM.AI) =======================");

string capitals = DemoPaths.Input("Llm/national-capitals.pdf");
string nato = DemoPaths.Input("Llm/nato-members.pdf");
Expand All @@ -22,7 +23,7 @@ internal static async Task TestMicrosoftAiConnector(string[] args)
bool azure = !string.IsNullOrWhiteSpace(Environment.GetEnvironmentVariable("AZURE_OPENAI_ENDPOINT"));
Console.WriteLine($"Pipeline: {(azure ? "Azure OpenAI" : "development (in-memory index + demo chat)")}");

AiDocumentCollection aiDocs = await PdfExtractor.LoadAiAsync(
AiDocumentCollection aiDocs = await MuPDF4LLM.LoadAiAsync(
new[] { capitals, nato },
options);

Expand Down
2 changes: 1 addition & 1 deletion Demo/Samples/Llm/Program.Llm.PdfMarkdownReader.Fixtures.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
namespace Demo
{
/// <summary>
/// <see cref="PDFMarkdownReader"/> demos aligned with PDF4LLM / repository reader tests.
/// <see cref="PDFMarkdownReader"/> demos aligned with MuPDF.NET.PDF4LLM / repository reader tests.
/// </summary>
internal partial class Program
{
Expand Down
30 changes: 16 additions & 14 deletions Demo/Samples/Llm/Program.Llm.TableExtract.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ internal partial class Program

internal static void TestTableExtract1()
{
PdfExtractor.UseLayout = true;
JArray pages = GetPagesFromJson(PdfExtractor.ToJson(NationalCapitalsPdf));
MuPDF4LLM.UseLayout = true;
JArray pages = GetPagesFromJson(MuPDF4LLM.ToJson(NationalCapitalsPdf));

foreach (JObject page in pages)
{
Expand All @@ -35,8 +35,8 @@ internal static void TestTableExtract1()

internal static void TestTableExtract2()
{
PdfExtractor.UseLayout = true;
JArray pages = GetPagesFromJson(PdfExtractor.ToJson(NationalCapitalsPdf));
MuPDF4LLM.UseLayout = true;
JArray pages = GetPagesFromJson(MuPDF4LLM.ToJson(NationalCapitalsPdf));
var csvLines = new List<string>();

foreach (JObject page in pages)
Expand Down Expand Up @@ -64,8 +64,8 @@ internal static void TestTableExtract2()

internal static void TestTableExtract3()
{
PdfExtractor.UseLayout = true;
JArray pages = GetPagesFromJson(PdfExtractor.ToJson(NationalCapitalsPdf));
MuPDF4LLM.UseLayout = true;
JArray pages = GetPagesFromJson(MuPDF4LLM.ToJson(NationalCapitalsPdf));
var mergedRows = new List<List<string>>();
int? prevColCount = null;

Expand Down Expand Up @@ -101,25 +101,27 @@ internal static void TestTableExtract3()

internal static void TestOcr()
{
PdfExtractor.UseLayout = true;
string md = PdfExtractor.ToMarkdown(@"..\..\..\..\TestDocuments\Demo\Ocr.pdf", useOcr: true, writeImages: false, embedImages: false);
MuPDF4LLM.UseLayout = true;
string ocrPdf = DemoPaths.Input("Ocr.pdf");
string md = MuPDF4LLM.ToMarkdown(ocrPdf, useOcr: true, writeImages: false, embedImages: false);
Console.WriteLine(md);
string text = PdfExtractor.ToText(@"..\..\..\..\TestDocuments\Demo\Ocr.pdf", useOcr: true);
string text = MuPDF4LLM.ToText(ocrPdf, useOcr: true);
Console.WriteLine(text);
}

internal static void TestLLM2()
{
PdfExtractor.UseLayout = true;
var reader = PdfExtractor.LlamaMarkdownReader();
var chunks = reader.LoadData(@"..\..\..\..\TestDocuments\Demo\magazine.pdf");
MuPDF4LLM.UseLayout = true;
var reader = MuPDF4LLM.LlamaMarkdownReader();
var chunks = reader.LoadData(DemoPaths.Input("Magazine.pdf"));

Directory.CreateDirectory("Output");
string outDir = DemoPaths.Output("pages");
Directory.CreateDirectory(outDir);
foreach (var chunk in chunks)
{
int pageNum = (int)chunk.ExtraInfo["page"];
Console.WriteLine(pageNum);
string filePath = $"output/page-{pageNum}.md";
string filePath = Path.Combine(outDir, $"page-{pageNum}.md");
File.WriteAllText(filePath, chunk.Text, Encoding.UTF8);
}
}
Expand Down
16 changes: 8 additions & 8 deletions Demo/Samples/Llm/Program.Llm.ToMarkdown.Fixtures.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
namespace Demo
{
/// <summary>
/// PDF4LLM <see cref="PDF4LLM.PdfExtractor.ToMarkdown"/> demos aligned with PDF4LLM test fixtures
/// MuPDF.NET.PDF4LLM <see cref="MuPDF.NET.PDF4LLM.MuPDF4LLM.ToMarkdown"/> demos aligned with MuPDF.NET.PDF4LLM test fixtures
/// (golden markdown, OCR behavior). PDFs live under <c>TestDocuments/Demo/Llm/</c>; samples skip if missing.
/// </summary>
internal partial class Program
Expand All @@ -13,7 +13,7 @@ private static bool LlmOcrEnvironmentLikelyAvailable() =>
internal static void Test4LlmToMarkdownCompareExpected370(string[] args)
{
_ = args;
Console.WriteLine("\n=== Test4LlmToMarkdownCompareExpected370 (PDF4LLM) =======================");
Console.WriteLine("\n=== Test4LlmToMarkdownCompareExpected370 (MuPDF.NET.PDF4LLM) =======================");

string pdfPath = DemoPaths.Input("Llm/test_370.pdf");
string expectedPath = DemoPaths.Input("Llm/test_370_expected.md");
Expand All @@ -27,7 +27,7 @@ internal static void Test4LlmToMarkdownCompareExpected370(string[] args)
Document document = new Document(pdfPath);
try
{
string actual = ToMarkdown(
string actual = MuPDF4LLM.ToMarkdown(
document,
header: false,
footer: false,
Expand Down Expand Up @@ -75,7 +75,7 @@ internal static void Test4LlmToMarkdownOcrFixture1(string[] args)
string md;
try
{
md = ToMarkdown(doc);
md = MuPDF4LLM.ToMarkdown(doc);
}
finally
{
Expand Down Expand Up @@ -104,7 +104,7 @@ internal static void Test4LlmToMarkdownOcrFixture1(string[] args)
}
}

/// <summary><c>ToMarkdown(..., useOcr: false)</c> on FFFD fixture.</summary>
/// <summary><c>MuPDF4LLM.ToMarkdown(..., useOcr: false)</c> on FFFD fixture.</summary>
internal static void Test4LlmToMarkdownOcrFixture2(string[] args)
{
_ = args;
Expand All @@ -121,7 +121,7 @@ internal static void Test4LlmToMarkdownOcrFixture2(string[] args)
string md;
try
{
md = ToMarkdown(doc, useOcr: false);
md = MuPDF4LLM.ToMarkdown(doc, useOcr: false);
}
finally
{
Expand Down Expand Up @@ -151,8 +151,8 @@ internal static void Test4LlmToMarkdownOcrFixture3(string[] args)
string mdNoOcr;
try
{
md = ToMarkdown(doc);
mdNoOcr = ToMarkdown(doc, useOcr: false);
md = MuPDF4LLM.ToMarkdown(doc);
mdNoOcr = MuPDF4LLM.ToMarkdown(doc, useOcr: false);
}
finally
{
Expand Down
Loading