Skip to content
Merged
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
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:Support@example.com">Support@example.com</a><br />
<strong>Marketing:</strong> <a href="mailto:Marketing@example.com">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:Support@example.com">Support@example.com</a><br />
<strong>Marketing:</strong> <a href="mailto:Marketing@example.com">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