Reportix.Preview
0.1.0-alpha
dotnet add package Reportix.Preview --version 0.1.0-alpha
NuGet\Install-Package Reportix.Preview -Version 0.1.0-alpha
<PackageReference Include="Reportix.Preview" Version="0.1.0-alpha" />
<PackageVersion Include="Reportix.Preview" Version="0.1.0-alpha" />
<PackageReference Include="Reportix.Preview" />
paket add Reportix.Preview --version 0.1.0-alpha
#r "nuget: Reportix.Preview, 0.1.0-alpha"
#:package Reportix.Preview@0.1.0-alpha
#addin nuget:?package=Reportix.Preview&version=0.1.0-alpha&prerelease
#tool nuget:?package=Reportix.Preview&version=0.1.0-alpha&prerelease
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 |
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 |
|---|---|---|
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
docs/GUIDE.md— panduan pemakaian, dari dokumen pertama sampai previewerCLAUDE.md— panduan kerja dan aturan proyekdocs/ARCHITECTURE.md— bagaimana Fase 1–6 disusun, dan mengapadocs/BACKLOG.md— yang diketahui belum dikerjakan, termasuk angka performadocs/THIRD-PARTY.md— dependensi dan lisensinya
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 | Versions 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. |
-
.NETStandard 2.0
- Reportix.Rendering.Skia (>= 0.1.0-alpha)
-
net8.0
- Reportix.Rendering.Skia (>= 0.1.0-alpha)
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 0.1.0-alpha | 0 | 8/27/2026 |