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.
-
Unduh binary
ddreportSatu 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 menjadiddreport, atau build sendiri dengan Go:git clone https://github.com/gvjavx/DdDesignReport cd DdDesignReport go build -o ddreport ./cmd/ddreport -
Jalankan designer
./ddreport -serve 127.0.0.1:8080Buka
http://127.0.0.1:8080di browser. -
Pilih template, lalu ubah
Di bagian atas pilih Templates… → Faktur. Klik ganda judulnya untuk mengganti teks, atau pakai tab Insert untuk menambah elemen.
-
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.
-
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 -servemenyimpannya di folderddreport-templates(ubah dengan-templates), beserta 20 versi sebelumnya dari setiap template: tombol History membuka versi lama, lalu Update menjadikannya versi sekarang lagi. -
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.pdfGanti
"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 kosong | Klik Text box, Table, Picture, atau Chart di kotak Start here di tengah halaman. |
| Menambah elemen | Tab Insert: klik tombolnya, atau seret tombolnya ke bagian halaman yang dituju. |
| Tampilan yang lebih sederhana | Mode mudah (di aplikasi standalone aktif sejak awal): alat yang jarang dipakai disembunyikan dan setiap tombol diberi nama. Cocok untuk pemula, juga anak-anak. |
| Mengetik teks | Klik 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 teks | Saat mengetik, blok kata yang dimaksud lalu tekan B, I, atau U. |
| Memilih beberapa elemen | Shift+klik, seret kotak di area kosong, atau Ctrl+A. |
| Memindah | Geser dengan mouse (menempel ke tepi elemen lain; tahan Alt untuk mematikan) atau tombol panah. Geser ke band lain untuk memindahkannya ke sana. |
| Mengubah ukuran | Tarik pegangan biru di sudut kanan bawah elemen terpilih (juga dengan sentuhan). Tarik tepi bawah sebuah band untuk mengubah tingginya. |
| Memperbesar tampilan | Bilah status di bawah: tombol − dan +, penggeser, klik persentasenya untuk kembali ke 100%, atau tombol paskan lebar. |
| Menghapus elemen | Klik 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. |
| Meratakan | Pilih beberapa elemen, lalu tombol di grup Arrange (tab Home). |
| Mengatur kertas | Tab Layout: ukuran, orientasi, margin, warna dan gambar latar. |
| Membatalkan | Ctrl+Z, sampai 100 langkah. Ctrl+Y untuk mengulang. Memulai laporan baru atau memuat template juga bisa dibatalkan. |
Isi ribbon
| Tab | Isi |
|---|---|
| Home | Undo/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. |
| Insert | Text box, tabel (baris × kolom), gambar, kotak, garis, chart, barcode, dan daftar field siap sisip. |
| Layout | Kertas (A4, A5, Letter, Legal, F4, custom), orientasi, margin per sisi, latar, pengelompokan, halaman baru per kelompok, pemisah angka. |
| View | Penggaris, 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
| Tombol | Aksi |
|---|---|
Ctrl+Z · Ctrl+Y | Undo · redo |
Ctrl+C · X · V · D | Copy · cut · paste · duplicate |
Ctrl+B · I · U | Bold · italic · underline |
Ctrl+A · Esc | Pilih semua · batalkan pilihan |
Ctrl+= · Ctrl+- · Ctrl+0 | Perbesar · perkecil · zoom 100% |
Ctrl + scroll | Zoom di titik kursor |
Panah · Shift+panah | Geser 1pt · 10pt |
Delete | Hapus 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.
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
groupBydiisi. Untuk nama kategori dan subtotal. - summarySekali saja, setelah baris terakhir. Untuk total, chart, catatan, tanda tangan.
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
Kepada: Budi
Kopi
2 × 18,000
= 36,000
contoh.json, lalu jalankan ddreport -format pdf < contoh.json > hasil.pdf, atau kirim isinya apa adanya ke POST /render?format=pdf (lihat Backend).| Tulis | Hasil |
|---|---|
{{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 denganand,or,not. Teks ditulis dalam kutip tunggal:status = 'lunas'. - Fungsi
if(kondisi, jika ya, jika tidak),upper,lower,round(x, desimal), danterbilang(x)(bahasa Indonesia, desimal dibulatkan; dipakai template Kuitansi). - Pola tanggal:
yyyyyyMMMMMMMMMMdddEEEEEEEHHmmss; 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,countmenghitung atas banyak baris. Isinya boleh berupa perhitungan. count()menghitung semua baris;count(f)hanya baris yangf-nya berupa angka.- Di dalam perhitungan, nama field yang bukan satu kata ditulis dalam kurung siku:
{{[unit-price] * qty}},{{sum([harga satuan])}}.
"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
detaillaporan 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" }
measuredihitung dari baris yang punya nilai baris dan kolom sel itu:sum(qty * price):0,avg(price):2,min(date), ataucount()(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
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 <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:
| Panel | JSON | Arti |
|---|---|---|
| header row · header fill · header text | header, headerFill, headerColor | Baris pertama menjadi judul: tebal, dengan warna latar dan teks sendiri. |
| total row · total row fill · total row text | footer, footerFill, footerColor | Baris terakhir menjadi baris total (footer), tebal. Mencentangnya menambah satu baris di bawah; pada tabel satu baris, baris itu sendiri yang menjadi baris total. |
| banded rows | stripe | Warna latar baris isi selang-seling. |
| text colour · line colour | textColor, color | Warna teks isi dan warna garis. |
| lines | grid | all (semua garis), outer (bingkai luar saja), rows (garis antar-baris saja). |
| header line · total line · frame | headerLine, footerLine, frame | Garis 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:
Entermenyimpan,Shift+Enterbaris baru,Tab/Shift+Tabke 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:
- di band
columnHeader: judul kolom (bold, beri warna latar), - di band
detail: placeholder, misalnya{{name}},{{qty}},{{qty * price:0}}, - 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.
| Jenis | Bentuk | Cocok untuk |
|---|---|---|
bar | Batang tegak, nilai di atasnya, skala nilai di kiri | Membandingkan beberapa kategori |
hbar | Batang mendatar, label di kiri, skala nilai di bawah | Nama kategori yang panjang |
line | Garis penghubung | Urutan waktu |
pie | Lingkaran dengan legenda | Porsi dari keseluruhan |
donut | Pie berlubang | Porsi 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}}
| Format | Untuk | Isi |
|---|---|---|
qr | Kamera ponsel: tautan, nomor verifikasi | Teks apa pun |
code128 | Pemindai kasir dan gudang: SKU, nomor transaksi | Huruf, 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
| Pengaturan | Keterangan |
|---|---|
| Ukuran kertas | A4 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). |
| Orientasi | Portrait atau Landscape. |
| Margin | Atas, kanan, bawah, kiri, masing-masing sendiri. |
| Warna latar | Warna seluruh halaman. |
| Gambar latar | Gambar yang direntangkan memenuhi setiap halaman: kop surat, bingkai sertifikat, watermark. Siapkan gambar dengan rasio yang sama dengan kertasnya. |
Format hasil
| Yang terbawa | HTML | XLSX | XLS | CSV | ESC/POS | TXT | |
|---|---|---|---|---|---|---|---|
| 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. xlsditulis 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-5tidak diubah.
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.
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.
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
| Opsi | Keterangan |
|---|---|
template | Template 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. |
fontBase | Opsional. 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. |
data | Opsional. 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). |
fonts | Opsional. 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. |
simple | Opsional. 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. |
hide | Opsional. 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. |
messages | Opsional. 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
JsonDataSourceatasrows, 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.terbilangmenjadi closure Groovy. Tabel dengan baris dari data dan sub-laporan menjadijr:listatas daftarnya (parameter berisijava.util.ListofMap, 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.jrxmlbuatan 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.jrxmlmilik sub-laporan. - Dari kode:
ToJRXML/FromJRXMLdi Go, format"jrxml"dirender, danPOST /import/jrxml.
Font
Pilihan font di designer:
| Font | Jenis | Asal |
|---|---|---|
| Helvetica, Times, Courier | sans, serif, monospace | bawaan PDF, huruf Latin saja |
| Roboto, Open Sans, Lato, Montserrat | sans | dibundel |
| Lora, Playfair Display | serif | dibundel |
| Great Vibes | tulisan tangan, cocok untuk sertifikat | dibundel (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") // opsionalexport DDREPORT_FONT=/fonts/NotoSans-Regular.ttf
export DDREPORT_FONT_BOLD=/fonts/NotoSans-Bold.ttfJika 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.
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": [] }
}
}
| Field | Default | Keterangan |
|---|---|---|
page.width · height | 595 · 842 | Ukuran halaman dalam point. Tukar keduanya untuk landscape. |
page.marginTop … | 0 | Margin per sisi. Sisi yang tidak diisi memakai page.margin. |
page.background | kosong | Warna latar, #rrggbb. |
page.backgroundImage | kosong | Gambar latar sebagai data URI. |
dataKey | rows | Kunci daftar baris di dalam data. |
groupBy | kosong | Field pengelompokan. |
groupPageBreak | false | Halaman baru untuk setiap kelompok. |
thousands · decimal | , · . | Pemisah untuk {{f:N}}. |
bands.*.height | 0 | Tinggi band. |
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.
| type | Properti | Keterangan |
|---|---|---|
text | text, font, fontSize (10), lineHeight (1.2), bold, italic, underline, align, color, fill, grow | font: Helvetica, Times, Courier, atau nama font bundel / terdaftar. align: left, center, right. fill = highlight. |
table | cells, cols, aligns, border, color, fill, grow, grid, textColor, stripe, header, headerFill, headerColor, footer, footerFill, footerColor, heights, padding, cellStyle, data, valign, dan properti font milik text | cells: teks per sel, baris demi baris. cols: lebar relatif. aligns: rata per kolom. color: warna garis. Sisanya lihat Tabel. |
image | src | Data URI. PDF menerima PNG, JPEG, dan GIF. |
rect | border, color, fill | Tebal garis, warna garis, warna isi (kosong = transparan). |
line | border, color | Mendatar jika w ≥ h, tegak jika sebaliknya. |
chart | chart, label, value, color | chart: bar, hbar, line, pie, donut. value boleh perhitungan. |
barcode | text, format, color | format: 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 / variabel | Keterangan |
|---|---|
-format | pdf (default), html, xlsx, xls, csv, jrxml, escpos (printer struk), txt |
-serve alamat | Menjalankan 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 folder | Bersama -serve: folder tempat My templates disimpan, satu file JSON per template (default ddreport-templates; di Docker /data) |
DDREPORT_TOKEN | Untuk -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 |
-stdio | Terus 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=1 | Laporan 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_ROWS | Laporan 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 alamat | Meminta GET /healthz dari server di alamat itu dan keluar dengan kode 1 bila tidak dijawab ok. Dipakai HEALTHCHECK image Docker, yang tidak punya curl |
-mcp | Server MCP lewat stdin/stdout untuk asisten AI, dengan tool template_guide, check_template, dan render_template. Lihat Template JSON |
DDREPORT_BIN | Lokasi binary, dibaca oleh wrapper Java dan Node.js |
DDREPORT_FONT · _BOLD | Font pengganti untuk seluruh laporan. Lihat Font |
DDREPORT_FONTS | Folder font tambahan (Nama-Regular.ttf, ...). Lihat Font |
DDREPORT_SIGN_CERT · _KEY · _REASON | Sertifikat 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.
| Baris | HTML | XLSX | XLS | CSV | |
|---|---|---|---|---|---|
| 10 000 (±250 halaman) | ±1 dtk | ±0,8 dtk | 0,2 dtk | 0,2 dtk | 0,2 dtk |
| 100 000 (±2 500 halaman) | ±12 dtk, memori puncak ±500 MB | ±9 dtk, ±400 MB | 2,2 dtk, ±220 MB | 1,8 dtk | 1,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_ROWSmembatasi 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
.jrxmlsebagai 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) dantxt: huruf dalam code page Windows-1252, tanpa chart;txttanpa 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=1menolak laporan seperti itu; dari Goddreport.Missing. - Designer: tanpa opsi
fields, menu Field berisi field dari data contoh.
0.4.2 · 30 Okt 2026
- Printer struk: format
escposmencetak langsung ke printer thermal 58 dan 80 mm (teks tebal dan besar, QR, Code 128, logo, potong kertas), dantxtuntuk 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 barisDDREPORT_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-fontsdipakai 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,-servehanya melayanilocalhostdan 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_TOKENmewajibkanAuthorization: Bearer <token>, dan-servememberi peringatan bila alamatnya terbuka ke jaringan tanpa token. - Warna dari data:
color,fill, warna tabel, dancellStyleboleh 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) danllms.txtuntuk asisten AI. Template dari PHP yang menulis objek kosong sebagai[]tidak lagi ditolak.
0.2.1 · 22 Okt 2026
- Keamanan: binary
ddreportdibangun 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
hidedanmessages,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) dancreateRenderer(Node.js) yang menjaga proses tetap berjalan. Lihat Memanggil dari backend. - Perbaikan dan keamanan: legenda pie tanpa irisan nol, pesan jelas untuk gambar rusak,
-servemenolak 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
ddreportdanddreport-designerpertama 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:
renderAsyncdi Node.js, batas waktu satu menit di Java dan Node.js, dan-serveyang mencetak paling banyak satu laporan per CPU sekaligus. - Pengujian: CI menjalankan test di Linux dan Windows, serta test designer di Chromium, Firefox, dan WebKit.