DdDesignReport
Library reporting · lisensi MIT

Klien Anda mengubah layout faktur sendiri, tanpa tiket ke Anda

Pasang designer laporan drag-and-drop di aplikasi Anda. Admin atau staf klien mengatur sendiri kop faktur, kolom slip gaji, atau tata letak rapor, seperti di Word. Template tersimpan sebagai satu file JSON, dan backend Anda mencetaknya ke PDF, Excel, atau CSV. Kuitansi dengan terbilang, kertas F4, dan nama bulan Indonesia sudah ada.

  • Hasil:
  • PDF
  • HTML
  • XLSX
  • XLS
  • CSV
  • Printer struk
  •  Backend:
  • Go
  • Java
  • Kotlin
  • Node.js
  • PHP
  • Python
  • .NET lewat HTTP
Cara kerjanya

Tiga langkah, tanpa menulis kode tata letak

Yang mendesain tidak perlu bisa memprogram, dan yang memprogram tidak perlu mengatur posisi teks.

  1. Desain

    Buka designer di browser. Sisipkan teks, tabel, gambar, chart, dan barcode, lalu atur dengan ribbon seperti di pengolah kata.

  2. Simpan

    Designer menghasilkan satu template JSON. Tempat data akan muncul ditandai dengan {{nama_field}}.

  3. Cetak

    Backend memanggil satu fungsi: template + data → PDF, HTML, Excel, atau CSV. Hasilnya sama di semua bahasa.

Template siap pakai

Mulai dari contoh, bukan dari halaman kosong

Semua pratinjau di bawah adalah hasil cetak asli library ini, bukan gambar. Buka di designer untuk mengubahnya, atau unduh JSON-nya.

Coba sekarang

Designer, langsung di halaman ini

Klik tombol di halaman kosong (atau tab Insert) untuk menambah elemen. Klik ganda teks untuk mengetik. Mode mudah hanya menampilkan alat yang dibutuhkan, masing-masing dengan namanya. Ini designer yang sama dengan yang akan Anda pasang di aplikasi.

Pratinjau dan ekspor butuh backend; jalankan designer lengkapnya di langkah Mulai.
Demo butuh halaman ini dibuka lewat web server (bukan file://).
Jasa

Mau langsung jadi?

Library-nya gratis dan tetap MIT. Kalau Anda ingin dikerjakan sekalian, pembuat DdDesignReport bisa membantu. Harga di bawah adalah harga awal; angka pastinya setelah melihat kebutuhan Anda.

Mulai

Mulai dalam 5 menit

Dari nol sampai PDF pertama. Yang dibutuhkan hanya satu binary. Memasangnya sebagai fitur di aplikasi Anda, dengan template per pelanggan dan cetak struk: Integrasi dalam 10 menit.

  1. Unduh binary ddreport

    Satu binary ini menjalankan designer sekaligus menjadi mesin cetak untuk Java, Kotlin, dan Node.js. Ambil yang sesuai sistem Anda dari GitHub Release (ddreport-linux-amd64, ddreport-darwin-arm64, ddreport-windows-amd64.exe, ...) dan ganti namanya menjadi ddreport, atau build sendiri dengan Go:

    git clone https://github.com/gvjavx/DdDesignReport
    cd DdDesignReport
    go build -o ddreport ./cmd/ddreport
  2. Jalankan designer

    ./ddreport -serve 127.0.0.1:8080

    Buka http://127.0.0.1:8080 di browser.

  3. Pilih template, lalu ubah

    Di bagian atas pilih Templates… → Faktur. Klik ganda judulnya untuk mengganti teks, atau pakai tab Insert untuk menambah elemen.

  4. Lihat hasilnya

    Buka tab View → Preview. Data contohnya ada di Sample data for preview and export dan bisa Anda ganti. Tombol PDF / HTML / XLSX / XLS / CSV mengunduh hasilnya langsung.

  5. Simpan template

    Klik Save template. Anda mendapat report.json; simpan di project atau database Anda. Atau simpan dengan nama lewat My templates untuk dibuka, diperbarui, diganti nama, atau dihapus lagi nanti. ddreport -serve menyimpannya di folder ddreport-templates (ubah dengan -templates), beserta 20 versi sebelumnya dari setiap template: tombol History membuka versi lama, lalu Update menjadikannya versi sekarang lagi.

  6. Cetak dari backend

    Kirim template dan data, terima dokumennya.

    import ddreport "github.com/gvjavx/DdDesignReport"
    
    template, _ := os.ReadFile("report.json")
    data := map[string]any{"number": "INV-001", "rows": rows}
    
    pdf, err := ddreport.Render(template, data, "pdf")
    import io.ddreport.DdReport;
    
    String template = Files.readString(Path.of("report.json"));
    byte[] pdf = DdReport.render(template, dataJson, "pdf");
    Files.write(Path.of("report.pdf"), pdf);
    import io.ddreport.DdReport
    
    val template = File("report.json").readText()
    val pdf = DdReport.render(template, dataJson, "pdf")
    File("report.pdf").writeBytes(pdf)
    const { renderAsync } = require('ddreport')
    
    const template = JSON.parse(fs.readFileSync('report.json', 'utf8'))
    const pdf = await renderAsync(template, { number: 'INV-001', rows }, 'pdf')
    fs.writeFileSync('report.pdf', pdf)
    curl -X POST "http://127.0.0.1:8080/render?format=pdf" \
      -d '{"template": {...}, "data": {...}}' -o report.pdf

    Ganti "pdf" dengan "html", "xlsx", "xls", atau "csv" untuk format lain. Detail tiap bahasa ada di Backend.

Memakai designer

Designer terdiri dari ribbon di atas, halaman di tengah, panel properti di kanan, dan bilah status di bawah. Di layar sempit panel properti pindah ke bawah halaman dan terlipat, dan halaman otomatis dipaskan ke lebar layar.

Saya ingin…Caranya
Mulai dari halaman kosongKlik Text box, Table, Picture, atau Chart di kotak Start here di tengah halaman.
Menambah elemenTab Insert: klik tombolnya, atau seret tombolnya ke bagian halaman yang dituju.
Tampilan yang lebih sederhanaMode mudah (di aplikasi standalone aktif sejak awal): alat yang jarang dipakai disembunyikan dan setiap tombol diberi nama. Cocok untuk pemula, juga anak-anak.
Mengetik teksKlik ganda teks atau sel tabel. Klik di tempat lain untuk menyimpan, Esc untuk membatalkan. Enter membuat baris baru pada teks dan menyelesaikan pengisian sel.
Menebalkan sebagian teksSaat mengetik, blok kata yang dimaksud lalu tekan B, I, atau U.
Memilih beberapa elemenShift+klik, seret kotak di area kosong, atau Ctrl+A.
MemindahGeser dengan mouse (menempel ke tepi elemen lain; tahan Alt untuk mematikan) atau tombol panah. Geser ke band lain untuk memindahkannya ke sana.
Mengubah ukuranTarik pegangan biru di sudut kanan bawah elemen terpilih (juga dengan sentuhan). Tarik tepi bawah sebuah band untuk mengubah tingginya.
Memperbesar tampilanBilah status di bawah: tombol − dan +, penggeser, klik persentasenya untuk kembali ke 100%, atau tombol paskan lebar.
Menghapus elemenKlik tombol merah × di sudut kanan atas elemen terpilih, seret elemen ke tempat sampah yang muncul di atas halaman saat menggeser, atau tekan Delete. Beberapa elemen terpilih bisa diseret ke tempat sampah sekaligus. Ctrl+Z mengembalikannya.
MeratakanPilih beberapa elemen, lalu tombol di grup Arrange (tab Home).
Mengatur kertasTab Layout: ukuran, orientasi, margin, warna dan gambar latar.
MembatalkanCtrl+Z, sampai 100 langkah. Ctrl+Y untuk mengulang. Memulai laporan baru atau memuat template juga bisa dibatalkan.

Isi ribbon

TabIsi
HomeUndo/redo; cut, copy, paste, duplicate, delete; font, ukuran, bold, italic, underline, warna teks, highlight; rata kiri/tengah/kanan, spasi baris, wrap; perataan dan distribusi elemen, bawa ke depan/belakang.
InsertText box, tabel (baris × kolom), gambar, kotak, garis, chart, barcode, dan daftar field siap sisip.
LayoutKertas (A4, A5, Letter, Legal, F4, custom), orientasi, margin per sisi, latar, pengelompokan, halaman baru per kelompok, pemisah angka.
ViewPenggaris, snap, Preview. Zoom (25–300%) ada di bilah status, dan bisa langsung di kanvas dengan Ctrl + scroll, pinch di trackpad, atau cubit dua jari di layar sentuh.

Pintasan keyboard

TombolAksi
Ctrl+Z · Ctrl+YUndo · redo
Ctrl+C · X · V · DCopy · cut · paste · duplicate
Ctrl+B · I · UBold · italic · underline
Ctrl+A · EscPilih semua · batalkan pilihan
Ctrl+= · Ctrl+- · Ctrl+0Perbesar · perkecil · zoom 100%
Ctrl + scrollZoom di titik kursor
Panah · Shift+panahGeser 1pt · 10pt
DeleteHapus yang dipilih

Satuan di designer adalah point: 1px di layar = 1pt di kertas. A4 berukuran 595 × 842.

Designer menjaga elemen tetap di kertas: elemen tidak bisa keluar dari margin kiri dan kanan, dan band otomatis memanjang jika sebuah elemen melewati tepi bawahnya. Elemen baru tidak ditumpuk persis di atas elemen lain.

Panduan

Band: kerangka laporan

Satu konsep ini menjelaskan hampir semua perilaku laporan. Sisanya hanya soal mengisi.

Halaman laporan dibagi menjadi beberapa band (pita mendatar). Setiap band punya aturan kapan ia dicetak. Anda cukup menaruh elemen di band yang tepat; library yang mengurus pengulangan dan ganti halaman.

  • header · footerDi atas dan di bawah setiap halaman. Untuk logo, judul, nomor halaman.
  • columnHeaderDi bawah header, setiap halaman. Untuk judul kolom tabel, supaya terulang saat tabel berlanjut ke halaman berikutnya.
  • detailDicetak sekali untuk setiap baris data. Seratus baris data berarti seratus kali band ini.
  • groupHeader · groupFooterSebelum dan sesudah tiap kelompok, jika groupBy diisi. Untuk nama kategori dan subtotal.
  • summarySekali saja, setelah baris terakhir. Untuk total, chart, catatan, tanda tangan.
Dokumen tanpa baris data, seperti surat atau sertifikat, cukup memakai header dan summary. Lihat template Surat resmi dan Sertifikat.

Posisi elemen (x, y) dihitung dari sudut kiri atas band-nya. Halaman baru dibuat otomatis saat band berikutnya tidak muat.

Data & placeholder

Data adalah JSON biasa: beberapa field bebas, ditambah daftar baris di bawah kunci rows. Di template, tulis nama field di antara kurung kurawal ganda.

1 · Data

{
  "number": "INV-001",
  "customer": { "name": "Budi" },
  "rows": [
    { "name": "Kopi",
      "qty": 2,
      "price": 18000 }
  ]
}

2 · Teks di template

Faktur {{number}}
Kepada: {{customer.name}}

{{name}}
{{qty}} × {{price:0}}
= {{qty * price:0}}

3 · Hasil

Faktur INV-001
Kepada: Budi

Kopi
2 × 18,000
= 36,000
Tentang contoh di panduan ini. Setiap kotak contoh adalah hasil cetak asli library, dan di bawahnya JSON lengkap yang mencetaknya: template dan data dalam satu file. Buka di designer memuatnya ke designer di atas untuk diubah-ubah. Untuk mencetaknya sendiri, simpan JSON-nya sebagai contoh.json, lalu jalankan ddreport -format pdf < contoh.json > hasil.pdf, atau kirim isinya apa adanya ke POST /render?format=pdf (lihat Backend).
TulisHasil
{{name}}Nilai field name. Di band detail dicari di baris saat ini dulu, lalu di data utama.
{{customer.name}}Field di dalam objek.
{{price:2}}Angka dengan 2 desimal dan pemisah ribuan: 18,000.00. Angka di belakang titik dua = jumlah desimal (0–12).
{{unit-price}} · {{nama barang}}Nama field apa pun boleh dipakai, termasuk yang bertanda hubung atau berspasi, selama placeholder berisi nama itu saja.
{{page}} · {{pages}}Nomor halaman dan jumlah halaman (di header, columnHeader, footer).

Format Indonesia. Isi thousands dengan . dan decimal dengan , (tab Layout → Data) agar {{price:2}} menjadi 18.000,00.

Field yang tidak ada menghasilkan teks kosong. Array baris saja, tanpa objek pembungkus, juga diterima sebagai data. Nama kunci rows bisa diganti lewat dataKey.

Perhitungan & total

Template bisa menghitung sendiri, jadi backend cukup mengirim data mentah.

Saya ingin…Tulis
Jumlah per baris{{qty * price:0}}
Harga setelah diskon{{(price - discount) * qty:0}}
Total semua baris{{sum(qty * price):0}}
Pajak 11% dari total{{sum(qty * price) * 0.11:0}}
Kembalian{{cash - sum(qty * price):0}}
Persentase terhadap total{{qty * price / sum(qty * price) * 100:1}}%
Jumlah baris{{count()}}
Rata-rata, terkecil, terbesar{{avg(price):2}} · {{min(price)}} · {{max(price)}}
Teks sesuai kondisi{{if(paid, 'LUNAS', 'BELUM LUNAS')}} · {{if(stock < minimum, 'Pesan ulang')}}
Huruf besar, pembulatan{{upper(name)}} · {{round(price / 3, 2)}}
Angka dalam kata (terbilang){{terbilang(total)}} → dua juta tujuh ratus lima puluh ribu · {{upper(terbilang(total))}} RUPIAH
Nomor urut baris{{index}} di band detail: 1, 2, 3, …, mulai lagi dari 1 di setiap kelompok
Tanggal{{date:dd MMMM yyyy}} → 19 Oktober 2026 · {{now:dd/MM/yyyy HH:mm}} (waktu cetak)

Nomor urut, subtotal, pajak, total, terbilang, status, dan tanggal berbahasa Indonesia, semuanya dihitung template dari data mentah:

  • Operator: + - * / dan kurung. Perkalian dan pembagian dihitung lebih dulu.
  • Perbandingan = != < > <= >=, digabung dengan and, or, not. Teks ditulis dalam kutip tunggal: status = 'lunas'.
  • Fungsi if(kondisi, jika ya, jika tidak), upper, lower, round(x, desimal), dan terbilang(x) (bahasa Indonesia, desimal dibulatkan; dipakai template Kuitansi).
  • Pola tanggal: yyyy yy MMMM MMM MM M dd d EEEE EEE HH mm ss; teks lain dalam 'kutip'. Nama bulan dan hari berbahasa Indonesia dengan Dates in: Indonesian di tab Layout ("locale": "id"). Tanggal dibaca dari teks ISO (2026-10-19, 2026-10-19T08:30:00Z) atau Unix time.
  • Fungsi sum, avg, min, max, count menghitung atas banyak baris. Isinya boleh berupa perhitungan.
  • count() menghitung semua baris; count(f) hanya baris yang f-nya berupa angka.
  • Di dalam perhitungan, nama field yang bukan satu kata ditulis dalam kurung siku: {{[unit-price] * qty}}, {{sum([harga satuan])}}.
Angka yang dikirim sebagai teks ("18000", seperti dari BigDecimal atau kolom numeric database) juga dihitung. Nilai yang memang bukan angka, pembagian dengan nol, atau hasil yang terlalu besar memberi hasil kosong, bukan angka yang salah. Hasil tanpa :N tidak menampilkan sisa pembulatan: 0.1 + 0.2 menjadi 0.3.

Total yang dipakai di setiap baris, misalnya persentase terhadap total, dihitung sekali saja, jadi laporan besar tetap cepat.

Kelompok & subtotal

Isi Group by (tab Layout) dengan nama field, misalnya category. Baris dengan nilai yang sama dikumpulkan menjadi satu kelompok, urut sesuai kemunculan pertamanya, jadi data tidak perlu diurutkan lebih dulu.

groupBy      category
groupHeader  {{category}} ({{count()}} produk)
detail       {{name}}            {{qty * price:0}}
groupFooter  Subtotal            {{sum(qty * price):0}}
summary      Total               {{sum(qty * price):0}}

Data di contoh ini tidak urut kategori; library mengelompokkannya sendiri:

Rumus yang sama memberi hasil berbeda tergantung band-nya:

  • di groupHeader, detail, groupFooter: dihitung atas kelompok itu,
  • di summary, header, footer: dihitung atas seluruh baris.

Centang New page per group agar setiap kelompok mulai di halaman baru. Judul kelompok tidak pernah tertinggal sendirian di dasar halaman: jika baris pertamanya tidak muat, keduanya pindah bersama. Kelompok yang berlanjut ke halaman berikutnya mendapat judulnya lagi di atas halaman itu.

Sort by mengurutkan baris lebih dulu, misalnya category, -total (tanda - untuk menurun). Band groupHeader dan groupFooter yang berisi elemen tetap dicetak tanpa Group by, sekali di sekitar semua baris.

Sampai enam tingkat

Isi Group by dengan beberapa field, misalnya region, city. Band kepala grup 2 / kaki grup 2 (dan seterusnya sampai 6 untuk field keenam) muncul di designer di dalam band grup pertama. Setiap tingkat punya subtotalnya sendiri, dan judul semua grup yang sedang berjalan diulang di atas halaman baru.

groupBy       region, city
groupHeader   Wilayah {{region}}
groupHeader2  Kota {{city}}
detail        {{name}}        {{amt:0}}
groupFooter2  Subtotal kota   {{sum(amt):0}}
groupFooter   Total wilayah   {{sum(amt):0}}

Centang A sheet per group agar XLSX dan XLS berisi satu sheet untuk setiap grup tingkat pertama, dinamai sesuai grupnya.

Sub-laporan

Untuk daftar di dalam data yang butuh tata letak sendiri, bukan sekadar tabel: tab Insert → Sub-report, isi rows from data dengan nama daftarnya (misalnya lines), lalu Edit sub-report…. Terbuka designer kedua selebar kotak elemen, dengan band header, column header, grup (sampai enam tingkat), detail, dan summary; Sort by dan Group by-nya bisa diisi sendiri. Band footer-nya tidak dipakai.

  • Di band detail laporan induk, setiap baris mendapat sub-laporannya: setiap pesanan dengan rincian per jenisnya, setiap pegawai dengan riwayatnya.
  • Field induk tetap bisa dipakai di dalamnya ({{number}}); {{index}} adalah nomor item; agregat di header dan summary-nya menghitung semua item.
  • Band induk ikut memanjang. Sub-laporan yang lebih panjang dari satu halaman berlanjut tanpa memotong band-nya, dengan header dan column header diulang.
  • Di Excel dan CSV, setiap baris sub-laporan menjadi baris spreadsheet.

Crosstab (tabel silang)

Ringkasan dua arah, misalnya penjualan per wilayah (baris) dan per bulan (kolom): tab Insert → Crosstab, lalu isi rows by, columns by, dan value.

{ "type": "crosstab", "text": "Wilayah",
  "rowField": "region", "columnField": "month",
  "measure": "sum(total):0", "totalLabel": "Jumlah",
  "headerFill": "#1e3a8a", "headerColor": "#ffffff", "footerFill": "#e0e7ff" }
  • measure dihitung dari baris yang punya nilai baris dan kolom sel itu: sum(qty * price):0, avg(price):2, min(date), atau count() (default).
  • Kolom dan baris total serta total keseluruhan ditambahkan otomatis. Urutan baris dan kolom mengikuti data, jadi Sort by mengaturnya.
  • Di band grup, crosstab menghitung baris grup itu; di tempat lain semua baris, atau daftar di data.
  • Hasilnya tabel biasa: garis, warna header dan total, baris berselang-seling, ganti halaman, dan ekspor XLSX/XLS/CSV sama seperti tabel.

Elemen

TextTeks dengan placeholder. Bisa membungkus dan memanjangkan band.
TableKisi sel bergaris. Satu baris di band detail = tabel data.
PictureLogo, tanda tangan, stempel. PNG, JPEG, atau GIF.
RectangleKotak dengan garis dan warna isi. Untuk bingkai dan latar.
LineGaris mendatar, tegak, atau miring (panel kanan: direction).
ChartBatang, batang mendatar, garis, pie, donut.
BarcodeQR code atau Code 128 dari teks atau field, dengan teks di bawahnya bila dicentang.
Sub-reportLaporan kecil untuk setiap item dari daftar di data. Lihat Sub-laporan.
CrosstabTabel silang: baris × kolom dari data, dengan total. Lihat Crosstab.

Teks panjang: centang Wrap

Tanpa Wrap (grow), teks yang melebihi kotaknya terpotong. Dengan Wrap, teks turun ke baris berikutnya dan band ikut bertambah tinggi: elemen di bawahnya turun, dan kotak serta garis tegak di sekelilingnya ikut memanjang. Kata yang lebih lebar dari kotaknya (URL, nomor panjang) dipecah di tengah.

Teks yang lebih panjang dari satu halaman, misalnya isi surat, berlanjut ke halaman berikutnya baris demi baris, tanpa ada yang terpotong. Footer yang memanjang tetap berada di dalam margin bawah.

Tampil jika

Setiap elemen punya kolom show when (showIf) di panel kanan, misalnya paid = 'yes' atau stock < minimum. Jika kondisinya salah, elemen tidak dicetak; di Excel dan CSV selnya kosong. Elemen dengan kondisi diberi garis putus-putus jingga di designer. Kondisi yang salah tulis tetap menampilkan elemennya agar kesalahannya terlihat.

Cap LUNAS (kotak dan teks) dan JATUH TEMPO ada di setiap baris, tapi hanya satu yang tercetak sesuai paid:

Beberapa teks Wrap dalam satu band saling memperhitungkan: teks yang bertumpuk mendorong bergantian (dorongannya dijumlahkan), sedangkan teks yang bersebelahan mengikuti yang paling tinggi.

Gambar

Gambar disimpan di dalam template sebagai data URI (data:image/png;base64,…); designer melakukannya otomatis saat Anda memilih file. Alamat gambar di internet tidak diterima, begitu pula SVG dan WebP: pencetakan gagal dengan pesan image: src must be a PNG, JPEG or GIF data URI.

Gambar juga bisa diambil dari data: isi picture from data dengan placeholder, misalnya {{photo}}, dan kirim data URI di field itu (foto produk, tanda tangan, logo per perusahaan). Gambar yang kosong atau rusak dibiarkan kosong tanpa menggagalkan laporan.

Format teks

Pilih elemen, lalu pakai grup Font dan Paragraph di tab Home: jenis font (lihat Font), ukuran, bold, italic, underline, warna teks, highlight, rata, dan spasi baris.

Format sebagian teks

Di designer: klik ganda teksnya, blok kata yang dimaksud, tekan B / I / U. Di JSON hasilnya berupa tag <b>, <i>, <u>:

Total: <b>{{sum(price):2}}</b> <i>(belum termasuk pajak)</i>

Hanya tiga tag itu yang dikenali. Tanda < lain, termasuk yang berasal dari data, dicetak apa adanya, jadi data pengguna tidak bisa menyisipkan HTML. Untuk mencetak tulisan <b> itu sendiri, tulis &lt;b>; designer melakukannya otomatis saat Anda mengetiknya.

Warna sesuai data

Warna teks, isi, warna tabel (textColor, stripe, headerFill, ...), dan warna di cellStyle boleh berupa rumus, dihitung per baris (di tabel data: per item), juga di Excel:

"color": "{{if(total < 0, '#dc2626', '#000000')}}"      angka negatif merah
"fill":  "{{if(stock < minimum, '#fee2e2', '')}}"       stok menipis disorot

Bulan yang rugi dicetak merah dengan latar merah muda; judulnya memakai tebal dan miring di dalam satu teks:

Hasil yang bukan #rrggbb diabaikan. Di designer: kolom colour from data dan fill from data di panel kanan (untuk tabel: warna teks isinya), ditulis tanpa kurung kurawal seperti show when: if(total < 0, '#dc2626', '#000000'). Mengosongkannya kembali ke warna dari ribbon.

Huruf yang tidak ada di font PDF bawaan (aksara non-Latin, emoji) dicetak sebagai ?. Pakai salah satu font bundel atau font sendiri; lihat Font.

Tabel

Tab Insert: isi jumlah baris × kolom, klik Table. Klik ganda sel untuk mengisi; isinya boleh placeholder. Di panel kanan atur jumlah baris dan kolom, lebar kolom (3,1,1 = kolom pertama tiga kali lebih lebar), tebal dan warna garis. Klik sebuah sel lalu tombol rata untuk mengatur rata kolom itu.

Warna, garis, judul, dan baris total

Semua diatur di panel kanan saat tabel dipilih:

PanelJSONArti
header row · header fill · header textheader, headerFill, headerColorBaris pertama menjadi judul: tebal, dengan warna latar dan teks sendiri.
total row · total row fill · total row textfooter, footerFill, footerColorBaris terakhir menjadi baris total (footer), tebal. Mencentangnya menambah satu baris di bawah; pada tabel satu baris, baris itu sendiri yang menjadi baris total.
banded rowsstripeWarna latar baris isi selang-seling.
text colour · line colourtextColor, colorWarna teks isi dan warna garis.
linesgridall (semua garis), outer (bingkai luar saja), rows (garis antar-baris saja).
header line · total line · frameheaderLine, footerLine, frameGaris lebih tebal (pt) di bawah baris judul, di atas baris total, dan di sekeliling tabel.

Warna yang belum dipilih tampak pudar; tombol × di sebelahnya mengosongkannya lagi. Isi tabel juga bisa memakai fill, font, dan ukuran dari ribbon.

Tabel dengan baris judul, baris selang-seling, dan baris total, yang barisnya diulang untuk setiap item di items (lihat Tabel data di bawah):

Sel: warna sendiri dan sel gabungan

Klik sebuah sel. Bagian Sel di panel kanan mengatur warna latar, warna teks, rata, tebal, dan miring sel itu saja, di atas gaya baris dan kolomnya. Tombol Merge right dan Merge down menggabung sel dengan tetangganya (teksnya ikut dipindahkan, tidak ada yang hilang), Unmerge memisahkannya lagi. Gaya dan gabungan ikut bergeser saat baris atau kolom disisipkan atau dihapus.

  • Seret garis antar kolom atau antar baris pada tabel terpilih untuk mengubah lebar kolom dan tinggi baris; angkanya juga bisa diisi di column widths dan row heights.
  • cell padding mengatur jarak teks dari tepi sel (kosong = 3pt mendatar, 2pt tegak).
  • Saat mengetik di sel: Enter menyimpan, Shift+Enter baris baru, Tab / Shift+Tab ke sel berikutnya / sebelumnya. Sebagian teks sel bisa dibuat tebal, miring, atau bergaris bawah seperti di text box.
  • Sel yang disalin dari Excel bisa langsung ditempel (Ctrl+V) ke tabel, mulai dari sel yang diklik; baris dan kolom ditambah bila perlu.

Di JSON, gaya sel ada di cellStyle, dengan kunci "baris,kolom" mulai dari 0. Judul selebar empat kolom, baris judul kolom berwarna, satu sel disorot, dan label yang digabung tiga kolom:

Tabel data: daftar di dalam data

Isi rows from data dengan nama daftar di data, misalnya items. Baris judul dan baris total dicetak sekali; baris di antaranya diulang untuk setiap item. Di baris yang diulang, {{name}} adalah field item dan {{index}} nomor urutnya; di judul dan baris total, {{sum(qty * price):0}} dan {{count()}} menghitung semua item. Di designer tabel ini bertanda ↻ items.

{ "number": "SO-1", "items": [ { "name": "Kopi", "qty": 2, "price": 18000 }, ... ] }

Taruh tabel data di band detail untuk mencetak daftar di setiap baris (setiap pesanan dengan itemnya sendiri), atau di summary untuk daftar di data utama. Tabel yang lebih panjang dari satu halaman berlanjut ke halaman berikutnya tanpa memotong baris, dan baris judulnya diulang di atas setiap halaman. Di XLSX dan CSV setiap item menjadi satu baris; warna latar dan warna teks sel ikut ke XLSX dan XLS.

Tabel yang barisnya mengikuti data

Pakai dua tabel satu baris dengan lebar kolom yang sama:

  1. di band columnHeader: judul kolom (bold, beri warna latar),
  2. di band detail: placeholder, misalnya {{name}}, {{qty}}, {{qty * price:0}},
  3. opsional, di band summary: baris total dengan {{sum(qty * price):0}}; centang total row agar bergaya footer.

Di pola ini tiap baris data adalah tabelnya sendiri, jadi banded rows tidak berlaku; beri warna judul lewat header row pada tabel judul.

Karena band detail dicetak sekali per baris data, hasilnya satu tabel utuh. Centang Wrap pada tabel detail agar baris ikut tinggi saat isinya panjang. Kolom tabel ini juga menjadi kolom di Excel dan CSV. Lihat template Faktur dan Daftar stok.

Chart

Chart menggambar data baris. Isi label dengan field pengelompok dan value dengan field angka atau perhitungan. Nilai dengan label yang sama dijumlahkan, jadi label: category dan value: qty * price langsung memberi penjualan per kategori.

JenisBentukCocok untuk
barBatang tegak, nilai di atasnya, skala nilai di kiriMembandingkan beberapa kategori
hbarBatang mendatar, label di kiri, skala nilai di bawahNama kategori yang panjang
lineGaris penghubungUrutan waktu
pieLingkaran dengan legendaPorsi dari keseluruhan
donutPie berlubangPorsi dari keseluruhan

Dua chart dari baris yang sama: batang menjumlah penjualan per bulan, donut per kategori.

Chart memakai cakupan yang sama dengan rumus: di summary ia merangkum seluruh baris, di groupFooter hanya kelompok itu. Skala nilai bar, hbar, dan line selalu memuat nol, jadi nilai negatif tampil di bawah garis nol (pie dan donut menggambarnya sebagai nol). Jika kategorinya rapat, label ditampilkan selang-seling agar tidak bertumpuk. Chart muncul di PDF dan HTML, dan di XLSX sebagai chart Excel yang bisa diubah (tidak di XLS, CSV, dan printer struk). Di designer, chart digambar dari data contoh dengan skala, label, dan format angka yang sama seperti hasil cetak. Lihat template Laporan penjualan (donut dan batang) dan Daftar stok (batang mendatar).

Barcode & QR

Tab Insert → Barcode. Isi text dengan teks tetap, field, atau gabungan keduanya:

{{sku}}
https://tokosaya.example/faktur/{{number}}
FormatUntukIsi
qrKamera ponsel: tautan, nomor verifikasiTeks apa pun
code128Pemindai kasir dan gudang: SKU, nomor transaksiHuruf, angka, dan tanda baca dasar (ASCII)

Coba pindai dari layar dengan ponsel:

QR code diberi ruang kosong dua modul di sekelilingnya, di dalam kotaknya, agar mudah dipindai di samping elemen lain. Centang show text untuk mencetak teksnya di bawah kode. Di designer, kode yang tampil di halaman sama persis dengan hasil cetak (modul demi modul), jadi bisa langsung dicoba dipindai dengan ponsel; {{field}} di dalamnya diisi dari data contoh. Teks yang tidak bisa dijadikan kode (kosong, atau bukan ASCII untuk Code 128) tidak menggambar apa-apa. Lihat template Label produk, Struk kasir, dan Sertifikat.

Halaman & latar

PengaturanKeterangan
Ukuran kertasA4 595 × 842, A5 420 × 595, Letter 612 × 792, Legal 612 × 1008, F4 595 × 935, atau isi lebar dan tinggi sendiri (struk 80 mm = lebar 226).
OrientasiPortrait atau Landscape.
MarginAtas, kanan, bawah, kiri, masing-masing sendiri.
Warna latarWarna seluruh halaman.
Gambar latarGambar yang direntangkan memenuhi setiap halaman: kop surat, bingkai sertifikat, watermark. Siapkan gambar dengan rasio yang sama dengan kertasnya.

Format hasil

Yang terbawaPDFHTMLXLSXXLSCSVESC/POSTXT
Tata letak persis seperti desain✓✓–––––
Rata kiri, tengah, kanan✓✓✓✓–✓✓
Baris data, subtotal, total✓✓✓✓✓✓✓
Gambar, barcode, QR✓✓✓––✓–
Chart✓✓✓––––
Latar halaman✓✓–––––
Teks tebal✓✓✓✓–✓–
Angka tetap angka (bisa dijumlah)––✓✓✓––
Warna, garis sel, sel yang digabung✓✓✓✓–––

Printer struk (ESC/POS) dan teks biasa

Format escpos mencetak langsung ke printer struk thermal 58 mm dan 80 mm (USB, jaringan, atau Bluetooth) tanpa PDF: hasilnya byte perintah ESC/POS yang dikirim apa adanya ke printer. Format txt adalah tata letak yang sama sebagai teks biasa, untuk pratinjau, log, atau printer dot matrix.

  • Setiap teks ditaruh di kolom karakter yang ditutupi kotaknya, dengan perataan seperti di desain, dan dibungkus di dalam kolom itu. Teks tebal tetap tebal, teks 11 pt ke atas dicetak dua kali lebih tinggi, dan garis mendatar menjadi deretan -.
  • Jumlah karakter per baris mengikuti lebar kertas: 32 untuk kertas sampai 60 mm (printer 58 mm), 48 sampai 92 mm (printer 80 mm), dan 80 untuk yang lebih lebar (dot matrix). Ubah dengan "textColumns" atau Karakter per baris di tab Layout, yang juga punya ukuran kertas Struk 58 mm dan Struk 80 mm.
  • QR code dan Code 128 digambar oleh printer sendiri, logo dicetak sebagai titik hitam-putih, lalu kertas didorong dan dipotong. Di txt, barcode ditulis sebagai isinya dan gambar dilewati.
  • Kertasnya satu gulungan: header sekali di atas, footer sekali di bawah, tanpa ganti halaman. Huruf dikirim dalam code page Windows-1252 (é, €); huruf di luarnya menjadi ?.
ddreport -format escpos < struk.json > struk.bin
cat struk.bin > /dev/usb/lp0                 # Linux, printer USB
nc 192.168.1.50 9100 < struk.bin             # printer jaringan (port 9100)
copy /b struk.bin \\localhost\PrinterStruk   # Windows, printer yang di-share

Dari kode: render(template, data, 'escpos') di Node.js, DdReport.render(..., "escpos") di Java, atau POST /render?format=escpos, lalu kirim byte-nya ke printer: lewat socket ke port 9100, atau lewat library printer Bluetooth di aplikasi Android (fungsi yang menulis byte mentah). Dari aplikasi web: printer jaringan lewat server, atau printer Bluetooth/USB langsung dari browser dengan Web Serial; lihat Cetak ke printer struk. Template Struk kasir siap pakai sudah selebar kertas 80 mm.

Bagaimana desain menjadi tabel Excel

Untuk XLSX, XLS, dan CSV, setiap teks dan sel tabel di band detail menjadi satu kolom, urut seperti dibaca: dari atas ke bawah, lalu kiri ke kanan. Setiap baris data menjadi satu baris.

Band lain (judul kolom, judul dan subtotal kelompok, ringkasan) menambah satu baris untuk setiap baris teksnya. Tiap teks masuk ke kolom yang sejajar dengannya, sehingga subtotal jatuh tepat di bawah kolom angkanya. Band header dan footer tidak ikut.

Laporan tanpa band detail, seperti surat atau sertifikat, tidak punya kolom: teksnya diekspor baris demi baris seperti dibaca, termasuk band header.

  • Teks yang isinya tepat satu placeholder angka, seperti {{qty * price:2}}, ditulis sebagai angka.
  • Garis tabel ikut sesuai lines (grid): semua garis, hanya antar-baris, atau hanya bingkai luar. Sel yang digabung ke kanan (misalnya label "Total" selebar dua kolom) tetap tergabung, asal kolom yang ditutupnya jelas dan hanya ada satu nilai di dalamnya, sehingga tidak ada angka yang tersembunyi. Gabungan ke bawah tidak ikut.
  • xls ditulis sebagai XML Spreadsheet 2003. Excel menampilkan satu peringatan bahwa format dan ekstensi berbeda; pilih Yes.
  • CSV diawali BOM UTF-8 agar Excel membaca huruf seperti é dengan benar. Untuk Excel berbahasa Indonesia atau Eropa, yang memakai ; sebagai pemisah, pilih CSV separator ; di tab Layout.
  • Di CSV, teks yang berawalan seperti formula (=, @, +, -) diberi tanda kutip tunggal di depan, supaya isian pengguna tidak dijalankan sebagai formula. Angka seperti -5 tidak diubah.
Resep

Bagaimana cara…?

Jawaban singkat untuk hal yang paling sering ditanyakan.

…menampilkan "Halaman 1 dari 5"?

Taruh teks Halaman {{page}} dari {{pages}} di band footer.

…menaruh total tepat di bawah tabel?

Taruh teks {{sum(qty * price):0}} di band summary, rata kanan, dengan tepi kanan yang sama dengan kolom jumlahnya. Band summary dicetak tepat setelah baris terakhir.

…membuat baris tabel berbingkai yang mengikuti data?

Tabel satu baris di columnHeader untuk judul, tabel satu baris di detail untuk isi. Lihat Tabel.

…membiarkan deskripsi panjang turun ke baris berikutnya?

Pilih teks atau tabelnya, centang Wrap di tab Home. Baris akan memanjang sesuai isinya.

…menulis angka dengan format rupiah?

Di tab Layout → Data isi pemisah ribuan . dan desimal ,. Lalu tulis Rp {{price:0}}.

…memasang kop surat atau watermark?

Tab Layout → Background → Picture, pilih gambar seukuran kertas. Atau susun kop dari teks dan garis di band header seperti template Surat resmi.

…mencetak satu halaman untuk setiap pelanggan?

Isi Group by dengan field pelanggan dan centang New page per group.

…mencetak satu label per barang?

Buat kertas seukuran label dan beri band detail tinggi yang sama dengan area cetaknya. Setiap baris data akan menempati satu halaman. Lihat template Label produk.

…menambahkan QR untuk verifikasi dokumen?

Tab Insert → Barcode, format qr, isi https://situsanda.example/verify/{{id}}.

…membuat laporan mendatar (landscape)?

Tab Layout → Orientation → Landscape.

…membuat dokumen tanpa tabel, seperti surat?

Kosongkan band detail. Taruh kop di header dan isi surat di summary; centang Wrap pada paragrafnya agar bagian tanda tangan turun mengikuti panjang isi.

…menampilkan huruf Arab, Jepang, atau aksara lain di PDF?

Berikan font TrueType yang memuat aksara itu. Lihat Font. Untuk Arab dan Ibrani, PDF menyambung huruf Arab dan menyusun teks dari kanan ke kiri sendiri: pakai Amiri atau Noto Naskh Arabic dari paket 100 font dan ratakan teksnya ke kanan. Thai: Sarabun. Devanagari dan aksara Jawa belum didukung di PDF.

Backend

Memanggil dari backend

Satu fungsi di setiap bahasa: template + data + format → dokumen.

Go memakai library-nya langsung, tanpa binary.

go get github.com/gvjavx/DdDesignReport
import ddreport "github.com/gvjavx/DdDesignReport"

template, _ := os.ReadFile("invoice.json")
data := map[string]any{
    "number": "INV-001",
    "rows":   rows, // slice of struct atau map
}

// "pdf" | "html" | "xlsx" | "xls" | "csv"
pdf, err := ddreport.Render(template, data, "pdf")

data boleh berupa apa pun yang bisa di-marshal ke JSON: map, struct dengan tag json, atau json.RawMessage. Nama field di placeholder mengikuti nama JSON-nya.

Dari Maven Central (io.github.gvjavx:ddreport): tambahkan wrapper-nya dan binary untuk sistem tempat aplikasi berjalan, dengan classifier linux-x64, linux-arm64, windows-x64, windows-arm64, darwin-x64, atau darwin-arm64. Tidak ada dependency lain; DDREPORT_BIN menunjuk binary ddreport lain bila perlu.

<dependency>
  <groupId>io.github.gvjavx</groupId>
  <artifactId>ddreport</artifactId>
  <version>0.4.3</version>
</dependency>
<dependency>
  <groupId>io.github.gvjavx</groupId>
  <artifactId>ddreport</artifactId>
  <version>0.4.3</version>
  <classifier>linux-x64</classifier>
</dependency>
import io.ddreport.DdReport;

byte[] pdf = DdReport.render(templateJson, dataJson, "pdf");
Files.write(Path.of("report.pdf"), pdf);

Dua argumen pertama adalah string JSON; serialisasikan objek data dengan Jackson atau Gson. Kegagalan dilempar sebagai IOException.

Di server yang mencetak banyak laporan, pakai DdReport.Renderer: satu proses yang terus berjalan, sekitar lima kali lebih cepat per laporan kecil.

try (DdReport.Renderer renderer = new DdReport.Renderer()) { // atau new DdReport.Renderer(Duration.ofSeconds(30))
    byte[] pdf = renderer.render(templateJson, dataJson, "pdf");
}

Beberapa thread memakai satu Renderer secara bergantian; untuk mencetak paralel, buat satu per thread. Laporan yang gagal atau terlalu lama hanya menggagalkan laporan itu sendiri.

Kotlin memakai class Java yang sama dari Maven Central: implementation("io.github.gvjavx:ddreport:0.4.3") dan runtimeOnly("io.github.gvjavx:ddreport:0.4.3:linux-x64"), dengan classifier untuk sistem tempat aplikasi berjalan.

import io.ddreport.DdReport

val xlsx = DdReport.render(templateJson, dataJson, "xlsx")
File("report.xlsx").writeBytes(xlsx)

Binary untuk sistemnya ikut terpasang (paket @ddreport/<sistem>); DDREPORT_BIN menunjuk binary ddreport lain bila perlu.

npm install ddreport
const { renderAsync } = require('ddreport')

const pdf = await renderAsync(template, data, 'pdf') // Buffer
res.type('application/pdf').send(pdf)

template dan data adalah objek biasa. renderAsync mengembalikan Promise dan tidak memblokir event loop; render adalah versi sinkronnya. Keduanya melempar Error berisi pesan dari binary jika gagal.

Di server yang mencetak banyak laporan, biarkan prosesnya tetap berjalan (sekitar lima kali lebih cepat per laporan kecil):

const { createRenderer } = require('ddreport')

const renderer = createRenderer({ processes: 2 }) // opsional: { timeout: 30000 }
const pdf = await renderer.render(template, data, 'pdf')
renderer.close() // saat server berhenti

Laporan menunggu giliran jika semua proses sibuk. Proses yang berhenti atau terlalu lama hanya menggagalkan laporannya sendiri dan diganti yang baru; proses yang menganggur tidak menahan Node.js tetap berjalan.

Dari bahasa apa pun (PHP, Python, .NET, ...), lewat mode -serve atau image Docker-nya (di setiap rilis mulai 0.3.0).

DDREPORT_TOKEN=rahasia-panjang ddreport -serve 127.0.0.1:8080
# atau: docker run -p 8080:8080 -e DDREPORT_TOKEN=rahasia-panjang ghcr.io/gvjavx/ddreport

curl -X POST "http://127.0.0.1:8080/render?format=pdf" \
  -H "Authorization: Bearer rahasia-panjang" \
  -d '{"template": {...}, "data": {...}}' -o report.pdf

Respons berisi dokumen dengan Content-Type yang sesuai, status 400 dengan pesan error, atau 401 bila tokennya salah. Ukuran body dibatasi 32 MB, dan koneksi yang macet diputus otomatis. Bila render yang menunggu dan berjalan sudah empat kali jumlah prosesor, render berikutnya dijawab 503 dengan Retry-After: 5. GET /healthz menjawab ok tanpa token untuk load balancer, dan setiap render dicatat satu baris di stderr (POST /render?format=pdf 200 48213B 85ms), tanpa token dan isi laporan. Body-nya sama persis dengan file JSON di setiap contoh panduan. Contoh siap pakai untuk PHP, Python, dan C# ada di tab masing-masing.

Tanpa DDREPORT_TOKEN, siapa pun yang bisa menjangkau port itu bisa mencetak, dan ddreport -serve memberi peringatan bila alamatnya terbuka ke jaringan. Dengan token, designer di / tetap terbuka; preview dan ekspornya memakai token dari alamat halaman: http://server:8080/#token=rahasia-panjang.

PHP mencetak lewat server cetak ddreport: jalankan dulu seperti di tab HTTP, misalnya dengan Docker di sebelah aplikasi Anda.

$ch = curl_init('http://localhost:8080/render?format=pdf');
curl_setopt_array($ch, [
    CURLOPT_POST => true,
    CURLOPT_HTTPHEADER => ['Content-Type: application/json', 'Authorization: Bearer ' . getenv('DDREPORT_TOKEN')],
    CURLOPT_POSTFIELDS => json_encode(['template' => $template, 'data' => $data]),
    CURLOPT_RETURNTRANSFER => true,
]);
$pdf = curl_exec($ch);
if (curl_getinfo($ch, CURLINFO_HTTP_CODE) !== 200) {
    throw new RuntimeException('ddreport: ' . $pdf); // pesan kesalahan template ada di body
}

// Laravel
$pdf = Http::withToken(env('DDREPORT_TOKEN'))
    ->post('http://localhost:8080/render?format=pdf', ['template' => $template, 'data' => $data])
    ->throw()->body();

Template boleh dibaca dengan json_decode($json, true): [] yang ditulis PHP untuk objek kosong dibaca sebagai objek kosong.

Python mencetak lewat server cetak ddreport: jalankan dulu seperti di tab HTTP.

import os, requests

res = requests.post(
    "http://localhost:8080/render",
    params={"format": "pdf"},
    headers={"Authorization": "Bearer " + os.environ["DDREPORT_TOKEN"]},
    json={"template": template, "data": data},
    timeout=60,
)
res.raise_for_status()  # pesan kesalahan template ada di res.text
pdf = res.content

C# dan .NET mencetak lewat server cetak ddreport: jalankan dulu seperti di tab HTTP.

var http = new HttpClient { BaseAddress = new Uri("http://localhost:8080") };
http.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", Environment.GetEnvironmentVariable("DDREPORT_TOKEN"));

// template, data: objects, or JsonElement read with JsonDocument.Parse(...).RootElement
var body = new StringContent(JsonSerializer.Serialize(new { template, data }), Encoding.UTF8, "application/json");
var res = await http.PostAsync("/render?format=pdf", body);
if (!res.IsSuccessStatusCode)
    throw new Exception("ddreport: " + await res.Content.ReadAsStringAsync()); // pesan kesalahan template ada di body
byte[] pdf = await res.Content.ReadAsByteArrayAsync();

Memasang designer di aplikasi Anda

Designer adalah satu file ES module tanpa dependency dan tanpa build step. Pasang dari npm, atau salin designer/designer.js:

npm install ddreport-designer
import { createDesigner, emptyTemplate } from 'ddreport-designer'

const designer = createDesigner(document.getElementById('designer'), {
  template: savedTemplate,
  onChange: template => save(template),
  preview: async template => {
    const res = await fetch('/api/report/preview', {
      method: 'POST',
      body: JSON.stringify(template),
    })
    return res.text()
  },
})

designer.getTemplate()      // salinan template saat ini
designer.setTemplate(other) // mengganti template; pengguna bisa membatalkannya dengan Ctrl+Z
designer.destroy()          // saat designer dilepas dari halaman
OpsiKeterangan
templateTemplate awal. Kosongkan untuk mulai dari A4 kosong. Template yang rusak atau dari versi lama diperbaiki saat dimuat, bukan ditolak.
theme'light' (default), 'dark', atau 'auto' untuk mengikuti pengaturan sistem. Kertas laporannya sendiri selalu putih.
lang'en' (default) atau 'id' untuk antarmuka berbahasa Indonesia. Di aplikasi standalone: tambahkan ?lang=id pada alamatnya, atau klik tautan bahasa di kanan atas.
onChange(template)Dipanggil setiap ada perubahan. Objeknya adalah objek kerja designer, jangan diubah; pakai getTemplate() jika butuh salinan.
fontBaseOpsional. URL tempat font bundel disajikan, misalnya '/fonts/' (ddreport -serve menyajikannya di sana; di Go, sajikan ddreport.BundledFonts). Tanpa ini kanvas memakai font yang terpasang di perangkat; hasil cetak tidak terpengaruh.
dataOpsional. Data contoh (field dan baris): chart di kanvas digambar darinya, menu Field berisi field-nya bila fields tidak diisi, dan Preview menyebut field di template yang tidak ada di data ("Tidak ada di data contoh, tercetak kosong: ..."). Ubah dengan designer.setData(data).
fontsOpsional. Nama font tambahan yang Anda daftarkan di backend, misalnya ['Inter'], agar muncul di pilihan font.
preview(template)Opsional. Fungsi async yang mengembalikan HTML hasil cetak (format "html" dari backend Anda). Jika diisi, tombol Preview muncul di tab View dan di bilah status. Hasilnya tampil per halaman, dipaskan ke lebar dialog, dengan jumlah halaman; jika fungsi ini melempar error, pesannya ditampilkan di dialog.
simpleOpsional. true untuk mode mudah: grup Arrange, Margins, Grouping, sub-laporan, dan properti lanjutan (posisi, garis tabel, showIf, ...) disembunyikan; setiap tombol ribbon diberi nama; band grup baru muncul setelah Group by diisi.
hideOpsional. Daftar nama yang disembunyikan: tab ('Layout'), grup ribbon ('Arrange', atau 'Layout.Data' untuk grup di satu tab saja), perintah ('delete', 'preview'), elemen ('chart', 'subreport'), atau pengaturan di ribbon dan panel properti ('sortBy', 'showIf'). Perintah yang disembunyikan juga tidak jalan lewat pintasan keyboard.
messagesOpsional. Teks antarmuka versi Anda sendiri, dengan teks Inggrisnya sebagai kunci: { 'Text box': 'Label' }. Bisa untuk mengganti istilah atau menerjemahkan ke bahasa lain; daftar teksnya ada di objek ID di designer.js.

Di React atau Vue

// React
function ReportDesigner({ template, onChange }) {
  const ref = useRef(null)
  useEffect(() => {
    const d = createDesigner(ref.current, { template, onChange, simple: true })
    return () => d.destroy()
  }, [])
  return <div ref={ref} style={{ height: 600 }} />
}

// Vue
const el = ref(null); let d
onMounted(() => { d = createDesigner(el.value, { template, onChange }) })
onBeforeUnmount(() => d.destroy())

Paket npm membawa tipe TypeScript (autocomplete untuk semua opsi di atas) dan ketiga belas template siap pakai: import invoice from 'ddreport-designer/templates/invoice.json', lalu designer.setTemplate(invoice.template) dan designer.setData(invoice.data).

Dokumentasi ini memakai nama tombol versi Inggris (Home, Insert, Layout, View, Wrap); dengan lang: 'id' namanya menjadi Beranda, Sisipkan, Tata Letak, Tampilan, Bungkus.

Designer mengisi elemen tempatnya dipasang dan menyesuaikan diri dengan lebar elemen itu, bukan lebar jendela, jadi tetap rapi di dalam panel samping atau dialog. Beri elemen itu tinggi (misalnya height: 80vh) agar halaman bisa digulir di dalamnya. Warna bisa diganti lewat CSS, misalnya #designer.ddr { --a: #0f766e }; variabel lainnya --a-soft, --bg, --surface, --text, --muted, --line, --stage, --hover, sudut --r, dan font --font. Setiap bagian punya class (.ddr-tabs, .ddr-b, .ddr-page, .ddr-side, .ddr-status, ...) yang bisa ditimpa dengan .ddr .ddr-b { ... }.

Bootstrap dan toolbar sendiri

theme: 'inherit' membuat designer memakai warna, font, dan sudut dari variabel Bootstrap (--bs-primary, --bs-body-bg, --bs-border-radius, ...), termasuk dark mode Bootstrap. Framework lain (Tailwind, DaisyUI, Bulma) cukup mendefinisikan variabel --bs-* itu. Untuk toolbar dari komponen Anda sendiri, sembunyikan ribbon dan jalankan perintah designer dari tombol Anda:

const designer = createDesigner(el, {
  theme: 'inherit',
  hide: ['ribbon'],
  onState: s => boldButton.classList.toggle('active', s.bold),  // tombol mana yang aktif
})
boldButton.onclick = () => designer.run('bold')   // 'align:center', 'add:table', 'undo', 'zoom:fit', ...
designer.commands()                                // semua perintah

Contoh halaman lengkap dengan navbar, dropdown, dan tombol Bootstrap ada di designer/bootstrap.html; ddreport -serve menampilkannya di /bootstrap.html.

Bisa dipakai di framework apa pun: panggil createDesigner pada elemen DOM setelah komponen ter-mount. Template siap pakai ada di designer/templates/; tiap file berisi template dan data contohnya.

PDF/A dan tanda tangan digital

PDF/A untuk arsip jangka panjang: "pdfA": true di template, atau centang PDF/A di tab Layout. Hasilnya PDF/A-2b: semua font ada di dalam file (Helvetica, Times, dan Courier dicetak dengan Roboto dan Lora), warna sRGB, metadata XMP, dan ID dokumen. Hasilnya, juga yang ditandatangani dan yang berisi teks Arab, lulus validasi veraPDF untuk PDF/A-2b.

Tanda tangan digital: setiap PDF ditandatangani bila DDREPORT_SIGN_CERT dan DDREPORT_SIGN_KEY menunjuk sertifikat dan kunci privat (PEM; RSA atau ECDSA), dari CLI, -serve, Docker, Java, maupun Node.js. Dari Go: ddreport.SignPDF(pdf, cert, key, reason).

openssl pkcs12 -in identitas.p12 -nodes -out identitas.pem
DDREPORT_SIGN_CERT=identitas.pem DDREPORT_SIGN_KEY=identitas.pem \
DDREPORT_SIGN_REASON="Faktur asli" ./ddreport -serve 127.0.0.1:8080

Tanda tangannya tidak terlihat di halaman (adbe.pkcs7.detached, SHA-256) dan ditambahkan sebagai pembaruan inkremental, jadi PDF/A tetap PDF/A. Agar pembaca mempercayainya, sertifikatnya harus dari penerbit yang dipercaya pembaca, misalnya PSrE untuk Indonesia.

JasperReports

Template bisa dibawa ke dan dari JasperReports (.jrxml versi 6, yang dibuka Jaspersoft Studio 6 dan 7). Di aplikasi standalone: Export → JRXML, dan Open… menerima file .jrxml.

ddreport -format jrxml < template.json > report.jrxml   # ke JasperReports
ddreport -from-jrxml < report.jrxml > template.json     # dari JasperReports
  • Ke JasperReports: ekspresi ditulis dalam Groovy. Ketiga belas template siap pakai diuji di JasperReports 6.21: semuanya dikompilasi, diisi, dan diekspor ke PDF. Field baris diisi JsonDataSource atas rows, nilai data lain menjadi parameter. Teks, tabel, gambar, kotak, garis, barcode/QR, grup, agregat, nomor halaman ("Halaman X dari Y"), dan format angka/tanggal ikut. terbilang menjadi closure Groovy. Tabel dengan baris dari data dan sub-laporan menjadi jr:list atas daftarnya (parameter berisi java.util.List of Map, atau field baris berisi daftar JSON), dengan total di bawahnya yang ikut turun. Chart menjadi chart Jasper yang menjumlahkan nilai per label (butuh JFreeChart; donut dicetak sebagai pie).
  • Bolak-balik: properti ddreport.* di dalam file menyimpan apa yang tidak punya tempat di JasperReports, jadi .jrxml buatan ddreport kembali menjadi template yang sama persis. Ketiga belas template siap pakai dicetak identik setelah bolak-balik.
  • Dari JasperReports: band, grup, urutan, teks, text field (field, nomor halaman, variabel Sum/Average/Count, aritmetika, perbandingan, kondisi ? a : b, pattern), kotak, garis, frame, barcode, gambar Base64, chart batang/garis/pie, list (menjadi sub-laporan), dan crosstab ikut. Java lain disimpan di dalam [kurung] dan dicatat, begitu juga file .jrxml milik sub-laporan.
  • Dari kode: ToJRXML / FromJRXML di Go, format "jrxml" di render, dan POST /import/jrxml.

Font

Pilihan font di designer:

FontJenisAsal
Helvetica, Times, Couriersans, serif, monospacebawaan PDF, huruf Latin saja
Roboto, Open Sans, Lato, Montserratsansdibundel
Lora, Playfair Displayserifdibundel
Great Vibestulisan tangan, cocok untuk sertifikatdibundel (hanya regular)

Font bundel berlisensi SIL Open Font License, mencakup huruf Latin, Kiril, Yunani, dan Vietnam, dan ikut disematkan ke PDF dan HTML (hanya bentuk yang dipakai), jadi hasilnya sama di perangkat mana pun.

Font sendiri

Siapkan file Nama-Regular.ttf, dan bila ada Nama-Bold.ttf, Nama-Italic.ttf, Nama-BoldItalic.ttf. NamaFont-Bold.ttf menjadi font bernama "Nama Font".

// Go
ddreport.AddFonts(os.DirFS("fonts-saya"))
// binary (Java, Kotlin, Node.js, HTTP)
export DDREPORT_FONTS=/path/ke/fonts-saya

Lalu tambahkan namanya ke designer: createDesigner(el, { fonts: ['Inter'] }). Designer standalone (ddreport -serve) melakukannya sendiri: semua font di DDREPORT_FONTS muncul di menu font dan tergambar di kanvas.

Paket 100 font tambahan

Sans, serif, display, tulisan tangan, monospace, serta Arab (Amiri, Noto Naskh Arabic) dan Thai (Sarabun) dari Google Fonts (lisensi SIL OFL atau Apache), misalnya Inter, Poppins, Merriweather, Oswald, Dancing Script, dan Roboto Mono. Paket ini tidak ikut di binary (sekitar 39 MB). Unduh sekali dari repo library ke folder ddreport-fonts. Binary memakai folder itu dengan sendirinya bila dijalankan dari folder yang sama dan DDREPORT_FONTS tidak di-set:

node fontpack/download.mjs ddreport-fonts
./ddreport -serve 127.0.0.1:8080   # 100 font itu langsung ada di menu font

Folder lain: DDREPORT_FONTS=/path/ke/folder. Font yang baru diunduh muncul setelah ddreport dijalankan ulang.

Memilih font di menu

Tombol ⋯ di sebelah menu font membuka daftar semua font, masing-masing dengan centang. Hilangkan centangnya agar font itu tidak tampil di menu, centang lagi untuk memunculkannya kembali; ada kolom cari dan tombol Semua / Tidak ada. Pilihan disimpan di browser. Elemen yang memakai font tersembunyi tetap menampilkan fontnya.

Font hanya dibaca saat sebuah laporan memakainya, jadi folder berisi 100 font tidak memperlambat laporan yang tidak memakainya.

Mengganti font bawaan

Untuk aksara yang tidak ada di font bundel (Arab, Jepang, dan sebagainya), Anda juga bisa mengganti font bawaan PDF untuk seluruh laporan:

// set sekali saat aplikasi mulai
ddreport.Font, _ = os.ReadFile("NotoSans-Regular.ttf")
ddreport.FontBold, _ = os.ReadFile("NotoSans-Bold.ttf") // opsional
export DDREPORT_FONT=/fonts/NotoSans-Regular.ttf
export DDREPORT_FONT_BOLD=/fonts/NotoSans-Bold.ttf

Jika font bold tidak diberikan, teks bold memakai font regular. Dengan font pengganti, pilihan Helvetica/Times/Courier diabaikan dan italic memakai bentuk tegak; font bundel dan font yang didaftarkan tetap dipakai apa adanya.

Referensi

Template JSON

Template adalah JSON biasa, jadi bisa juga dibuat atau diubah lewat kode.

JSON Schema-nya (juga di paket npm ddreport-designer/template.schema.json dan di /template.schema.json pada ddreport -serve) memeriksa template di editor seperti VS Code ("$schema" tidak perlu ditulis di template; pasang lewat pengaturan json.schemas editor) dan menangkap salah ketik: properti yang tidak dikenal adalah kesalahan. Designer menulis "version": 1 (versi format template); ddreport menolak template yang versinya lebih baru dari yang bisa dibacanya, alih-alih mencetaknya tidak lengkap, jadi perbarui backend bersamaan dengan atau sebelum designer. Untuk asisten AI, llms.txt berisi ringkasan cara menulis template beserta contoh yang dicek oleh test library.

Asisten AI dengan MCP. ddreport -mcp menjadikan binary-nya server MCP, sehingga asisten AI (Claude, Cursor, dan lainnya) bisa membaca panduan dan schema (template_guide), memeriksa template yang ditulisnya (check_template: properti yang salah ketik, yang diam-diam diabaikan saat mencetak, lalu dicoba dicetak), dan mencetaknya ke file (render_template). Misalnya: minta asisten membuat template dari foto faktur lama Anda, dan biarkan ia memeriksanya sendiri sampai valid.

claude mcp add ddreport -- /path/ke/ddreport -mcp     # Claude Code; aplikasi lain: perintah ddreport, argumen -mcp
{
  "page": {
    "width": 595, "height": 842,
    "marginTop": 36, "marginRight": 36, "marginBottom": 36, "marginLeft": 36,
    "background": "", "backgroundImage": ""
  },
  "dataKey": "rows",
  "groupBy": "category",
  "groupPageBreak": false,
  "thousands": ".",
  "decimal": ",",
  "bands": {
    "header":       { "height": 60, "elements": [] },
    "columnHeader": { "height": 20, "elements": [] },
    "groupHeader":  { "height": 20, "elements": [] },
    "detail": {
      "height": 18,
      "elements": [
        { "type": "text", "x": 0, "y": 3, "w": 300, "h": 14, "text": "{{name}}", "grow": true },
        { "type": "text", "x": 380, "y": 3, "w": 140, "h": 14, "text": "{{qty * price:2}}", "align": "right" }
      ]
    },
    "groupFooter":  { "height": 20, "elements": [] },
    "summary":      { "height": 30, "elements": [] },
    "footer":       { "height": 30, "elements": [] }
  }
}
FieldDefaultKeterangan
page.width · height595 · 842Ukuran halaman dalam point. Tukar keduanya untuk landscape.
page.marginTop …0Margin per sisi. Sisi yang tidak diisi memakai page.margin.
page.backgroundkosongWarna latar, #rrggbb.
page.backgroundImagekosongGambar latar sebagai data URI.
dataKeyrowsKunci daftar baris di dalam data.
groupBykosongField pengelompokan.
groupPageBreakfalseHalaman baru untuk setiap kelompok.
thousands · decimal, · .Pemisah untuk {{f:N}}.
bands.*.height0Tinggi band.
Cetak gagal dengan pesan bands do not fit on the page jika header, footer, dan satu band isi tidak muat dalam satu halaman.

Properti elemen

Semua elemen punya type, x, y, w, h. Warna ditulis #rrggbb.

typePropertiKeterangan
texttext, font, fontSize (10), lineHeight (1.2), bold, italic, underline, align, color, fill, growfont: Helvetica, Times, Courier, atau nama font bundel / terdaftar. align: left, center, right. fill = highlight.
tablecells, cols, aligns, border, color, fill, grow, grid, textColor, stripe, header, headerFill, headerColor, footer, footerFill, footerColor, heights, padding, cellStyle, data, valign, dan properti font milik textcells: teks per sel, baris demi baris. cols: lebar relatif. aligns: rata per kolom. color: warna garis. Sisanya lihat Tabel.
imagesrcData URI. PDF menerima PNG, JPEG, dan GIF.
rectborder, color, fillTebal garis, warna garis, warna isi (kosong = transparan).
lineborder, colorMendatar jika w ≥ h, tegak jika sebaliknya.
chartchart, label, value, colorchart: bar, hbar, line, pie, donut. value boleh perhitungan.
barcodetext, format, colorformat: qr (default) atau code128. text boleh berisi placeholder.

Binary ddreport

Java, Kotlin, dan Node.js mencetak lewat binary ddreport, sehingga hasilnya identik dengan Go. Paket npm dan Maven sudah membawa binary untuk Windows, Linux, dan macOS (x64 dan arm64). Untuk memakai binary lain, unduh dari GitHub Release atau build sendiri, lalu taruh di PATH atau tunjuk lewat DDREPORT_BIN.

go build -o ddreport ./cmd/ddreport
export DDREPORT_BIN="$PWD/ddreport"
go build -o ddreport.exe ./cmd/ddreport
$env:DDREPORT_BIN = "$PWD\ddreport.exe"

Binary membaca {"template": ..., "data": ...} dari stdin dan menulis dokumen ke stdout:

ddreport -format pdf < request.json > report.pdf
Flag / variabelKeterangan
-formatpdf (default), html, xlsx, xls, csv, jrxml, escpos (printer struk), txt
-serve alamatMenjalankan designer (dengan template siap pakai) dan endpoint POST /render. Permintaan dari halaman situs lain di browser ditolak, jadi panggil endpoint ini dari backend Anda
-templates folderBersama -serve: folder tempat My templates disimpan, satu file JSON per template (default ddreport-templates; di Docker /data)
DDREPORT_TOKENUntuk -serve: setiap render harus mengirim Authorization: Bearer <token>. Tanpa token, server hanya melayani localhost dan alamat IP; untuk nama domain, isi token. Lihat tab HTTP di Memanggil dari backend
-stdioTerus berjalan dan mencetak permintaan satu per satu dari stdin: baris <format> <panjang> lalu {"template": ..., "data": ...} sepanjang itu; jawabannya baris ok <panjang> dan dokumennya, atau error <panjang> dan pesannya. Dipakai DdReport.Renderer dan createRenderer
DDREPORT_STRICT=1Laporan yang memakai field yang tidak ada di data (salah ketik, atau kolom yang tidak dikembalikan query) ditolak dengan nama field-nya, alih-alih dicetak kosong. Berlaku juga untuk Node.js dan Java. Dari Go: ddreport.Missing(template, data)
DDREPORT_MAX_ROWSLaporan dengan baris lebih dari ini ditolak dengan pesan error, alih-alih menghabiskan memori server (PDF 100 ribu baris butuh sekitar 500 MB). Dari Go: ddreport.MaxRows
-healthcheck alamatMeminta GET /healthz dari server di alamat itu dan keluar dengan kode 1 bila tidak dijawab ok. Dipakai HEALTHCHECK image Docker, yang tidak punya curl
-mcpServer MCP lewat stdin/stdout untuk asisten AI, dengan tool template_guide, check_template, dan render_template. Lihat Template JSON
DDREPORT_BINLokasi binary, dibaca oleh wrapper Java dan Node.js
DDREPORT_FONT · _BOLDFont pengganti untuk seluruh laporan. Lihat Font
DDREPORT_FONTSFolder font tambahan (Nama-Regular.ttf, ...). Lihat Font
DDREPORT_SIGN_CERT · _KEY · _REASONSertifikat dan kunci privat (PEM) untuk menandatangani setiap PDF, dan alasannya. Lihat PDF/A & tanda tangan

Jika gagal, binary keluar dengan kode 1 dan pesan di stderr; wrapper meneruskannya sebagai exception.

Test

Core Go punya unit test untuk setiap bagian mesin cetak (placeholder, perhitungan, format angka, pembungkusan teks, tabel, chart, barcode, ganti halaman, ekspor), test keamanan terhadap template berisi HTML berbahaya, dan test yang mencetak setiap template siap pakai ke semua format. Wrapper diuji terhadap binary sungguhan.

go test ./...                         # core, binary, semua template, dan test regresi
go build -o ddreport ./cmd/ddreport
export DDREPORT_BIN="$PWD/ddreport"
node --test node/test.js              # wrapper Node.js
javac -d out java/io/ddreport/DdReport.java java/DdReportTest.java
java -cp out DdReportTest             # wrapper Java

Designer punya halaman test sendiri: sajikan folder designer/ dengan web server (npx serve designer) lalu buka /test.html. Halaman itu menjalankan lebih dari 200 pemeriksaan pada designer dan menampilkan yang gagal; node designer/run-tests.mjs menjalankannya di Chromium, Firefox, dan WebKit sekaligus.

CI juga memeriksa kode Go dengan govulncheck di setiap push dan gagal bila ada celah keamanan yang sudah diketahui; jalankan sendiri dengan go run golang.org/x/vuln/cmd/govulncheck@v1.9.0 ./....

Batasan

Laporan penjualan siap pakai dengan data buatan, di laptop Intel Core i7-7500U (2017). Waktunya naik lurus dengan jumlah baris.

BarisPDFHTMLXLSXXLSCSV
10 000 (±250 halaman)±1 dtk±0,8 dtk0,2 dtk0,2 dtk0,2 dtk
100 000 (±2 500 halaman)±12 dtk, memori puncak ±500 MB±9 dtk, ±400 MB2,2 dtk, ±220 MB1,8 dtk1,6 dtk
  • PDF dan HTML digambar halaman demi halaman, jadi yang tersisa di memori hanya data dan dokumen jadinya. Untuk ekspor data mentah, XLSX dan CSV tetap paling hemat; DDREPORT_MAX_ROWS membatasi jumlah baris di server.
  • Pengelompokan sampai enam tingkat.
  • Di PDF, Arab, Ibrani, dan Thai didukung (dengan font yang memuatnya, misalnya Amiri dan Sarabun). Devanagari, aksara Jawa, dan aksara lain yang butuh penyusunan rumit belum.
  • JasperReports: crosstab ditulis ke .jrxml sebagai tabel yang dicetaknya, donut sebagai pie. Dari .jrxml, ekspresi Java selain field, variabel, aritmetika, perbandingan, dan ? :, serta file sub-laporan tersendiri, tidak ikut. Semuanya dicatat saat konversi.
  • XLSX dan XLS membawa sel yang digabung ke kanan dan ke bawah (colSpan, rowSpan), selama kolom dan baris yang ditutupnya jelas. Gambar, barcode/QR, dan chart (sebagai chart Excel) ikut di XLSX. Format XLS (XML Spreadsheet 2003) tidak punya tempat untuk gambar maupun chart.
  • Printer struk (escpos) dan txt: huruf dalam code page Windows-1252, tanpa chart; txt tanpa gambar.
  • Designer diuji otomatis di Chromium (Chrome, Edge), Firefox, dan WebKit (mesin Safari) di setiap push, termasuk sentuhan di layar HP yang diemulasikan browser (ketuk, geser, ubah ukuran, cubit); tetap coba juga di perangkat sungguhan sebelum rilis.

Riwayat versi

Daftar lengkapnya ada di CHANGELOG.md.

0.4.3 · 30 Okt 2026

  • Integrasi: panduan Integrasi dalam 10 menit dan contoh aplikasi lengkap di examples/app: designer, template per pelanggan, preview, PDF dan Excel, cetak struk lewat jaringan atau Web Serial.
  • Field yang salah ketik: preview di designer menyebut field yang tidak ada di data contoh; DDREPORT_STRICT=1 menolak laporan seperti itu; dari Go ddreport.Missing.
  • Designer: tanpa opsi fields, menu Field berisi field dari data contoh.

0.4.2 · 30 Okt 2026

  • Printer struk: format escpos mencetak langsung ke printer thermal 58 dan 80 mm (teks tebal dan besar, QR, Code 128, logo, potong kertas), dan txt untuk pratinjau dan dot matrix. Designer: kertas Struk 58/80 mm dan Karakter per baris. Lihat Printer struk.
  • Excel: chart ikut ke XLSX sebagai chart Excel asli yang bisa diubah.
  • JasperReports: terbilang, tabel dari data, sub-laporan, chart, dan "Halaman X dari Y" ikut ke .jrxml; dari .jrxml, chart, list, crosstab, dan ? : ikut. File buatan ddreport kembali menjadi template yang sama persis.
  • Laporan besar: memori puncak untuk 100 000 baris turun menjadi ±500 MB (PDF) dan ±400 MB (HTML).
  • Perbaikan: tabel di bawah tabel data yang memanjang ikut turun di XLSX, XLS, dan CSV.

0.4.1 · 30 Okt 2026

  • Template: "version" (versi format template). Template yang butuh ddreport lebih baru ditolak dengan pesan yang jelas, bukan dicetak tidak lengkap. Lihat Template JSON.
  • Server: GET /healthz, jawaban 503 saat antrean penuh, satu baris log per render, batas baris DDREPORT_MAX_ROWS, dan HEALTHCHECK di image Docker. Lihat Binary ddreport.
  • Asisten AI: ddreport -mcp, server MCP untuk membaca panduan, memeriksa, dan mencetak template.
  • JasperReports: ketiga belas template siap pakai diuji di JasperReports 6.21; enam perbaikan ekspor .jrxml.
  • Font: folder ddreport-fonts dipakai dengan sendirinya, dan tombol ⋯ memilih font mana yang tampil di menu. PDF/A-2b lulus veraPDF.

0.4.0 · 28 Okt 2026

  • Designer: My templates di server dengan riwayat 20 versi, tema yang mengikuti Bootstrap (theme: 'inherit'), toolbar sendiri (designer.run, onState), dan contoh halaman Bootstrap. Lihat Memasang designer.
  • Laporan: elemen crosstab, grouping sampai enam tingkat, serta Arab, Ibrani, dan Thai di PDF. Lihat Crosstab.
  • Excel: sel yang digabung ke bawah, serta gambar dan barcode di XLSX.
  • PDF: PDF/A-2b dan tanda tangan digital. Lihat PDF/A & tanda tangan.
  • JasperReports: ekspor dan impor .jrxml. Lihat JasperReports.
  • Font: paket 100 font tambahan yang baru dibaca saat dipakai. Lihat Font.
  • Laporan besar: benchmark 10 000 dan 100 000 baris, memori PDF sekitar 30% lebih kecil. Lihat Batasan.
  • Keamanan: tanpa DDREPORT_TOKEN, -serve hanya melayani localhost dan alamat IP.
  • Galeri template komunitas: kirim template lewat pull request. Lihat galeri template.

0.3.0 · 26 Okt 2026

  • Server dan Docker: image ghcr.io/gvjavx/ddreport (amd64 dan arm64, sekitar 17 MB) untuk mencetak dari PHP, Python, .NET, atau bahasa apa pun lewat HTTP, dengan contoh PHP, Laravel, Python, dan C#. DDREPORT_TOKEN mewajibkan Authorization: Bearer <token>, dan -serve memberi peringatan bila alamatnya terbuka ke jaringan tanpa token.
  • Warna dari data: color, fill, warna tabel, dan cellStyle boleh berupa rumus, misalnya {{if(total < 0, '#dc2626', '#000000')}}, juga di XLSX dan XLS. Di designer: colour from data dan fill from data.
  • Template: JSON Schema (template.schema.json) dan llms.txt untuk asisten AI. Template dari PHP yang menulis objek kosong sebagai [] tidak lagi ditolak.

0.2.1 · 22 Okt 2026

  • Keamanan: binary ddreport dibangun dengan Go 1.26.10. Binary 0.2.0 dibangun dengan Go 1.25.0 dan membawa celah pustaka standar Go yang sudah diketahui, terutama berisiko untuk mode -serve. Perbarui ke 0.2.1; tidak ada perubahan perilaku.

0.2.0 · 22 Okt 2026

  • Designer lebih mudah: mode mudah dengan nama di setiap tombol, tombol Start here di halaman kosong, dan properti bernama dari kiri, dari atas, lebar, tinggi. Lihat Memakai designer.
  • Designer bisa disesuaikan: opsi hide dan messages, destroy() untuk React dan Vue, tipe TypeScript, dan template di paket npm. Lihat Memasang designer.
  • Lima template baru: kuitansi, surat jalan, rapor siswa, daftar hadir, dan laporan laba rugi. Lihat galeri template.
  • Excel: garis sel tabel (semua, antar-baris, atau bingkai), sel yang digabung ke kanan, dan rata tengah/kanan ikut ke XLSX dan XLS.
  • Mesin cetak: terbilang() untuk angka dalam kata, dan {{index}} sebagai nomor baris di band detail. Lihat Perhitungan & total.
  • Kanvas designer: QR dan Code 128 yang sama persis dengan hasil cetak, chart dengan skala nilai, dan cubit-zoom di layar sentuh yang tidak lagi mengubah pilihan.
  • Backend: DdReport.Renderer (Java, Kotlin) dan createRenderer (Node.js) yang menjaga proses tetap berjalan. Lihat Memanggil dari backend.
  • Perbaikan dan keamanan: legenda pie tanpa irisan nol, pesan jelas untuk gambar rusak, -serve menolak permintaan dari situs lain, dan template dari orang lain tidak bisa membuat designer memuat alamat luar.

0.1.3 · 20 Okt 2026

  • Semua paket npm terbit lewat trusted publishing dari GitHub Actions, tanpa token npm.
  • Dokumentasi: pasang dari npm dan Maven Central, binary dari GitHub Release.

0.1.2 · 20 Okt 2026

  • ddreport dan ddreport-designer pertama kali terbit di npm.
  • Binary per sistem untuk npm bernama @ddreport/<sistem>, termasuk Windows.

0.1.1 · 20 Okt 2026

  • Pertama kali terbit di Maven Central: io.github.gvjavx:ddreport, dengan jar binary per sistem.

0.1.0 · 19 Okt 2026

Rilis pertama, dengan binary untuk Windows, Linux, dan macOS (x64 dan arm64) di GitHub Release. Dibanding versi awal (14 Okt):

  • Designer: tampilan baru dengan tema terang dan gelap, zoom kanvas, hapus elemen dengan tombol atau tempat sampah, ubah ukuran dengan pegangan (juga dengan sentuhan), dan Preview yang menampilkan halaman sebagai lembar. Lihat Memakai designer.
  • Tabel: tabel data yang barisnya mengikuti daftar di data, gabung sel, warna dan gaya per sel, garis tebal, tabel panjang yang berlanjut ke halaman berikutnya, dan tempel sel dari spreadsheet. Lihat Tabel.
  • Kelompok dan sub-laporan: pengelompokan sampai enam tingkat dengan subtotal, sub-laporan untuk setiap item dari daftar, dan satu worksheet per kelompok. Lihat Kelompok & subtotal dan Sub-laporan.
  • Mesin cetak: ekspresi dan format tanggal baru, showIf, gambar dari data, sortBy, teks panjang yang berlanjut ke halaman berikutnya, dan hasil hitungan yang tepat (0.1 + 0.2 = 0.3). Lihat Data & placeholder dan Perhitungan & total.
  • Font: font TrueType bawaan dan font sendiri. Lihat Font.
  • Chart: skala nilai dengan angka negatif dan label yang tidak bertumpuk; di designer, chart dan barcode digambar dari data contoh. Lihat Chart.
  • Backend: renderAsync di Node.js, batas waktu satu menit di Java dan Node.js, dan -serve yang mencetak paling banyak satu laporan per CPU sekaligus.
  • Pengujian: CI menjalankan test di Linux dan Windows, serta test designer di Chromium, Firefox, dan WebKit.