![]() |
VOOZH | about |
dotnet add package Cayaqui.MPS.Reports --version 0.5.0
NuGet\Install-Package Cayaqui.MPS.Reports -Version 0.5.0
<PackageReference Include="Cayaqui.MPS.Reports" Version="0.5.0" />
<PackageVersion Include="Cayaqui.MPS.Reports" Version="0.5.0" />Directory.Packages.props
<PackageReference Include="Cayaqui.MPS.Reports" />Project file
paket add Cayaqui.MPS.Reports --version 0.5.0
#r "nuget: Cayaqui.MPS.Reports, 0.5.0"
#:package Cayaqui.MPS.Reports@0.5.0
#addin nuget:?package=Cayaqui.MPS.Reports&version=0.5.0Install as a Cake Addin
#tool nuget:?package=Cayaqui.MPS.Reports&version=0.5.0Install as a Cake Tool
Generación de reportes Excel · Word · PowerPoint · PDF basada en templates OpenXML, para .NET 10. Usa Syncfusion File Formats (XlsIO, DocIO, Presentation) — .xlsx/.docx/.pptx nativos con MailMerge, template markers y placeholder replacement.
Distribución propietaria — requiere contrato comercial con Cayaqui. Ver
LICENSE.txt.
Dependencias:
Cayaqui.MPS.Storageregistrado (para cargar templates + GenerateAndStoreAsync) + licencia Syncfusion válida del consumidor.
{{Field}} en múltiples runs (formato parcial — ej. negrita solo en {{), el reemplazo silenciosamente fallaba. Ahora se consolidan los runs a paragraph-level si ningún run individual hace match.ValidateTemplateAsync con extensiones desconocidas — antes throweaba InvalidOperationException violando el contrato "validate, don't throw". Ahora retorna IsValid=false con el error en Errors.SlideSize.Width/Height — funciona con widescreen, 4:3 (720×540) y custom.Title/Author/Subject/Keywords/Creator se aplican en PdfDocument antes del primer save, no via PdfLoadedDocument post. Ahorra una pasada de IO/parse para PDFs solo-metadata.IReportService.ConvertToPdfAsync(stream, format, options, ct) — permite aplicar metadata/watermark/password/PDF-A en conversión directa de PDF (sin pasar por un template).ColorParser único — DRY de los 4 ParseColor duplicados en Word/Excel/PPT/PDF.PowerPoint_scalar_placeholder_survives_run_fragmentation + Validate_unknown_extension_returns_invalid_not_throws.Cayaqui.MPS.Metadata 0.4.0 integrado — IExtendedPropertyResolver.Format resuelve [Display(Name)] para enums automáticamente en MailMerge / markers / placeholders.SkiaSharp.NativeAssets.Linux (PDF rendering en containers).%var% parser quirk (workaround: registrar key y key+"%").RequiredFields + RequiredFieldsMode (Throw/Warn/Ignore).{{image:key}} / %image:key%).ReportContext fluent builder.dotnet add package Cayaqui.MPS.Reports
dotnet add package Cayaqui.MPS.Storage
Registrar en Program.cs:
builder.Services.AddMpsStorage(opt => opt.UseFileSystem("./templates-root"));
builder.Services.AddMpsReports(opt =>
{
opt.TemplatesContainer = "report-templates";
opt.SyncfusionLicenseKey = "<your-license-key>"; // o registrar en otro punto
});
Cayaqui.MPS.Reports usa placeholders distintos por formato porque cada motor Syncfusion tiene su gramática nativa. Esto es deliberado (no se quiere reescribir lo que ya hace MailMerge en Word). Pero el template author tiene que conocerlas todas.
| Concepto | Word .docx |
Excel .xlsx |
PowerPoint .pptx |
Header/Footer |
|---|---|---|---|---|
| Scalar | «FieldName» (DocIO MailMerge) |
%FieldName% (XlsIO markers) |
{{FieldName}} (handlebars custom) |
{date}, {datetime}, plus user dict |
| Nested | «Customer.Name» |
%Customer.Name% |
{{Customer.Name}} |
n/a |
| Iteración | «TableStart:Items» … «TableEnd:Items» (group) |
%Items.Property% (collection markers) |
{{#foreach Items}} … {{/foreach}} (slide clone) |
n/a |
| Imagen | {{image:key}} (DocIO Find + AppendPicture) |
%image:key% (cell → AddPicture) |
{{image:key}} (textbox → replace shape) |
n/a |
| Header/footer auto | Header/Footer injection sección |
PageSetup.CenterHeader/Footer |
HeadersFooters.Footer slot |
tokens {date} {time} {datetime} |
«» lo entiende DocIO MailMerge nativamente. Los field codes («Total \# "$#,##0.00"») funcionan con untyped data; ojo: en 0.5.x los grupos pasan strings al DataTable, así que el field code Word no aplica sobre data ya pre-formateada por [Currency]/[Date]. Si querés Word field codes, setea ApplyExtendedProperties = false en ReportRenderOptions.%% es el formato nativo de XlsIO Template Markers. El parser en XlsIO 33.2.3 tiene un quirk con el % final — el paquete registra el variable como key y key% (workaround documentado).{{}} es custom (Syncfusion Presentation no tiene marker engine nativo). Se implementó con regex sobre el texto del slide. Desde 0.5.0 maneja runs fragmentados (placeholder partido por formato).{token} single-brace es solo para los strings de Header / Footer / Watermark — no para el cuerpo del template.var ctx = ReportContext.Create()
.With("ProjectCode", "LBX-01")
.With("ControlDate", DateTime.UtcNow)
.With("Customer", new { FullName = "Acme Corp", TaxId = "12345-6" })
.WithCollection("Items", invoice.Lines)
.WithImage("logo", File.ReadAllBytes("logo.png"), "image/png")
.WithImage("signature", signatureBytes);
await using var pdf = await reports.GenerateAsync(
"invoice.docx",
ctx.Build(),
format: ReportFormat.Pdf,
options: ctx.ToOptions(new ReportRenderOptions
{
PdfTitle = "Invoice {invoice.Number}",
PdfAuthor = "Cayaqui",
PdfConformance = PdfConformance.PdfA2b,
RequiredFields = new[] { "ProjectCode", "ControlDate" },
RequiredFieldsMode = RequiredFieldsMode.Throw
}));
Template Word (invoice.docx):
Invoice for {{image:logo}} — issued «ControlDate»
...
Signature: {{image:signature}}
Template Excel (report.xlsx), cell B1: %image:logo%, cell B20: %image:signature%.
Template PowerPoint (cover.pptx), text box con {{image:logo}}.
await reports.GenerateAsync("invoice.docx", data,
options: new ReportRenderOptions
{
Images = new Dictionary<string, ReportImage>
{
["logo"] = new ReportImage { Data = logoBytes, ContentType = "image/png",
WidthPx = 120, HeightPx = 40 },
["signature"] = new ReportImage { Data = sigBytes, ContentType = "image/png" }
}
});
var opts = new ReportRenderOptions
{
PdfTitle = "Monthly Status — April 2026",
PdfAuthor = "Project Controls Team",
PdfSubject = "EVM snapshot for LBX-01",
PdfKeywords = "evm;cpi;spi;monthly;lbx-01",
PdfCreator = "MPS",
PdfConformance = PdfConformance.PdfA2b
};
await using var pdf = await reports.GenerateAsync("monthly-status.docx", data,
format: ReportFormat.Pdf, options: opts);
Si solo querés convertir un PDF arbitrario y aplicarle metadata sin pasar por un template:
// Nuevo en 0.5.0 — overload con ReportRenderOptions
await using var pdf = await reports.ConvertToPdfAsync(
sourceStream, ReportFormat.Word,
new ReportRenderOptions { PdfTitle = "Report", Watermark = "CONFIDENTIAL" });
try
{
await reports.GenerateAsync("invoice.docx", data,
options: new ReportRenderOptions
{
RequiredFields = new[] { "CustomerName", "InvoiceTotal", "DueDate" },
RequiredFieldsMode = RequiredFieldsMode.Throw
});
}
catch (ReportRenderException ex) when (ex.Message.Contains("Required fields"))
{
_log.LogError("Invoice data incomplete: {Message}", ex.Message);
}
| Formato | Implementación |
|---|---|
| Word | DocIO TextWatermark diagonal, semitransparente |
| Excel | Shape de texto centrado por sheet (no rotado — XlsIO 33.x limita ITextBoxShape.Rotation) |
| PowerPoint | Text box rotado -45° por slide, centrado relativo a SlideSize (0.5.0: respeta 4:3 / widescreen / custom) |
| Overlay post-conversion con rotación -45° y opacity 0.35 |
await reports.GenerateAsync("doc.docx", data,
options: new ReportRenderOptions
{
Watermark = "CONFIDENTIAL",
WatermarkColor = "#E5E7EB"
});
.xlsx. Si querés diagonal, exporta a PDF (format: ReportFormat.Pdf) — el post-process aplica la rotación.DataTable, los field codes Word («Total \# "$#,##0.00"») no formatean. Setea ApplyExtendedProperties = false para preservar tipos numéricos en grupos, o usá los atributos [Currency] / [Date] del DTO (recomendado).DataBinding.Flatten profundidad fija = 3 niveles. DTOs con nesting más profundo (A → B → C → D) pierden niveles. Aplaná o referenciá explícitamente las hojas que necesites.Cayaqui.MPS.Storage registradoCayaqui.MPS.Metadata 0.4.0+ (auto-resuelto)| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net10.0 net10.0 is compatible. net10.0-android net10.0-android was computed. net10.0-browser net10.0-browser was computed. net10.0-ios net10.0-ios was computed. net10.0-maccatalyst net10.0-maccatalyst was computed. net10.0-macos net10.0-macos was computed. net10.0-tvos net10.0-tvos was computed. net10.0-windows net10.0-windows was computed. |
This package is not used by any NuGet packages.
This package is not used by any popular GitHub repositories.
0.5.0 — Hardening release. Fixes: (a) PowerPoint placeholders fragmentados por formato parcial ahora se consolidan a paragraph-level (antes silenciosamente fallaban); (b) ValidateTemplateAsync nunca throwea por extensiones desconocidas — devuelve IsValid=false con Errors; (c) PowerPoint logos+watermark respetan SlideSize real (antes hardcoded 960x540 widescreen). Performance: PDF metadata se aplica directo en el converter sin doble roundtrip via PdfLoadedDocument. API: nuevo overload IReportService.ConvertToPdfAsync(stream, format, options, ct) — permite metadata/watermark/password en conversión directa. Refactor: ColorParser único elimina 4 ParseColor duplicados. PackageReadme actualizado con matriz canónica de las 4 grammars de template (Word MailMerge / Excel markers / PPT handlebars / HF tokens). +2 regression tests (62 total). Sin breaking changes; el overload viejo de ConvertToPdfAsync sigue funcionando.