Skip to content

Commit

Permalink
Merge pull request #92 from ArjanKw/feature/add-image-support
Browse files Browse the repository at this point in the history
Support images, .NET 8.0 and added options
  • Loading branch information
webgio authored Oct 28, 2024
2 parents 8d2b11d + 51c7c71 commit 36b1191
Show file tree
Hide file tree
Showing 21 changed files with 856 additions and 217 deletions.
133 changes: 127 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
# Rotativa.AspNetCore for .Net core 3.1, .Net 5, .Net 6, .Net 7
# Create PDFs and images with .NET

Rotativa for Asp.Net Core, easy Pdf from Razor views for .Net core 3.1, .Net 5, .Net 6, .Net 7.

Docs are in the making. Should work almost exactly as Rotativa https://github.com/webgio/Rotativa
Use Rotativa to transform a Razor view into a PDF or image.
This package is compatible with .NET Core 3.1, .NET 5, .NET 6, .NET 7 and .NET 8.

## Install with nuget.org:

Expand All @@ -12,19 +11,141 @@ https://www.nuget.org/packages/Rotativa.AspNetCore
Please give feedback!

## Needs configuration
Basic configuration done in Program.cs (.net 6 or 7, beta package):
Basic configuration done in Program.cs (.NET 6 up to 8):

```csharp
app.UseRotativa();
```
or, if using .Net Core 3.1 and .Net 5:
or, if using .NET Core 3.1 and .NET 5:

```csharp
app.UseRotativa(env);
```

Make sure you have a folder with the wkhtmltopdf.exe file accessible by the process running the web app. By default it searches in a folder named "Rotativa" in the root of the web app. If you need to change that use the optional parameter to the Setup call `RotativaConfiguration.Setup(env, "path/relative/to/root")`

## Usage

This package should work almost exactly as Rotativa https://github.com/webgio/Rotativa.

Instead of returning a `View()` in your .NET controller, use `new ViewAsPdf()` to return a PDF or use `new ViewAsImage()` to return an image:

```csharp
public class InvoiceController : Controller
{
private readonly ILogger<InvoiceController> _logger;

public InvoiceController(ILogger<InvoiceController> logger)
{
_logger = logger;
}

public IActionResult Index()
{
// Returns the Index view as HTML.
return View();
}

public IActionResult Invoice()
{
// Returns the Invoice view as PDF.
return new ViewAsPdf();
}

public IActionResult InvoiceImage()
{
// Returns the InvoiceImage view as PDF.
return new ViewAsImage();
}
}
```

You can specify the View that should be transformed into a PDF or image:

```csharp
return new ViewAsPdf("NameOfView");
```

Pass ViewData as an optional property:

```csharp
ViewData["Message"] = "Thank you for downloading this PDF.";
return new ViewAsPdf(viewData: ViewData);
```

We support partial views as well:

```csharp
return new ViewAsImage("MyPartialView", isPartialView: true);
```

By default Rotativa injects a base url in the head section of the HTML. This can be disabled:

```csharp
return new ViewAsImage(setBaseUrl: false);
```

The settings can be combined as well:

```csharp
ViewData["Message"] = "Thank you for downloading this PDF.";
return new ViewAsImage("MyPartialView", isPartialView: true, viewData: ViewData, setBaseUrl: false);
```

To change the way the PDF or image is generated, you can pass the settings as parameters:

```csharp
return new ViewAsImage()
{
Format = ImageFormat.png,
Quality = 90
};
```

By default the PDF or image is shown to the user in the browser, like HTML. If you want to force the document to be downloaded use the Content-Disposition property:

```csharp
return new ViewAsPdf()
{
ContentDisposition = ContentDisposition.Attachment,
FileName = "MyDocument.pdf"
};
```

Each property is documented in the object for easy reference.

Rotativa uses wkhtmltopdf/wkhtmltoimage behind the scenes. If you want to specify custom switches that are unsupported by Rotativa, you can pass them as well:

```csharp
return new ViewAsPdf()
{
CustomSwitches = "--disable-smart-shrinking"
};
```

If you need to write the PDF to the server, you can call `BuildFile` and use the resulting byte array to save the file:

```csharp
var pdfFile = new ViewAsPdf().BuildFile(this.ControllerContext);
File.WriteAllBytes("wwwroot/output.pdf", pdfFile);
```

This is how you save the PDF file to the server before displaying it in the browser:

```csharp
public IActionResult Invoice()
{
// Generate the PDF.
var pdfFile = new ViewAsPdf();

// Save to the server.
File.WriteAllBytes("wwwroot/output.pdf", pdfFile.BuildFile(this.ControllerContext));

// Show in the browser.
return pdfFile;
}
```

## Issues and Pull Request
Contribution is welcomed. If you would like to provide a PR please add some testing.

Expand Down
22 changes: 22 additions & 0 deletions Rotativa.AspNetCore.DemoApp/Controllers/HomeController.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using Microsoft.AspNetCore.Mvc;
using Rotativa.AspNetCore.DemoApp.Models;
using System.Diagnostics;
using Rotativa.AspNetCore.Options;

namespace Rotativa.AspNetCore.DemoApp.Controllers
{
Expand Down Expand Up @@ -29,6 +30,27 @@ public IActionResult Privacy()
return new ViewAsPdf();
}

public IActionResult ContactImage()
{
ViewData["Message"] = "Your contact page image.";

// Example on how to set custom data.
// For demo purposes we changed the name of the view, and specified that it isn't a partial view.
// IsPartialView is false by default. We add some additional ViewData.
// Using custom options 'Format' and 'Quality' as a demo.
// See AsImageResultBase for more options.
return new ViewAsImage("ContactDemo", isPartialView: false, viewData: ViewData, setBaseUrl: true)
{
Format = ImageFormat.png,
Quality = 90
};
}

public IActionResult PrivacyImage()
{
return new ViewAsImage();
}

[ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
public IActionResult Error()
{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk.Web">

<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<TargetFramework>net8.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>
Expand Down
1 change: 1 addition & 0 deletions Rotativa.AspNetCore.DemoApp/Views/Home/Contact.cshtml
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,5 @@
<address>
<strong>Support:</strong> <a href="mailto:[email protected]">Support@example.com</a><br />
<strong>Marketing:</strong> <a href="mailto:[email protected]">Marketing@example.com</a>
<strong>Special character test:</strong> àéù
</address>
17 changes: 17 additions & 0 deletions Rotativa.AspNetCore.DemoApp/Views/Home/ContactDemo.cshtml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
@{
ViewData["Title"] = "Contact";
}
<h2>@ViewData["Title"]</h2>
<h3>@ViewData["Message"] </h3>

<address>
One Microsoft Way<br />
Redmond, WA 98052-6399<br />
<abbr title="Phone">P:</abbr>
425.555.0100
</address>

<address>
<strong>Support:</strong> <a href="mailto:[email protected]">Support@example.com</a><br />
<strong>Marketing:</strong> <a href="mailto:[email protected]">Marketing@example.com</a>
</address>
6 changes: 6 additions & 0 deletions Rotativa.AspNetCore.DemoApp/Views/Home/PrivacyImage.cshtml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
@{
ViewData["Title"] = "Privacy Policy";
}
<h1>@ViewData["Title"]</h1>

<p>Use this page to detail your site's privacy policy.</p>
6 changes: 6 additions & 0 deletions Rotativa.AspNetCore.DemoApp/Views/Shared/_Layout.cshtml
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,15 @@
<li class="nav-item">
<a class="nav-link text-dark" asp-area="" asp-controller="Home" asp-action="Contact">Contact</a>
</li>
<li class="nav-item">
<a class="nav-link text-dark" asp-area="" asp-controller="Home" asp-action="ContactImage">Contact Image</a>
</li>
<li class="nav-item">
<a class="nav-link text-dark" asp-area="" asp-controller="Home" asp-action="Privacy">Privacy</a>
</li>
<li class="nav-item">
<a class="nav-link text-dark" asp-area="" asp-controller="Home" asp-action="PrivacyImage">Privacy Image</a>
</li>
</ul>
</div>
</div>
Expand Down
82 changes: 82 additions & 0 deletions Rotativa.AspNetCore.Tests/OptionFlagTest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading.Tasks;
using Xunit;

namespace Rotativa.AspNetCore.Tests
{

[Trait("Rotativa.AspNetCore", "testing if the option flags are passed to wkhtmltopdf/wkhtmltoimage in the right way.")]
public class OptionFlagTest
{
StringBuilder verificationErrors;

public OptionFlagTest()
{
verificationErrors = new StringBuilder();
}

[Fact(DisplayName = "should not pass options by default.")]
public void NoOptions_ShouldNotPassOptions()
{
var test = new ViewAsPdf();

Assert.Empty(test.GetConvertOptions());
}

[Fact(DisplayName = "zoom option flag is outputted in wkhtml format.")]
public void SingleOption_Zoom_ShouldBeFormatted()
{
var test = new ViewAsPdf()
{
Zoom = 1.5
};

Assert.Equal("--zoom 1.5", test.GetConvertOptions());
}

[Fact(DisplayName = "boolean option flag are outputted in wkhtml format.")]
public void SingleOption_Boolean_ShouldBeFormatted()
{
var test = new ViewAsPdf()
{
NoImages = true
};

Assert.Equal("--no-images", test.GetConvertOptions());
}

[Fact(DisplayName = "multiple option flags should be combined to one option string.")]
public void MultipleOption_Boolean_ShouldBeCombined()
{
var test = new ViewAsPdf()
{
IsLowQuality = true,
NoImages = true
};

Assert.Equal("-l --no-images", test.GetConvertOptions());
}

[Fact(DisplayName = "dictionary options should be repeated for each key")]
public void DictionaryOption_ShouldBeFormatted()
{
var test = new ViewAsPdf()
{
CustomHeaders = new Dictionary<string, string>
{
{ "Header1", "value" },
{ "Header2", "value" },
}
};

Assert.Equal("--custom-header Header1 value --custom-header Header2 value", test.GetConvertOptions());
}
}
}
9 changes: 5 additions & 4 deletions Rotativa.AspNetCore.Tests/PDFParser.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
using iTextSharp.text.pdf;
using iText.Kernel.Pdf;
using System;
using System.Collections.Generic;
using System.IO;
Expand Down Expand Up @@ -43,19 +43,20 @@ public bool ExtractText(string inFileName, string outFileName)
{
// Create a reader for the given PDF file
var reader = new PdfReader(inFileName);
var document = new PdfDocument(reader);
//outFile = File.CreateText(outFileName);
outFile = new StreamWriter(outFileName, false, System.Text.Encoding.UTF8);

Console.Write("Processing: ");

int totalLen = 68;
float charUnit = ((float)totalLen) / (float)reader.NumberOfPages;
float charUnit = ((float)totalLen) / (float)document.GetNumberOfPages();
int totalWritten = 0;
float curUnit = 0;

for (int page = 1; page <= reader.NumberOfPages; page++)
for (int page = 1; page <= document.GetNumberOfPages(); page++)
{
outFile.Write(ExtractTextFromPDFBytes(reader.GetPageContent(page)) + " ");
outFile.Write(ExtractTextFromPDFBytes(document.GetPage(page).GetContentBytes()) + " ");

// Write the progress.
if (charUnit >= 1.0f)
Expand Down
Loading

0 comments on commit 36b1191

Please sign in to comment.