Reportix.Rendering 0.1.0-alpha

This is a prerelease version of Reportix.Rendering.
dotnet add package Reportix.Rendering --version 0.1.0-alpha
                    
NuGet\Install-Package Reportix.Rendering -Version 0.1.0-alpha
                    
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="Reportix.Rendering" Version="0.1.0-alpha" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Reportix.Rendering" Version="0.1.0-alpha" />
                    
Directory.Packages.props
<PackageReference Include="Reportix.Rendering" />
                    
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 Reportix.Rendering --version 0.1.0-alpha
                    
#r "nuget: Reportix.Rendering, 0.1.0-alpha"
                    
#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 Reportix.Rendering@0.1.0-alpha
                    
#: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=Reportix.Rendering&version=0.1.0-alpha&prerelease
                    
Install as a Cake Addin
#tool nuget:?package=Reportix.Rendering&version=0.1.0-alpha&prerelease
                    
Install as a Cake Tool

Reportix

Library .NET untuk menyusun dokumen PDF dari kode C#. Acuannya satu: QuestPDF — fluent API, layout deklaratif, paginasi otomatis, tanpa file template dan tanpa designer.

Status: Fase 6 selesai — roadmap tuntas. Repo ini sempat mengejar parity Crystal Reports sampai Fase 5. Arah itu dibatalkan pada 2026-08-24: band, dataset, formula engine, template JSON, cross-tab, sub-report, barcode, dan bagan sudah dikeluarkan dari solution. Yang tersisa adalah lapis dokumen — dan itulah keseluruhan cakupan proyek sekarang. Fase 2 melengkapi kumpulan elemennya, Fase 3 jalur teksnya, Fase 4 keluaran PDF-nya, dan Fase 5 menambahkan keluaran HTML dan SVG di samping PDF dan PNG, dan Fase 6 menutupnya dengan previewer, diagnostik layout, dan panduan pemakaian. Lihat Roadmap.


Satu lapis API

Dokumen ditulis sebagai kode. Perulangan data adalah foreach biasa milik pemanggil — library tidak mengenal dataset, binding, atau sumber data.

using Reportix;
using Reportix.Layout;
using Reportix.Pdf;
using Reportix.Rendering;
using Reportix.Rendering.Skia;

using var resolver = new SkiaFontResolver();
resolver.RegisterFromFile("fonts/Inter-Regular.ttf");

var settings = new RenderSettings
{
    FontResolver = resolver,
    TextShaper = new HarfBuzzTextShaper(resolver),
};

var document = Document.Create(doc => doc.Page(page =>
{
    page.Size(PageSizes.A4);
    page.Margin(2, Unit.Centimetre);
    page.DefaultTextStyle(TextStyle.Default.WithFontFamily("Inter").WithFontSize(10f));

    page.Header().Text(t => t.FontSize(20f).Bold().Span("Faktur"));

    page.Content().Table(table =>
    {
        table.Columns(columns =>
        {
            columns.RelativeColumn(4f);
            columns.ConstantColumn(90f);
        });

        table.Header(row =>
        {
            row.Cell().Padding(4f).Text(t => t.Bold().Span("Keterangan"));
            row.Cell().Padding(4f).Text(t => t.Bold().AlignRight().Span("Jumlah"));
        });

        foreach (var line in lines)
        {
            table.Row(row =>
            {
                row.Cell().Padding(4f).Text(line.Description);
                row.Cell().Padding(4f).Text(t => t.AlignRight().Span(line.Amount));
            });
        }
    });

    page.Footer().Text(t =>
    {
        t.AlignCenter();
        t.CurrentPageNumber();
        t.Span(" / ");
        t.TotalPages();
    });
}));

using var target = PdfRenderTarget.ToFile("faktur.pdf");
DocumentRenderer.Render(document, target, settings);
dotnet run --project samples/Reportix.Sample.Invoice -- 60 faktur.pdf --png ./preview

Isi Fase 1

Bagian Isi
Elemen Text, Row, Column, Table, Image, Line, Padding, Background, Border, Width/Height, perataan, PageBreak
Layout Kontrak Measure/Draw dua fase, SpacePlan, paginasi, deteksi layout yang tidak akan pernah selesai
Teks Shaping HarfBuzz, fallback font per karakter, pemecahan baris, rantai keluarga font, dasar RTL
Halaman Header dan footer berulang, nomor halaman, total halaman, ukuran halaman per bagian
PDF Penulis sendiri: font Type0/Identity-H tertanam, ToUnicode untuk teks yang bisa disalin, XObject gambar, transparansi, kompresi Flate
PNG PngRenderTarget untuk pratinjau dan visual test

Isi Fase 2

Bagian Isi
Transformasi Rotate bebas untuk watermark, RotateLeft/RotateRight yang ikut diperhitungkan tata letak, Scale
Komposisi Layers dengan satu lapisan penentu ukuran, Grid berkolom seragam dengan span dan pembungkusan baris
Gambar bebas Canvas yang menyerahkan ruang ke pemanggil, Placeholder untuk menahan tempat
Hiasan Sudut membulat pada latar dan garis tepi, Clip mengikuti sudut itu, garis putus-putus dan titik-titik
Tabel Sel bercakupan baris sekaligus kolom; baris yang terikat pindah halaman sebagai satu blok
SVG Pembaca subset sendiri — path, rect, circle, ellipse, line, polyline, polygon, group, transform, gaya. Tanpa dependency baru
page.Content().Column(column =>
{
    column.Item()
        .Background(Colors.Grey100, cornerRadius: 10f)
        .Border(1.5f, Colors.Grey500, cornerRadius: 10f)
        .Padding(10f)
        .Text("Kartu bersudut membulat");

    column.Item().Height(70f).Layers(layers =>
    {
        layers.Layer().AlignMiddle().AlignCenter()
            .Rotate(-20f)
            .Text(t => t.FontSize(22f).FontColor(Colors.Grey300).Span("SALINAN"));

        layers.PrimaryLayer().Padding(10f).Text("Di atas watermark");
    });

    column.Item().Grid(grid =>
    {
        grid.Columns(6);
        grid.Spacing(6f);

        grid.Item(3).Text("separuh");
        grid.Item(3).Text("separuh");
        grid.Item(4).Text("melipat ke baris baru");
    });

    column.Item().Height(90f).Svg(File.ReadAllText("logo.svg"));
});

SVG dibaca menjadi bentuk vektor, bukan dirasterisasi: tepinya tetap tajam pada resolusi cetak berapa pun. Yang didukung adalah subset statis — tanpa teks, gradien, filter, atau use; batasnya tercatat di docs/BACKLOG.md B-019.


Isi Fase 3

Bagian Isi
Pemecahan baris Kesempatan pecah ala UAX #14 yang disederhanakan: spasi, tanda hubung, dan antar-ideograf CJK dengan aturan larangan
Justifikasi Sisa ruang dibagi ke celah antar-kata; baris terakhir dan baris berganti-baris tidak diregangkan
Fitur OpenType liga, kern, tnum, smcp, ss01, dan tag apa pun lainnya, diteruskan ke HarfBuzz
Hyphenation Algoritma pola Liang lengkap dengan pemuat pola dan kata pengecualian
Bidi Isolate LRI/RLI/FSI/PDI dan pasangan kurung (BD16/N0) di atas aturan N1/N2
Subsetting font Penyusut TrueType sendiri: glyf dan loca disusun ulang, termasuk komponen glyph majemuk

Faktur contoh 3 halaman menyusut dari 381 KB menjadi 87 KB — hampir seluruh selisihnya adalah glyph yang tidak pernah dipakai.

page.Content().Text(t =>
{
    t.AlignJustify();
    t.Hyphenate(hyphenator);      // polanya dimuat sendiri, lihat catatan di bawah
    t.Span("Paragraf panjang yang diratakan kiri sekaligus kanan.");
});

page.Content().Text(t =>
{
    t.AlignRight();
    t.TabularNumbers();           // agar kolom angka berjajar lurus
    t.Span("1.234.567");
});

Reportix tidak membawa berkas pola hyphenation. Pola TeX tersebar di berbagai lisensi, sebagian LGPL, yang dilarang aturan proyek ini. Algoritmanya ada dan diuji; polanya Anda muat sendiri:

var hyphenator = Hyphenator.Load(File.ReadAllText("hyph-id.pat"));

Isi Fase 4

Bagian Isi
Tautan /Link ke alamat luar, tautan ke penanda di dalam dokumen, dan penanda tujuan bernama yang bisa dicapai lewat berkas.pdf#nama
Daftar isi Pohon /Outlines bertingkat, disusun dari entri datar beserta tingkatnya
Metadata Kamus /Info beserta paket XMP yang sepakat dengannya, plus /ModDate
PDF/A Profil 2b dan 3b, dengan profil warna sRGB yang dibangun sendiri dari spesifikasi
Tagged PDF Pohon struktur beserta pohon induk; teks, tabel, dan gambar ditandai otomatis, judul dan keterangan pengganti dinyatakan sendiri
Determinisme Dua render dokumen yang sama menghasilkan byte yang sama persis
var options = new PdfDocumentOptions
{
    Deterministic = true,
    Conformance = PdfConformance.PdfA2b,
    Tagged = true,
    Language = "id-ID",
};

using var target = PdfRenderTarget.ToFile("laporan.pdf", options);
DocumentRenderer.Render(document, target, settings);
page.Content().Column(column =>
{
    column.Item().Bookmark("Ringkasan").Text(t =>
    {
        t.Heading(1);              // struktur, bukan tampilan
        t.FontSize(18f).Bold();    // tampilan dinyatakan sendiri
        t.Span("Ringkasan");
    });

    column.Item().Anchor("rincian").Text("Rincian");
    column.Item().LinkTo("rincian").Text("Lompat ke rincian");
    column.Item().Link("https://example.test").Text("Buka situs");

    column.Item().Alt("Grafik penjualan naik sepanjang tahun")
        .Height(80f)
        .Canvas((canvas, size) => { /* ... */ });
});

Profil warna PDF/A dibangun dari angkanya — primer sRGB yang sudah diadaptasi ke D50 dan fungsi transfer sRGB — bukan disalin dari berkas ICC pihak ketiga, supaya tidak ada lisensi yang menempel pada keluaran Anda.

Belum ada: latar bergradien, subsetting font CFF, dan form field. Menyusul di fase berikutnya.


Isi Fase 5

Dokumen yang sama bisa dikeluarkan sebagai PDF, PNG, HTML, atau SVG — tanpa satu baris pun berubah di sisi penyusun dokumennya.

Keluaran Kelas Bentuknya
PDF PdfRenderTarget Satu berkas
PNG PngRenderTarget Satu bitmap per halaman, pada DPI yang dipilih
HTML HtmlRenderTarget Satu berkas berdiri sendiri, satu bagian per halaman
SVG SvgRenderTarget Satu berkas berdiri sendiri per halaman
using var html = HtmlRenderTarget.ToFile("faktur.html");
DocumentRenderer.Render(document, html, settings);

using var svg = new SvgRenderTarget();
DocumentRenderer.Render(document, svg, settings);
svg.SaveTo("./halaman");           // halaman/page-1.svg, page-2.svg, ...

Keduanya menuliskan teks sebagai teks, bukan sebagai kontur: font hasil resolusi disusut lalu ditanam ke dalam berkasnya, sehingga hasilnya bisa diseleksi, dicari mesin pencari, dan dibacakan pembaca layar.

Yang dibawa HTML SVG
Tautan luar dan tautan ke penanda <a href> <a href>
Skema tautan yang ditulis http, https, mailto, tel, ftp, ftps, dan alamat tanpa skema idem
Penanda tujuan id yang bisa dicapai lewat #nama id di dalam berkas halamannya
Daftar isi <nav> bersarang mengikuti tingkat penanda — (dilaporkan lewat Regions)
Penanda struktur <h1><h6>, <p>, <figure>, peran ARIA untuk tabel atribut role dan aria-*
Keterangan pengganti gambar alt aria-label
Ukuran cetak @page beserta pemisah halaman width/height dalam point
Lebar teks dipaku pada hasil layout — (peramban melakukan shaping ulang) textLength

Koordinat HTML dinyatakan dalam piksel CSS, bukan point: clip-path: path() hanya menerima angka tanpa satuan, dan angka itu selalu dibaca sebagai piksel. Karena CSS mendefinisikan 96px = 1in, ukuran cetaknya tetap tepat.

Batasannya dicatat di docs/BACKLOG.md B-026. XPS belum ada — CLAUDE.md §13 menyebutnya "kalau ada permintaan".


Isi Fase 6

Bagian Isi
Previewer Server HTTP loopback yang merasterisasi halaman lewat mesin gambar yang sama dengan PDF
Hot reload Dokumen diminta lewat factory; dotnet watch run menyimpan berkas, peramban ikut berubah
Diagnostik LayoutException menggambar pohon elemen beserta hasil Measure tiap simpul
Penamaan .Named("…") supaya simpul di pohon memakai nama yang Anda tulis
Panduan docs/GUIDE.md — dari dokumen pertama sampai previewer
using var preview = DocumentPreview.Start(
    () => InvoiceDocument.Create(data),      // factory, bukan instance
    settings,
    new PreviewOptions { Title = "Faktur", Dpi = 110f });

Console.WriteLine(preview.Url);              // http://127.0.0.1:5005/
preview.WaitForShutdown();
dotnet watch run --project samples/Reportix.Sample.Preview

Posisi gulir dan tingkat perbesaran bertahan melewati setiap perubahan, karena yang ditukar hanyalah gambar halamannya — bukan halaman previewer-nya. Kegagalan layout tidak mematikan previewer: pesannya ditampilkan di atas halaman terakhir yang berhasil.

Ketika sebuah elemen tidak akan pernah muat, yang Anda dapat bukan satu baris pesan:

Element does not fit on an empty page and would never fit, so pagination cannot make progress.
  Slot:      Content
  Path:      ColumnElement > Container > Blok alamat (PaddingElement)
  Available: 200 x 150 pt

  ColumnElement                       wrap
  ├─ Container                        wrap
  │  └─ Blok alamat (PaddingElement)  wrap    wants 320 x 250 pt unconstrained  <-- does not fit
  │     └─ Text("alamat pelanggan")   full    80 x 10 pt
  └─ Container                        full    50 x 10 pt

Yang dibentangkan hanya simpul yang ikut gagal; saudara yang muat tetap ditampilkan supaya terlihat mana yang berbeda. Elemen yang sudah tergambar habis di halaman sebelumnya tidak ikut dituding — itu jebakan yang mudah terlewat, karena mengukurnya lagi memang menghasilkan Wrap.


Yang sengaja tidak ada

Reportix bukan report engine. Tidak ada band/section laporan, grouping, running total, sub-report, cross-tab, parameter prompt, drill-down, conditional suppression, template JSON, visual designer, formula engine, atau adapter database.

Aturan penyaringnya sederhana: kalau QuestPDF tidak punya, kemungkinan besar kita juga tidak butuh. Rinciannya di CLAUDE.md §1.

Kode lapis laporan yang dulu ada tetap tersimpan di riwayat git — commit terakhir yang memuatnya adalah 0b60cc2.


Struktur

src/Reportix.Core/            model, satuan, gaya, abstraksi ICanvas
src/Reportix.Layout/          pohon elemen, measure/draw, paginasi
src/Reportix.Rendering/       IRenderTarget, DocumentRenderer
src/Reportix.Rendering.Skia/  SkiaCanvas, font, HarfBuzz, keluaran PNG
src/Reportix.Pdf/             penulis PDF
src/Reportix.Export.Html/     keluaran HTML
src/Reportix.Export.Svg/      keluaran SVG
src/Reportix.Preview/         previewer dan hot reload
src/Shared/Export/            kode bersama kedua eksportir, di-link ke keduanya
tests/                        unit, visual (snapshot PNG), benchmark
samples/                      contoh siap jalan
docs/                         arsitektur, backlog, dependensi

Reportix.Core menargetkan netstandard2.0 dan net8.0, sehingga tetap bisa dipakai dari aplikasi .NET Framework 4.8.


Perintah

dotnet build -c Release
dotnet test                                   # 296 test
dotnet pack -c Release                        # 8 paket .nupkg + .snupkg ke ./artifacts
dotnet test tests/Reportix.VisualTests        # snapshot saja
dotnet format
dotnet run -c Release --project tests/Reportix.Benchmarks

# contoh lengkap: PDF, pratinjau PNG, HTML, dan SVG sekaligus
dotnet run --project samples/Reportix.Sample.Invoice -- \
  60 invoice.pdf --png ./preview --html invoice.html --svg ./svg

# previewer dengan hot reload
dotnet watch run --project samples/Reportix.Sample.Preview

Memperbarui snapshot visual — hanya bila perubahan gambarnya memang disengaja, dan wajib dijelaskan di PR:

REPORTIX_UPDATE_SNAPSHOTS=1 dotnet test tests/Reportix.VisualTests

Roadmap

Fase Isi Status
0 Bersih-bersih: keluarkan lapis laporan dari solution Selesai
1 Fondasi: layout, teks, PDF, paginasi Selesai
2 Kelengkapan elemen: Grid, Layers, Canvas, Svg, Rotate/Scale, Clip Selesai
3 Teks tingkat lanjut: OpenType, justifikasi, hyphenation, bidi, subsetting font Selesai
4 PDF tingkat lanjut: bookmark, hyperlink, PDF/A, tagged PDF, output deterministik Selesai
5 Export lain: HTML dan SVG, di samping PDF dan PNG Selesai
6 Tooling: previewer, hot reload, diagnostik layout, panduan Selesai

Dokumentasi


Paket

Delapan paket, dipisah supaya Anda hanya menarik apa yang dipakai. Menambahkan Reportix.Pdf sudah cukup untuk membuat PDF — sisanya ikut sebagai dependensi.

Paket Isinya Tarik ini kalau
Reportix.Core Model, satuan, gaya, ICanvas Anda menulis backend gambar sendiri
Reportix.Layout Pohon elemen, measure/draw, paginasi Anda menulis target keluaran sendiri
Reportix.Rendering IRenderTarget, DocumentRenderer idem
Reportix.Rendering.Skia Canvas Skia, font, HarfBuzz, PNG Anda butuh keluaran PNG saja
Reportix.Pdf Penulis PDF Anda butuh PDF
Reportix.Export.Html Keluaran HTML Anda butuh HTML
Reportix.Export.Svg Keluaran SVG Anda butuh SVG
Reportix.Preview Previewer dan hot reload Anda ingin melihat hasilnya saat menulis

Seluruhnya menargetkan netstandard2.0 dan net8.0, sehingga tetap bisa dipakai dari aplikasi .NET Framework 4.8.


Lisensi

MIT. Reportix ditulis dari nol — tidak ada satu baris pun yang disalin dari QuestPDF maupun library berlisensi restriktif — dan seluruh dependensinya berlisensi permisif; rinciannya di docs/THIRD-PARTY.md.

Profil warna sRGB untuk PDF/A dibangun dari angkanya, bukan disalin dari berkas ICC pihak ketiga, supaya tidak ada lisensi yang menempel pada keluaran Anda.

Product Compatible and additional computed target framework versions.
.NET net5.0 was computed.  net5.0-windows was computed.  net6.0 was computed.  net6.0-android was computed.  net6.0-ios was computed.  net6.0-maccatalyst was computed.  net6.0-macos was computed.  net6.0-tvos was computed.  net6.0-windows was computed.  net7.0 was computed.  net7.0-android was computed.  net7.0-ios was computed.  net7.0-maccatalyst was computed.  net7.0-macos was computed.  net7.0-tvos was computed.  net7.0-windows was computed.  net8.0 is compatible.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 was computed.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 was computed.  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. 
.NET Core netcoreapp2.0 was computed.  netcoreapp2.1 was computed.  netcoreapp2.2 was computed.  netcoreapp3.0 was computed.  netcoreapp3.1 was computed. 
.NET Standard netstandard2.0 is compatible.  netstandard2.1 was computed. 
.NET Framework net461 was computed.  net462 was computed.  net463 was computed.  net47 was computed.  net471 was computed.  net472 was computed.  net48 was computed.  net481 was computed. 
MonoAndroid monoandroid was computed. 
MonoMac monomac was computed. 
MonoTouch monotouch was computed. 
Tizen tizen40 was computed.  tizen60 was computed. 
Xamarin.iOS xamarinios was computed. 
Xamarin.Mac xamarinmac was computed. 
Xamarin.TVOS xamarintvos was computed. 
Xamarin.WatchOS xamarinwatchos was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
0.1.0-alpha 0 8/27/2026