如何将具有表单和模型值(包括图像 URL)的剃刀视图转换为 PDF?
How to convert a razor view with a form & model values (including an image URL) into a PDF?
我有一个简单的 ASP.Net 核心 MVC 应用程序,它是输入的基本形式,上面带有 HTML Canvas(用于签名)。填写表格后,我需要将其转换为 PDF 并将其附加到电子邮件中。我发现 SelectPDF 有一个支持 .Net Core 的免费社区版,我想我会试一试。
我已将我的申请放在一个地方,我可以在其中提交表格并在单独的视图中查看完成的表格(附有图像以表示用户在 canvas 中输入的内容)。电子邮件发送正常,但我终生无法从呈现的视图中生成 PDF。
在我花了几天时间尝试解决之前我不知道的是,这个带有 SelectPdf 的解决方案在新会话中对 URL 执行 GET - 这意味着我需要提供大量请求,因为我的表单有 ~20 个字段,包括超过请求大小限制的转换图像。
我尝试在不使用数据库或服务的情况下执行此操作,但图像证明这是我预期的更具挑战性的工作。
我已经在 SO 和其他站点上看到并尝试了许多建议的解决方案。它们要么已有数年(在某些情况下为十年或更长时间)陈旧和过时,要么试图使问题变得比使用其他几种工具或扩展(其中大部分已付费或已过时)所需的复杂得多。
我有什么办法可以:
- Trim 下图URL 莫名其妙?或者
- 允许GET请求取
这么长的请求?
- 如何将填充了值的呈现视图转换为 HTML 字符串并发送它而不是 URL?
- 使用 Tempdata 在 "Display" 视图中重建我的模型以绕过请求长度限制?
任何关于如何完成我想做的事情的意见或建议都会很棒。
编辑:更多代码和我目前尝试的内容(扩展)
型号:
namespace Website.Models
{
//[Serializable]
public class ComputerRepairModel
{
[Required(AllowEmptyStrings = false)]
[Display(Name="Customer Name")]
public string CustomerName { get; set; }
[Display(Name = "Email")]
public string CustomerEmail { get; set; }
[Display(Name = "Home")]
public string ContactHomeNumber { get; set; }
[Display(Name = "Work")]
public string ContactWorkNumber { get; set; }
[Required(AllowEmptyStrings = false)]
[Display(Name = "Cell")]
public string ContactCellNumber { get; set; }
[Display(Name = "Signed")]
public string Signature { get; set; }
....
}
控制器:
namespace Website.Controllers
{
public class HomeController : Controller
{
[HttpGet]
public IActionResult RepairAgreement()
{
ComputerRepairModel model = new ComputerRepairModel();
return View(model);
}
[HttpPost]
public IActionResult RepairAgreement(ComputerRepairModel Model)
{
if (!ModelState.IsValid)
{
Model.Signature = "";
return View("RepairAgreement", Model);
}
return View(Model);
}
[HttpGet]
public IActionResult DisplayRepairAgreement()
{
//ComputerRepairModel model = (ComputerRepairModel)TempData["model"];
return View();
}
[HttpPost]
public IActionResult SubmitRepairAgreement(ComputerRepairModel Model)
{
if (!ModelState.IsValid)
{
Model.Signature = null;
return View("RepairAgreement", Model);
}
//TempData["model"] = Model;
return RedirectToAction("DisplayRepairAgreement");
}
查看:
@model ComputerRepairModel
@section Scripts{
<script src="https://cdn.jsdelivr.net/npm/signature_pad@2.3.2/dist/signature_pad.min.js"></script>
<script>
$(function () {
var canvas = document.querySelector('#signatureCanvas');
var pad = new SignaturePad(canvas);
});
</script>
<script>
$("#submit").click(function () {
//alert("button"); // Remove this line if it worked
var dataURL = document.getElementById('signatureCanvas').toDataURL();
document.getElementById('signature').value = dataURL;
$("#submitbutton").hide();
});
</script>
}
<head>
</head>
<body>
<h2 style="margin-top:20px;">Computer Repair Form</h2>
<hr />
<form method="post" asp-action="SubmitRepairAgreement">
<div class="form-group">
<div class="form-row">
<div class="form-group col-sm-3">
<label asp-for="CustomerName"></label>
<input type="text" asp-for="CustomerName" class="form-control" />
<span asp-validation-for="CustomerName" class="text-danger"></span>
</div>
<div class="form-group col-sm-3">
<label asp-for="CustomerEmail"></label>
<input type="text" asp-for="CustomerEmail" class="form-control" placeholder="example@domain.com" />
<span asp-validation-for="CustomerEmail" class="text-danger"></span>
</div>
</div>
</div>
<div class="form-group">
<label><b>Contact Number(s)</b></label>
<div class="form-row">
<div class="form-group col-sm-3">
<label asp-for="ContactHomeNumber"></label>
@*<input type="text" asp-for="ContactHomeNumber" class="phone form-control" maxlength="14" />*@
<input id="homePhone" class="form-control" type="text" asp-for="ContactHomeNumber" />
<span asp-validation-for="ContactHomeNumber" class="text-danger"></span>
</div>
<div class="form-group col-sm-3">
<label asp-for="ContactWorkNumber"></label>
<input id="workPhone" class="form-control" type="text" asp-for="ContactWorkNumber" />
<span asp-validation-for="ContactWorkNumber" class="text-danger"></span>
</div>
<div class="form-group col-sm-3">
<label asp-for="ContactCellNumber"></label>
<input id="cellPhone" class="form-control" type="text" asp-for="ContactCellNumber" />
<span asp-validation-for="ContactCellNumber" class="text-danger"></span>
</div>
</div>
</div>
<div class="form-group">
<label><b>Billing Address</b></label>
<div class="form-row">
<div class="form-group col-sm-5">
<label asp-for="BillingStreetAddress"></label>
<input class="form-control" type="text" asp-for="BillingStreetAddress" />
<span asp-validation-for="BillingStreetAddress" class="text-danger"></span>
</div>
<div class="form-group col-sm-2">
<label asp-for="BillingCity"></label>
<input class="form-control" type="text" asp-for="BillingCity" />
<span asp-validation-for="BillingCity" class="text-danger"></span>
</div>
<div class="form-group col-sm-2">
<label asp-for="BillingState"></label>
<input class="form-control" type="text" asp-for="BillingState" />
<span asp-validation-for="BillingState" class="text-danger"></span>
</div>
<div class="form-group col-sm-2">
<label asp-for="BillingZip"></label>
<input class="form-control" type="text" asp-for="BillingZip" />
<span asp-validation-for="BillingZip" class="text-danger"></span>
</div>
</div>
</div>
<div class="form-group">
<label><b>Computer Access</b></label>
<div class="form-row">
<div class="form-group col-sm-3">
<label asp-for="CustomerComputerUsername"></label>
<input class="form-control" type="text" asp-for="CustomerComputerUsername" />
<span asp-validation-for="CustomerComputerUsername" class="text-danger"></span>
</div>
<div class="form-group col-sm-3">
<label asp-for="CustomerComputerPassword"></label>
<input class="form-control" type="text" asp-for="CustomerComputerPassword" />
<span asp-validation-for="CustomerComputerPassword" class="text-danger"></span>
</div>
</div>
</div>
<div class="form-group">
<div class="form-row">
<div class="form-group col-sm-12">
<label asp-for="DescriptionOfProblem"></label>
<textarea class="form-control" asp-for="DescriptionOfProblem"></textarea>
<span asp-validation-for="DescriptionOfProblem" class="text-danger"></span>
</div>
</div>
</div>
<div class="form-group">
<div class="form-row">
<div class="form-group col-sm-12">
<label asp-for="ItemsReceived"></label>
<textarea class="form-control" asp-for="ItemsReceived"></textarea>
<span asp-validation-for="ItemsReceived" class="text-danger"></span>
</div>
</div>
</div>
<hr />
<div class="form-group">
<div class="form-row">
<div class="form-group col-sm-12">
<label asp-for="Comments"></label>
<textarea class="form-control" asp-for="Comments"></textarea>
<span asp-validation-for="Comments" class="text-danger"></span>
</div>
</div>
</div>
<div>
I hereby agree to the above terms and authorize AMTI to perform services/repairs as stated in the service order.<br />
I also agree to the terms and conditions within this Agreement.
</div>
<div class="form-group" style="margin-top:20px;">
<div class="form-row justify-content-between">
<div class="col-sm-6">
<label asp-for="Signature"></label>
@if (String.IsNullOrEmpty(Model.Signature))
{
<input type="hidden" id="signature" asp-for="Signature" />
<canvas width="500" height="100" id="signatureCanvas" style="border:1px solid black"></canvas>
}
else
{
<img src="@Url.Content(Model.Signature)" alt="Image" />
}
</div>
<div class="form-group col-sm-3">
<label asp-for="DateSigned"></label>
<input class="form-control" type="date" asp-for="DateSigned"/>
</div>
</div>
</div>
<div>
<hr />
<center><b>For Office Use Only</b></center>
<div class="form-group">
<div class="form-row">
<div class="form-group col-sm-4">
<label asp-for="ComputerMfg"></label>
<input class="form-control" readonly asp-for="ComputerMfg" />
</div>
<div class="form-group col-sm-4">
<label asp-for="ComputerModel"></label>
<input class="form-control" readonly asp-for="ComputerModel" />
</div>
<div class="form-group col-sm-4">
<label asp-for="ComputerOS"></label>
<input class="form-control" readonly asp-for="ComputerOS" />
</div>
</div>
</div>
</div>
<div id="submitbutton">
<input id="submit" class="form-control button" style="background-color: #4CAF50; color:white;" type="submit"/>
</div>
</form>
</body>
上面显示的基本上是我的模型、控制器和视图的样子。
我的模型和控制器中的注释代码代表了我最近尝试解决 this answer 中的问题。显然,如果我想尝试并使此方法起作用,我还有一些工作要做,因为尽管将我的模型标记为可序列化,但我收到以下错误。
我尝试这样做是因为如果我只是做一个普通的 RedirectToAction("DisplayRepairAgreement", Model);
请求会太长(因为我通过Javascript) 如图所示。
我尝试的另一件事是使用相同的视图并使 POST 操作成为用于发送到 PDF 转换的操作(这就是为什么我在签名输入附近有 if 条件的原因底部),但这只会在我将 URL 传递给该方法时获取 GET
,并且将以 PDF 形式提供表单,但没有填写任何值。
以下是我最近一次尝试之前在控制器中执行的更多操作(如上所示):
[HttpPost]
public IActionResult RepairAgreement(ComputerRepairModel Model)
{
if (!ModelState.IsValid)
{
Model.Signature = "";
return View("RepairAgreement", Model);
}
string url = Url.Action(nameof(DisplayRepairAgreement),
new { Model.CustomerName, Model.CustomerEmail, Model.ContactHomeNumber, Model.ContactWorkNumber, Model.ContactCellNumber,
Model.BillingStreetAddress, Model.BillingCity, Model.BillingState, Model.BillingZip, Model.CustomerComputerUsername, Model.CustomerComputerPassword, Model.DescriptionOfProblem,
Model.ItemsReceived, Model.Comments, Model.Signature, Model.DateSigned});
// instantiate a html to pdf converter object
HtmlToPdf converter = new HtmlToPdf();
// create a new pdf document converting an url
PdfDocument doc = converter.ConvertUrl(url);
// save pdf document
doc.Save("Sample.pdf");
// close pdf document
doc.Close();
return View(Model);
}
无奈之下,我还尝试直接在我的模型中硬编码 HTML 我的视图,因为 SelectPDF 对象的方法之一可以接受 HTML 字符串而不是 URL.我填写了表格并被带到了显示视图,在那里我使用检查器只抓取 HTML 的整个 blob 并将其粘贴进去。它几乎成功了。本质上,在我的操作中,我只是调用以下方法,并且传入的 Html 存储在模型中,如本段前面所述。
public PdfDocument CreatePdfFromHTML(string Html)
{
HtmlToPdf converter = new HtmlToPdf();
PdfDocument pdfDoc = converter.ConvertHtmlString(Html);
return pdfDoc;
}
这是表单在浏览器中的样子,我也希望 PDF 看起来像这样
这是我尝试使用 stringbuilder 方法并根据 Chrome.
中的检查器编写自己的 HTML 字符串时的样子
好吧,现在更有意义了。似乎您没有正确呈现您的视图。我之前曾尝试过类似的方法,您可以使用此方法将视图呈现为字符串:
public class MyController : Controller
{
private readonly ICompositeViewEngine _viewEngine;
public MyController(ICompositeViewEngine viewEngine)
{
_viewEngine = viewEngine;
}
[HttpPost]
public async Task<IActionResult> RepairAgreement(ComputerRepairModel Model)
{
if (!ModelState.IsValid)
{
Model.Signature = "";
return View("RepairAgreement", Model);
}
string url = await RenderPartialViewToString("DisplayRepairAgreement", new { Model.CustomerName, Model.CustomerEmail, Model.ContactHomeNumber, Model.ContactWorkNumber, Model.ContactCellNumber,
Model.BillingStreetAddress, Model.BillingCity, Model.BillingState, Model.BillingZip, Model.CustomerComputerUsername, Model.CustomerComputerPassword, Model.DescriptionOfProblem,
Model.ItemsReceived, Model.Comments, Model.Signature, Model.DateSigned});
// instantiate a html to pdf converter object
HtmlToPdf converter = new HtmlToPdf();
// create a new pdf document converting an url
PdfDocument doc = converter.ConvertHtmlString(url);
// save pdf document
doc.Save("Sample.pdf");
// close pdf document
doc.Close();
return View(Model);
}
[HttpPost]
public IActionResult DisplayRepairAgreement()
{
return Ok();
}
private async Task<string> RenderPartialViewToString(string viewName, object model)
{
if (string.IsNullOrEmpty(viewName))
viewName = ControllerContext.ActionDescriptor.ActionName;
ViewData.Model = model;
using (var writer = new StringWriter())
{
ViewEngineResult viewResult =
_viewEngine.FindView(ControllerContext, viewName, false);
ViewContext viewContext = new ViewContext(
ControllerContext,
viewResult.View,
ViewData,
TempData,
writer,
new HtmlHelperOptions()
);
await viewResult.View.RenderAsync(viewContext);
return writer.GetStringBuilder().ToString();
}
}
}
给你。我不得不填补一些空白,但我希望它有意义。我添加了一些能够呈现 Razor 视图的代码。这样,它应该使用 Razor 引擎以与浏览器完全相同的方式呈现它。上述的另一个好处是您没有发出任何其他 http 请求。您只是直接使用渲染引擎并在控制器中生成所需的 http 页面。
代码取自这个答案
我自己从未尝试过 SelectPdf,但如果它仍然没有任何样式,您可能需要研究某种渲染引擎,例如 Chromium。我希望这能让你更接近实现你想要的目标。
我有一个简单的 ASP.Net 核心 MVC 应用程序,它是输入的基本形式,上面带有 HTML Canvas(用于签名)。填写表格后,我需要将其转换为 PDF 并将其附加到电子邮件中。我发现 SelectPDF 有一个支持 .Net Core 的免费社区版,我想我会试一试。
我已将我的申请放在一个地方,我可以在其中提交表格并在单独的视图中查看完成的表格(附有图像以表示用户在 canvas 中输入的内容)。电子邮件发送正常,但我终生无法从呈现的视图中生成 PDF。
在我花了几天时间尝试解决之前我不知道的是,这个带有 SelectPdf 的解决方案在新会话中对 URL 执行 GET - 这意味着我需要提供大量请求,因为我的表单有 ~20 个字段,包括超过请求大小限制的转换图像。
我尝试在不使用数据库或服务的情况下执行此操作,但图像证明这是我预期的更具挑战性的工作。
我已经在 SO 和其他站点上看到并尝试了许多建议的解决方案。它们要么已有数年(在某些情况下为十年或更长时间)陈旧和过时,要么试图使问题变得比使用其他几种工具或扩展(其中大部分已付费或已过时)所需的复杂得多。
我有什么办法可以:
- Trim 下图URL 莫名其妙?或者
- 允许GET请求取 这么长的请求?
- 如何将填充了值的呈现视图转换为 HTML 字符串并发送它而不是 URL?
- 使用 Tempdata 在 "Display" 视图中重建我的模型以绕过请求长度限制?
任何关于如何完成我想做的事情的意见或建议都会很棒。
编辑:更多代码和我目前尝试的内容(扩展)
型号:
namespace Website.Models
{
//[Serializable]
public class ComputerRepairModel
{
[Required(AllowEmptyStrings = false)]
[Display(Name="Customer Name")]
public string CustomerName { get; set; }
[Display(Name = "Email")]
public string CustomerEmail { get; set; }
[Display(Name = "Home")]
public string ContactHomeNumber { get; set; }
[Display(Name = "Work")]
public string ContactWorkNumber { get; set; }
[Required(AllowEmptyStrings = false)]
[Display(Name = "Cell")]
public string ContactCellNumber { get; set; }
[Display(Name = "Signed")]
public string Signature { get; set; }
....
}
控制器:
namespace Website.Controllers
{
public class HomeController : Controller
{
[HttpGet]
public IActionResult RepairAgreement()
{
ComputerRepairModel model = new ComputerRepairModel();
return View(model);
}
[HttpPost]
public IActionResult RepairAgreement(ComputerRepairModel Model)
{
if (!ModelState.IsValid)
{
Model.Signature = "";
return View("RepairAgreement", Model);
}
return View(Model);
}
[HttpGet]
public IActionResult DisplayRepairAgreement()
{
//ComputerRepairModel model = (ComputerRepairModel)TempData["model"];
return View();
}
[HttpPost]
public IActionResult SubmitRepairAgreement(ComputerRepairModel Model)
{
if (!ModelState.IsValid)
{
Model.Signature = null;
return View("RepairAgreement", Model);
}
//TempData["model"] = Model;
return RedirectToAction("DisplayRepairAgreement");
}
查看:
@model ComputerRepairModel
@section Scripts{
<script src="https://cdn.jsdelivr.net/npm/signature_pad@2.3.2/dist/signature_pad.min.js"></script>
<script>
$(function () {
var canvas = document.querySelector('#signatureCanvas');
var pad = new SignaturePad(canvas);
});
</script>
<script>
$("#submit").click(function () {
//alert("button"); // Remove this line if it worked
var dataURL = document.getElementById('signatureCanvas').toDataURL();
document.getElementById('signature').value = dataURL;
$("#submitbutton").hide();
});
</script>
}
<head>
</head>
<body>
<h2 style="margin-top:20px;">Computer Repair Form</h2>
<hr />
<form method="post" asp-action="SubmitRepairAgreement">
<div class="form-group">
<div class="form-row">
<div class="form-group col-sm-3">
<label asp-for="CustomerName"></label>
<input type="text" asp-for="CustomerName" class="form-control" />
<span asp-validation-for="CustomerName" class="text-danger"></span>
</div>
<div class="form-group col-sm-3">
<label asp-for="CustomerEmail"></label>
<input type="text" asp-for="CustomerEmail" class="form-control" placeholder="example@domain.com" />
<span asp-validation-for="CustomerEmail" class="text-danger"></span>
</div>
</div>
</div>
<div class="form-group">
<label><b>Contact Number(s)</b></label>
<div class="form-row">
<div class="form-group col-sm-3">
<label asp-for="ContactHomeNumber"></label>
@*<input type="text" asp-for="ContactHomeNumber" class="phone form-control" maxlength="14" />*@
<input id="homePhone" class="form-control" type="text" asp-for="ContactHomeNumber" />
<span asp-validation-for="ContactHomeNumber" class="text-danger"></span>
</div>
<div class="form-group col-sm-3">
<label asp-for="ContactWorkNumber"></label>
<input id="workPhone" class="form-control" type="text" asp-for="ContactWorkNumber" />
<span asp-validation-for="ContactWorkNumber" class="text-danger"></span>
</div>
<div class="form-group col-sm-3">
<label asp-for="ContactCellNumber"></label>
<input id="cellPhone" class="form-control" type="text" asp-for="ContactCellNumber" />
<span asp-validation-for="ContactCellNumber" class="text-danger"></span>
</div>
</div>
</div>
<div class="form-group">
<label><b>Billing Address</b></label>
<div class="form-row">
<div class="form-group col-sm-5">
<label asp-for="BillingStreetAddress"></label>
<input class="form-control" type="text" asp-for="BillingStreetAddress" />
<span asp-validation-for="BillingStreetAddress" class="text-danger"></span>
</div>
<div class="form-group col-sm-2">
<label asp-for="BillingCity"></label>
<input class="form-control" type="text" asp-for="BillingCity" />
<span asp-validation-for="BillingCity" class="text-danger"></span>
</div>
<div class="form-group col-sm-2">
<label asp-for="BillingState"></label>
<input class="form-control" type="text" asp-for="BillingState" />
<span asp-validation-for="BillingState" class="text-danger"></span>
</div>
<div class="form-group col-sm-2">
<label asp-for="BillingZip"></label>
<input class="form-control" type="text" asp-for="BillingZip" />
<span asp-validation-for="BillingZip" class="text-danger"></span>
</div>
</div>
</div>
<div class="form-group">
<label><b>Computer Access</b></label>
<div class="form-row">
<div class="form-group col-sm-3">
<label asp-for="CustomerComputerUsername"></label>
<input class="form-control" type="text" asp-for="CustomerComputerUsername" />
<span asp-validation-for="CustomerComputerUsername" class="text-danger"></span>
</div>
<div class="form-group col-sm-3">
<label asp-for="CustomerComputerPassword"></label>
<input class="form-control" type="text" asp-for="CustomerComputerPassword" />
<span asp-validation-for="CustomerComputerPassword" class="text-danger"></span>
</div>
</div>
</div>
<div class="form-group">
<div class="form-row">
<div class="form-group col-sm-12">
<label asp-for="DescriptionOfProblem"></label>
<textarea class="form-control" asp-for="DescriptionOfProblem"></textarea>
<span asp-validation-for="DescriptionOfProblem" class="text-danger"></span>
</div>
</div>
</div>
<div class="form-group">
<div class="form-row">
<div class="form-group col-sm-12">
<label asp-for="ItemsReceived"></label>
<textarea class="form-control" asp-for="ItemsReceived"></textarea>
<span asp-validation-for="ItemsReceived" class="text-danger"></span>
</div>
</div>
</div>
<hr />
<div class="form-group">
<div class="form-row">
<div class="form-group col-sm-12">
<label asp-for="Comments"></label>
<textarea class="form-control" asp-for="Comments"></textarea>
<span asp-validation-for="Comments" class="text-danger"></span>
</div>
</div>
</div>
<div>
I hereby agree to the above terms and authorize AMTI to perform services/repairs as stated in the service order.<br />
I also agree to the terms and conditions within this Agreement.
</div>
<div class="form-group" style="margin-top:20px;">
<div class="form-row justify-content-between">
<div class="col-sm-6">
<label asp-for="Signature"></label>
@if (String.IsNullOrEmpty(Model.Signature))
{
<input type="hidden" id="signature" asp-for="Signature" />
<canvas width="500" height="100" id="signatureCanvas" style="border:1px solid black"></canvas>
}
else
{
<img src="@Url.Content(Model.Signature)" alt="Image" />
}
</div>
<div class="form-group col-sm-3">
<label asp-for="DateSigned"></label>
<input class="form-control" type="date" asp-for="DateSigned"/>
</div>
</div>
</div>
<div>
<hr />
<center><b>For Office Use Only</b></center>
<div class="form-group">
<div class="form-row">
<div class="form-group col-sm-4">
<label asp-for="ComputerMfg"></label>
<input class="form-control" readonly asp-for="ComputerMfg" />
</div>
<div class="form-group col-sm-4">
<label asp-for="ComputerModel"></label>
<input class="form-control" readonly asp-for="ComputerModel" />
</div>
<div class="form-group col-sm-4">
<label asp-for="ComputerOS"></label>
<input class="form-control" readonly asp-for="ComputerOS" />
</div>
</div>
</div>
</div>
<div id="submitbutton">
<input id="submit" class="form-control button" style="background-color: #4CAF50; color:white;" type="submit"/>
</div>
</form>
</body>
上面显示的基本上是我的模型、控制器和视图的样子。
我的模型和控制器中的注释代码代表了我最近尝试解决 this answer 中的问题。显然,如果我想尝试并使此方法起作用,我还有一些工作要做,因为尽管将我的模型标记为可序列化,但我收到以下错误。
我尝试这样做是因为如果我只是做一个普通的 RedirectToAction("DisplayRepairAgreement", Model);
请求会太长(因为我通过Javascript) 如图所示。
我尝试的另一件事是使用相同的视图并使 POST 操作成为用于发送到 PDF 转换的操作(这就是为什么我在签名输入附近有 if 条件的原因底部),但这只会在我将 URL 传递给该方法时获取 GET
,并且将以 PDF 形式提供表单,但没有填写任何值。
以下是我最近一次尝试之前在控制器中执行的更多操作(如上所示):
[HttpPost]
public IActionResult RepairAgreement(ComputerRepairModel Model)
{
if (!ModelState.IsValid)
{
Model.Signature = "";
return View("RepairAgreement", Model);
}
string url = Url.Action(nameof(DisplayRepairAgreement),
new { Model.CustomerName, Model.CustomerEmail, Model.ContactHomeNumber, Model.ContactWorkNumber, Model.ContactCellNumber,
Model.BillingStreetAddress, Model.BillingCity, Model.BillingState, Model.BillingZip, Model.CustomerComputerUsername, Model.CustomerComputerPassword, Model.DescriptionOfProblem,
Model.ItemsReceived, Model.Comments, Model.Signature, Model.DateSigned});
// instantiate a html to pdf converter object
HtmlToPdf converter = new HtmlToPdf();
// create a new pdf document converting an url
PdfDocument doc = converter.ConvertUrl(url);
// save pdf document
doc.Save("Sample.pdf");
// close pdf document
doc.Close();
return View(Model);
}
无奈之下,我还尝试直接在我的模型中硬编码 HTML 我的视图,因为 SelectPDF 对象的方法之一可以接受 HTML 字符串而不是 URL.我填写了表格并被带到了显示视图,在那里我使用检查器只抓取 HTML 的整个 blob 并将其粘贴进去。它几乎成功了。本质上,在我的操作中,我只是调用以下方法,并且传入的 Html 存储在模型中,如本段前面所述。
public PdfDocument CreatePdfFromHTML(string Html)
{
HtmlToPdf converter = new HtmlToPdf();
PdfDocument pdfDoc = converter.ConvertHtmlString(Html);
return pdfDoc;
}
这是表单在浏览器中的样子,我也希望 PDF 看起来像这样
这是我尝试使用 stringbuilder 方法并根据 Chrome.
中的检查器编写自己的 HTML 字符串时的样子好吧,现在更有意义了。似乎您没有正确呈现您的视图。我之前曾尝试过类似的方法,您可以使用此方法将视图呈现为字符串:
public class MyController : Controller
{
private readonly ICompositeViewEngine _viewEngine;
public MyController(ICompositeViewEngine viewEngine)
{
_viewEngine = viewEngine;
}
[HttpPost]
public async Task<IActionResult> RepairAgreement(ComputerRepairModel Model)
{
if (!ModelState.IsValid)
{
Model.Signature = "";
return View("RepairAgreement", Model);
}
string url = await RenderPartialViewToString("DisplayRepairAgreement", new { Model.CustomerName, Model.CustomerEmail, Model.ContactHomeNumber, Model.ContactWorkNumber, Model.ContactCellNumber,
Model.BillingStreetAddress, Model.BillingCity, Model.BillingState, Model.BillingZip, Model.CustomerComputerUsername, Model.CustomerComputerPassword, Model.DescriptionOfProblem,
Model.ItemsReceived, Model.Comments, Model.Signature, Model.DateSigned});
// instantiate a html to pdf converter object
HtmlToPdf converter = new HtmlToPdf();
// create a new pdf document converting an url
PdfDocument doc = converter.ConvertHtmlString(url);
// save pdf document
doc.Save("Sample.pdf");
// close pdf document
doc.Close();
return View(Model);
}
[HttpPost]
public IActionResult DisplayRepairAgreement()
{
return Ok();
}
private async Task<string> RenderPartialViewToString(string viewName, object model)
{
if (string.IsNullOrEmpty(viewName))
viewName = ControllerContext.ActionDescriptor.ActionName;
ViewData.Model = model;
using (var writer = new StringWriter())
{
ViewEngineResult viewResult =
_viewEngine.FindView(ControllerContext, viewName, false);
ViewContext viewContext = new ViewContext(
ControllerContext,
viewResult.View,
ViewData,
TempData,
writer,
new HtmlHelperOptions()
);
await viewResult.View.RenderAsync(viewContext);
return writer.GetStringBuilder().ToString();
}
}
}
给你。我不得不填补一些空白,但我希望它有意义。我添加了一些能够呈现 Razor 视图的代码。这样,它应该使用 Razor 引擎以与浏览器完全相同的方式呈现它。上述的另一个好处是您没有发出任何其他 http 请求。您只是直接使用渲染引擎并在控制器中生成所需的 http 页面。
代码取自这个答案
我自己从未尝试过 SelectPdf,但如果它仍然没有任何样式,您可能需要研究某种渲染引擎,例如 Chromium。我希望这能让你更接近实现你想要的目标。