Bill Maker Online – Free Invoice Bill Generator
Invoice Bill Maker
Online work, Photoshop/photo work, lamination, print and custom service bills.
5in x 7in Canvas
Dynamic Bill File
Preview updates automatically. Canvas size is 5 inch width x 7 inch height.

' : 'Logo';
if (state.logoData) {
var image = new Image();
image.onload = function () {
state.logoImage = image;
renderCanvas();
};
image.src = state.logoData;
}
renderCanvas();
}
function addItem(name, qty, rate) {
state.items.push({
name: name || '',
qty: qty || 1,
rate: rate || 0
});
renderItems();
renderCanvas();
}
function removeItem(index) {
state.items.splice(index, 1);
if (!state.items.length) {
addItem('Online Form Work', 1, 50);
return;
}
renderItems();
renderCanvas();
}
function renderItems() {
itemsWrap.innerHTML = '';
state.items.forEach(function (item, index) {
var row = document.createElement('div');
row.className = 'bill-item-row';
row.innerHTML =
'
' +
'
' +
'
' +
'
';
var inputs = row.querySelectorAll('input');
inputs[0].addEventListener('input', function () { item.name = this.value; renderCanvas(); });
inputs[1].addEventListener('input', function () { item.qty = Number(this.value); renderCanvas(); });
inputs[2].addEventListener('input', function () { item.rate = Number(this.value); renderCanvas(); });
row.querySelector('button').addEventListener('click', function () { removeItem(index); });
itemsWrap.appendChild(row);
});
}
function billData() {
var subtotal = state.items.reduce(function (sum, item) {
return sum + (Number(item.qty || 0) * Number(item.rate || 0));
}, 0);
var discount = Number(fields.discount.value || 0);
var taxable = Math.max(subtotal - discount, 0);
var taxPercent = Number(fields.taxPercent.value || 0);
var taxAmount = taxable * taxPercent / 100;
var total = taxable + taxAmount;
var paid = Number(fields.paidAmount.value || 0);
return {
id: state.selectedId || String(Date.now()),
shopName: fields.shopName.value || shop.name,
shopPhone: fields.shopPhone.value || '',
shopAddress: fields.shopAddress.value || '',
logoData: state.logoData,
billNo: fields.billNo.value || nextBillNo(),
date: fields.billDate.value || today(),
template: fields.billTemplate.value || 'classic',
customerName: fields.customerName.value || 'Walk-in Customer',
customerAddress: fields.customerAddress.value || '',
customerMobile: fields.customerMobile.value || '',
discount: discount,
taxPercent: taxPercent,
taxAmount: taxAmount,
paid: paid,
paymentMode: fields.paymentMode.value || 'Cash',
subtotal: subtotal,
total: total,
balance: Math.max(total - paid, 0),
note: fields.billNote.value || 'Thank you for your business.',
items: JSON.parse(JSON.stringify(state.items))
};
}
function palette(template) {
if (state.printMode === 'bw') {
return { brand: '#111111', accent: '#333333', soft: '#ffffff', line: '#111111', ink: '#111111', muted: '#333333', tableAlt: '#f7f7f7', footer: '#111111' };
}
var palettes = {
classic: {brand:'#8b3a1b', accent:'#f97316', soft:'#fff7ed', line:'#dbe3ee', ink:'#111827', muted:'#64748b', tableAlt:'#f8fafc', footer:'#8b3a1b'},
modern: {brand:'#0f766e', accent:'#14b8a6', soft:'#ecfdf5', line:'#ccfbf1', ink:'#102a43', muted:'#486581', tableAlt:'#f0fdfa', footer:'#0f766e'},
compact: {brand:'#1d4ed8', accent:'#60a5fa', soft:'#eff6ff', line:'#bfdbfe', ink:'#172554', muted:'#475569', tableAlt:'#f8fafc', footer:'#1d4ed8'},
boxed: {brand:'#7c2d12', accent:'#f59e0b', soft:'#fffbeb', line:'#fcd34d', ink:'#1f2937', muted:'#6b7280', tableAlt:'#fffbeb', footer:'#7c2d12'},
minimal: {brand:'#111827', accent:'#475569', soft:'#f8fafc', line:'#cbd5e1', ink:'#111827', muted:'#64748b', tableAlt:'#ffffff', footer:'#111827'},
premium: {brand:'#92400e', accent:'#f59e0b', soft:'#fffbeb', line:'#fcd34d', ink:'#1f2937', muted:'#6b7280', tableAlt:'#fff7ed', footer:'#92400e'},
blueclean: {brand:'#075985', accent:'#38bdf8', soft:'#e0f2fe', line:'#bae6fd', ink:'#0f172a', muted:'#475569', tableAlt:'#f0f9ff', footer:'#075985'},
retail: {brand:'#166534', accent:'#22c55e', soft:'#dcfce7', line:'#bbf7d0', ink:'#102a1d', muted:'#4b6353', tableAlt:'#f0fdf4', footer:'#166534'},
service: {brand:'#6d28d9', accent:'#a78bfa', soft:'#f3e8ff', line:'#ddd6fe', ink:'#20113f', muted:'#6b5b84', tableAlt:'#faf5ff', footer:'#6d28d9'},
receipt: {brand:'#334155', accent:'#64748b', soft:'#f8fafc', line:'#cbd5e1', ink:'#111827', muted:'#475569', tableAlt:'#ffffff', footer:'#334155'}
};
return palettes[template] || palettes.classic;
}
function drawText(text, x, y, maxWidth, lineHeight, size, weight, align, color) {
ctx.font = (weight || '400') + ' ' + size + 'px Arial';
ctx.textAlign = align || 'left';
ctx.fillStyle = color || '#111827';
var words = String(text || '').split(' ');
var line = '';
var lines = [];
words.forEach(function (word) {
var test = line ? line + ' ' + word : word;
if (ctx.measureText(test).width > maxWidth && line) {
lines.push(line);
line = word;
} else {
line = test;
}
});
lines.push(line);
lines.forEach(function (lineText, index) {
ctx.fillText(lineText, x, y + (index * lineHeight));
});
return y + (lines.length * lineHeight);
}
function renderCanvas() {
var bill = billData();
var colors = palette(bill.template);
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = '#ffffff';
ctx.fillRect(0, 0, canvas.width, canvas.height);
if (state.logoImage) {
ctx.drawImage(state.logoImage, 90, 80, 92, 92);
} else {
ctx.fillStyle = colors.soft;
ctx.fillRect(90, 80, 92, 92);
ctx.strokeStyle = colors.line;
ctx.lineWidth = 2;
ctx.strokeRect(90, 80, 92, 92);
drawText('V', 136, 142, 92, 40, 52, '800', 'center', colors.brand);
}
drawText(bill.shopName, 210, 128, 700, 48, 44, '800', 'left', colors.ink);
var nextY = drawText(bill.shopAddress, 210, 172, 700, 26, 22, '400', 'left', colors.muted);
drawText('Invoice / Bill', 1410, 132, 420, 50, 42, '800', 'right', colors.ink);
drawText((bill.shopPhone ? 'Mobile: ' + bill.shopPhone + ' | ' : '') + shop.portal, 210, Math.max(210, nextY + 6), 830, 26, 22, '600', 'left', colors.muted);
ctx.fillStyle = '#ffffff';
ctx.fillRect(1020, 240, 390, 90);
ctx.strokeStyle = colors.line;
ctx.lineWidth = 2;
ctx.strokeRect(1020, 240, 390, 90);
drawText(bill.billNo, 1048, 280, 330, 30, 28, '800', 'left', colors.brand);
drawText('Date: ' + bill.date, 1048, 318, 330, 28, 24, '600', 'left', colors.ink);
ctx.strokeStyle = colors.line;
ctx.lineWidth = 3;
ctx.beginPath();
ctx.moveTo(90, 360);
ctx.lineTo(1410, 360);
ctx.stroke();
drawText('Bill To', 90, 410, 400, 34, 30, '800', 'left', colors.brand);
var custNextY = drawText(bill.customerName, 90, 456, 760, 32, 28, '700', 'left', colors.ink);
if (bill.customerAddress) {
drawText(bill.customerAddress, 90, custNextY + 6, 760, 26, 22, '400', 'left', colors.muted);
}
var tableY = 530;
ctx.fillStyle = colors.brand;
ctx.fillRect(90, tableY, 1320, 62);
drawText('Item', 120, tableY + 40, 500, 26, 24, '800', 'left', '#ffffff');
drawText('Qty', 950, tableY + 40, 90, 26, 24, '800', 'right', '#ffffff');
drawText('Rate', 1150, tableY + 40, 150, 26, 24, '800', 'right', '#ffffff');
drawText('Amount', 1380, tableY + 40, 180, 26, 24, '800', 'right', '#ffffff');
var y = tableY + 98;
bill.items.forEach(function (item, index) {
if (index % 2 === 0) {
ctx.fillStyle = colors.tableAlt;
ctx.fillRect(90, y - 34, 1320, 58);
}
drawText(item.name, 120, y, 700, 28, 24, '600', 'left', colors.ink);
drawText(item.qty, 950, y, 90, 28, 24, '600', 'right', colors.ink);
drawText(money(item.rate), 1150, y, 150, 28, 24, '600', 'right', colors.ink);
drawText(money(item.qty * item.rate), 1380, y, 180, 28, 24, '700', 'right', colors.ink);
y += 70;
});
ctx.strokeStyle = colors.line;
ctx.lineWidth = 3;
ctx.beginPath();
ctx.moveTo(90, 1450);
ctx.lineTo(1410, 1450);
ctx.stroke();
drawText('Note', 90, 1510, 400, 34, 28, '800', 'left', colors.brand);
drawText(bill.note, 90, 1560, 720, 32, 24, '400', 'left', colors.ink);
var totalX = 900;
var totalY = 1500;
drawText('Subtotal', totalX, totalY, 260, 34, 26, '600', 'left', colors.ink);
drawText('Rs. ' + money(bill.subtotal), 1380, totalY, 240, 34, 26, '700', 'right', colors.ink);
drawText('Amount', totalX, totalY + 46, 260, 34, 26, '600', 'left', colors.ink);
drawText('Rs. ' + money(bill.subtotal), 1380, totalY + 46, 240, 34, 26, '700', 'right', colors.ink);
drawText('Discount', totalX, totalY + 92, 260, 34, 26, '600', 'left', colors.ink);
drawText('Rs. ' + money(bill.discount), 1380, totalY + 92, 240, 34, 26, '700', 'right', colors.ink);
drawText('Tax (' + cleanPercent(bill.taxPercent) + '%)', totalX, totalY + 138, 260, 34, 26, '600', 'left', colors.ink);
drawText('Rs. ' + money(bill.taxAmount), 1380, totalY + 138, 240, 34, 26, '700', 'right', colors.ink);
ctx.fillStyle = colors.soft;
ctx.fillRect(870, totalY + 172, 540, 72);
drawText('Grand Total', totalX, totalY + 220, 260, 34, 30, '800', 'left', colors.brand);
drawText('Rs. ' + money(bill.total), 1380, totalY + 220, 250, 34, 30, '800', 'right', colors.brand);
drawText('Paid', totalX, totalY + 280, 220, 34, 25, '600', 'left', colors.ink);
drawText('Rs. ' + money(bill.paid), 1380, totalY + 280, 240, 34, 25, '700', 'right', colors.ink);
drawText('Balance', totalX, totalY + 322, 220, 34, 25, '600', 'left', colors.ink);
drawText('Rs. ' + money(bill.balance), 1380, totalY + 322, 240, 34, 25, '700', 'right', colors.ink);
drawText('Payment Mode', totalX, totalY + 364, 240, 34, 24, '600', 'left', colors.ink);
drawText(bill.paymentMode, 1380, totalY + 364, 240, 34, 24, '700', 'right', colors.ink);
drawText('Generated by ' + shop.portal, 90, 1990, 700, 30, 22, '400', 'left', colors.muted);
drawText('Customer Sign', 1380, 1990, 260, 30, 22, '600', 'right', colors.ink);
ctx.strokeStyle = colors.ink;
ctx.lineWidth = 2;
ctx.beginPath();
ctx.moveTo(1140, 1945);
ctx.lineTo(1410, 1945);
ctx.stroke();
}
function resetBill() {
state.selectedId = null;
fields.billNo.value = nextBillNo();
fields.billDate.value = today();
fields.customerName.value = 'Walk-in Customer';
fields.customerAddress.value = '';
fields.customerMobile.value = '';
fields.discount.value = 0;
fields.taxPercent.value = 0;
fields.paidAmount.value = 0;
fields.paymentMode.value = 'Cash';
fields.billNote.value = 'Thank you for your business.';
state.items = [{ name: "Online Form Work", qty: 1, rate: 50 }];
renderItems();
renderCanvas();
}
Object.keys(fields).forEach(function (key) {
if (fields[key] && fields[key].type !== 'file') {
fields[key].addEventListener('input', renderCanvas);
fields[key].addEventListener('change', renderCanvas);
}
});
fields.shopLogo.addEventListener('change', function (e) {
var file = e.target.files[0];
if (file) {
var reader = new FileReader();
reader.onload = function (event) { setLogo(event.target.result); };
reader.readAsDataURL(file);
}
});
document.querySelectorAll('.bill-mode-btn').forEach(function (button) {
button.addEventListener('click', function () {
state.printMode = this.dataset.mode;
document.querySelectorAll('.bill-mode-btn').forEach(function (b) { b.classList.remove('active'); });
this.classList.add('active');
renderCanvas();
});
});
document.getElementById('addItem').addEventListener('click', function () { addItem('', 1, 0); });
document.querySelectorAll('.bill-quick').forEach(function (button) {
button.addEventListener('click', function () {
addItem(this.dataset.name, 1, Number(this.dataset.rate));
});
});
function prepareA4PrintSheet(callback) {
var printSheet = document.getElementById('billPrintSheet');
if (!printSheet) {
if (callback) callback();
return;
}
var imageData = canvas.toDataURL('image/png');
printSheet.innerHTML = '';
var copy = document.createElement('div');
var image = document.createElement('img');
copy.className = 'bill-print-copy';
image.alt = 'Bill copy';
image.onload = function () {
setTimeout(function () {
if (callback) callback();
}, 80);
};
image.src = imageData;
copy.appendChild(image);
printSheet.appendChild(copy);
}
function printWithMode(mode) {
var previousMode = state.printMode;
state.printMode = mode;
renderCanvas();
prepareA4PrintSheet(function () {
window.print();
state.printMode = previousMode;
renderCanvas();
});
}
document.getElementById('printColorBill').addEventListener('click', function () {
printWithMode('color');
});
document.getElementById('printBwBill').addEventListener('click', function () {
printWithMode('bw');
});
document.getElementById('newBill').addEventListener('click', resetBill);
document.getElementById('downloadBill').addEventListener('click', function () {
var link = document.createElement('a');
link.href = canvas.toDataURL('image/png');
link.download = (fields.billNo.value || 'invoice') + '.png';
link.click();
});
renderItems();
resetBill();
});