Cayaqui.MPS.Reports
0.6.1
dotnet add package Cayaqui.MPS.Reports --version 0.6.1
NuGet\Install-Package Cayaqui.MPS.Reports -Version 0.6.1
<PackageReference Include="Cayaqui.MPS.Reports" Version="0.6.1" />
<PackageVersion Include="Cayaqui.MPS.Reports" Version="0.6.1" />
<PackageReference Include="Cayaqui.MPS.Reports" />
paket add Cayaqui.MPS.Reports --version 0.6.1
#r "nuget: Cayaqui.MPS.Reports, 0.6.1"
#:package Cayaqui.MPS.Reports@0.6.1
#addin nuget:?package=Cayaqui.MPS.Reports&version=0.6.1
#tool nuget:?package=Cayaqui.MPS.Reports&version=0.6.1
Cayaqui.MPS.Reports — 0.6.0
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.
Novedades 0.6.0
🆕
ReportTypographyOptions— fuentes opt-in en el render. Nueva propiedadReportRenderOptions.Typographycon cuatro slots:BodyFontName— cuerpo / labels.TableFontName— celdas/runs en tablas (hereda de Body si null).NumericFontName— valores numéricos (hereda de Table → Body si null).ChartFontName— chart text (reservado; depende del template).
Helper
ReportTypographyOptions.MpsDefault()retorna la convención canónica MPS (Body="Inter", Table/Numeric/Chart="Roboto Condensed") — alineado conCayaqui.MPS.Components ≥ 0.29.var options = new ReportRenderOptions { Typography = ReportTypographyOptions.MpsDefault() }; await reportService.GenerateAsync("monthly-status.xlsx", data, options: options);Si
Typographyesnull(default), los renderers respetan la fuente del template — cero impacto sobre 0.5.x.Aplicación por renderer:
- Excel: walks
UsedRange.cell.HasNumber || HasFormula || HasDateTime→EffectiveNumeric; resto →EffectiveTable. Aplicado también enRenderMultiSheetAsync. - Word: walks runs en body + headers + footers + tablas. Runs cuyo texto matchea regex numérico (
^\(?[+\-]?\$?[\d.,\s]+(\)?\s*(%|k|M|kUSD|MUSD|USD|CLP|PEN|EUR|GBP)?\)?$) →EffectiveNumeric. Runs en tablas (no numéricos) →EffectiveTable. Resto →BodyFontName. - PowerPoint: walks shapes (incluye groups + tablas). Mismo patrón que Word.
Caveats:
- Las fuentes custom deben estar disponibles en el sistema que abre el archivo. Para distribuir documentos garantizando rendering consistente, generar PDF con
PdfConformance.PdfA2boPdfA3b(embeben fuentes). - La heurística numérica regexea el texto del run; no captura todos los formatos (ej.
Lunes 5 de mayo). Para esos casos, asegurar la fuente directamente en el template.
Novedades 0.5.0
- 🐛 Fix PowerPoint placeholders fragmentados — cuando Office partía un placeholder
{{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. - 🐛 Fix
ValidateTemplateAsynccon extensiones desconocidas — antes throweabaInvalidOperationExceptionviolando el contrato "validate, don't throw". Ahora retornaIsValid=falsecon el error enErrors. - 🎨 PowerPoint logos + watermark respetan el tamaño real del slide — antes hardcodeado a 960×540 (widescreen). Ahora lee
SlideSize.Width/Height— funciona con widescreen, 4:3 (720×540) y custom. - ⚡ PDF metadata sin doble roundtrip —
Title/Author/Subject/Keywords/Creatorse aplican enPdfDocumentantes del primer save, no viaPdfLoadedDocumentpost. Ahorra una pasada de IO/parse para PDFs solo-metadata. - ✨ Nuevo overload
IReportService.ConvertToPdfAsync(stream, format, options, ct)— permite aplicar metadata/watermark/password/PDF-A en conversión directa de PDF (sin pasar por un template). - ♻️ Refactor
ColorParserúnico — DRY de los 4ParseColorduplicados en Word/Excel/PPT/PDF. - 📚 Documentación de las 4 grammars — matriz explícita de qué placeholder usa cada formato (ver abajo).
- 🧪 +2 regression tests (total 62) —
PowerPoint_scalar_placeholder_survives_run_fragmentation+Validate_unknown_extension_returns_invalid_not_throws.
Novedades 0.4.x (referencia)
Cayaqui.MPS.Metadata 0.4.0integrado —IExtendedPropertyResolver.Formatresuelve[Display(Name)]para enums automáticamente en MailMerge / markers / placeholders.- Linux runtime support via
SkiaSharp.NativeAssets.Linux(PDF rendering en containers).
Novedades 0.3.0 (referencia)
- 🐛 Fix XlsIO
%var%parser quirk (workaround: registrarkeyykey+"%"). - 📄 PDF metadata + PDF/A 1b/2b/3b conformance.
- ✅
RequiredFields+RequiredFieldsMode(Throw/Warn/Ignore). - 🖼️ Image injection (placeholders
{{image:key}}/%image:key%). - 🧱
ReportContextfluent builder. - 💧 Excel + PowerPoint watermark.
Instalación
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
});
Las 4 grammars de template — matriz canónica
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} |
Por qué cuatro
- Word
«»lo entiende DocIO MailMerge nativamente. Los field codes («Total \# "$#,##0.00"») funcionan con untyped data; ojo: en 0.5.x los grupos pasan strings alDataTable, así que el field code Word no aplica sobre data ya pre-formateada por[Currency]/[Date]. Si querés Word field codes, seteaApplyExtendedProperties = falseenReportRenderOptions. - Excel
%%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 comokeyykey%(workaround documentado). - PowerPoint
{{}}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 deHeader/Footer/Watermark— no para el cuerpo del template.
Uso — ReportContext fluent
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
}));
Uso — Image injection
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" }
}
});
Uso — PDF metadata + PDF/A (0.5.0 sin roundtrip)
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" });
Uso — RequiredFields
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);
}
Watermark — implementación por formato
| 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"
});
Limitaciones conocidas
- Excel watermark sin rotación: aparece horizontal en
.xlsx. Si querés diagonal, exporta a PDF (format: ReportFormat.Pdf) — el post-process aplica la rotación. - Word merge groups con field codes numéricos/fecha: como los grupos pasan strings al
DataTable, los field codes Word («Total \# "$#,##0.00"») no formatean. SeteaApplyExtendedProperties = falsepara preservar tipos numéricos en grupos, o usá los atributos[Currency]/[Date]del DTO (recomendado). DataBinding.Flattenprofundidad fija = 3 niveles. DTOs con nesting más profundo (A → B → C → D) pierden niveles. Aplaná o referenciá explícitamente las hojas que necesites.- PowerPoint runs fragmentados: 0.5.0 consolida cross-portion cuando el placeholder está partido — pero pierde el formato de los portions consolidados (queda el del primer portion). Si necesitás formato fino dentro de un mismo run, escribí el placeholder de un tirón sin formatos parciales.
Requisitos
- .NET 10.0 o superior
Cayaqui.MPS.StorageregistradoCayaqui.MPS.Metadata 0.4.0+(auto-resuelto)- Licencia Syncfusion Essential Studio registrada en el consumidor
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net10.0
- Cayaqui.MPS.Metadata (>= 0.17.1)
- Cayaqui.MPS.Storage (>= 0.2.0)
- Microsoft.Extensions.DependencyInjection.Abstractions (>= 10.0.7)
- Microsoft.Extensions.Logging.Abstractions (>= 10.0.7)
- Microsoft.Extensions.Options (>= 10.0.7)
- SkiaSharp.NativeAssets.Linux (>= 3.119.1)
- Syncfusion.DocIO.Net.Core (>= 33.2.3)
- Syncfusion.DocIORenderer.Net.Core (>= 33.2.3)
- Syncfusion.Presentation.Net.Core (>= 33.2.3)
- Syncfusion.PresentationRenderer.Net.Core (>= 33.2.3)
- Syncfusion.XlsIO.Net.Core (>= 33.2.3)
- Syncfusion.XlsIORenderer.Net.Core (>= 33.2.3)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
0.6.0 — Tipografía opt-in. Nuevo `ReportTypographyOptions` (BodyFontName / TableFontName / NumericFontName / ChartFontName) en `ReportRenderOptions.Typography`. Si null, los renderers respetan la fuente del template (backward-compatible). Si seteado, aplica fuentes después del merge: Excel walks UsedRange y aplica NumericFontName a celdas con valor numérico/fórmula/datetime y TableFontName al resto; Word/PowerPoint walk paragraphs/runs y aplican BodyFontName globalmente, TableFontName dentro de tablas, NumericFontName a runs cuyo texto matchea patrón numérico (regex con soporte para %, paréntesis, k/M/USD/CLP/PEN/EUR). Helper `ReportTypographyOptions.MpsDefault()` retorna la convención canónica MPS (Body=Inter, Table/Numeric/Chart=Roboto Condensed) — alineado con Cayaqui.MPS.Components ≥ 0.29. Para fuentes custom, asegurar disponibilidad en el sistema que abre el archivo o embeber con `PdfConformance.PdfA2b/PdfA3b`. v0.5.0 — Hardening: PowerPoint placeholders fragmentados, ValidateTemplateAsync robusto, SlideSize real, PDF metadata pre-save, ConvertToPdfAsync con options. Sin breaking changes — Typography es opt-in.