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
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Cayaqui.MPS.Reports" Version="0.6.1" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Cayaqui.MPS.Reports" Version="0.6.1" />
                    
Directory.Packages.props
<PackageReference Include="Cayaqui.MPS.Reports" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add Cayaqui.MPS.Reports --version 0.6.1
                    
#r "nuget: Cayaqui.MPS.Reports, 0.6.1"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package Cayaqui.MPS.Reports@0.6.1
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=Cayaqui.MPS.Reports&version=0.6.1
                    
Install as a Cake Addin
#tool nuget:?package=Cayaqui.MPS.Reports&version=0.6.1
                    
Install as a Cake Tool

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.Storage registrado (para cargar templates + GenerateAndStoreAsync) + licencia Syncfusion válida del consumidor.

Novedades 0.6.0

  • 🆕 ReportTypographyOptions — fuentes opt-in en el render. Nueva propiedad ReportRenderOptions.Typography con 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 con Cayaqui.MPS.Components ≥ 0.29.

    var options = new ReportRenderOptions
    {
        Typography = ReportTypographyOptions.MpsDefault()
    };
    await reportService.GenerateAsync("monthly-status.xlsx", data, options: options);
    

    Si Typography es null (default), los renderers respetan la fuente del template — cero impacto sobre 0.5.x.

    Aplicación por renderer:

    • Excel: walks UsedRange. cell.HasNumber || HasFormula || HasDateTimeEffectiveNumeric; resto → EffectiveTable. Aplicado también en RenderMultiSheetAsync.
    • 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.PdfA2b o PdfA3b (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 ValidateTemplateAsync con extensiones desconocidas — antes throweaba InvalidOperationException violando el contrato "validate, don't throw". Ahora retorna IsValid=false con el error en Errors.
  • 🎨 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 roundtripTitle/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.
  • 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 4 ParseColor duplicados 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.0 integrado — IExtendedPropertyResolver.Format resuelve [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: registrar key y key+"%").
  • 📄 PDF metadata + PDF/A 1b/2b/3b conformance.
  • RequiredFields + RequiredFieldsMode (Throw/Warn/Ignore).
  • 🖼️ Image injection (placeholders {{image:key}} / %image:key%).
  • 🧱 ReportContext fluent 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 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.
  • 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 como key y key% (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 de Header / Footer / Watermarkno 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)
PDF 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. 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.
  • 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.Storage registrado
  • Cayaqui.MPS.Metadata 0.4.0+ (auto-resuelto)
  • Licencia Syncfusion Essential Studio registrada en el consumidor
Product 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. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
0.6.1 34 5/25/2026
0.5.0 242 5/6/2026
0.4.2 94 5/6/2026
0.4.1 203 4/25/2026
0.4.0 214 4/24/2026
0.3.1 104 4/24/2026
0.3.0 109 4/24/2026

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.