You built an HTML template that looks perfect in the browser. Then you generate a PDF — and every Japanese character turns into an empty box (tofu). The invoice is unreadable. The report is broken.
This is one of the most common issues developers face when generating Japanese PDFs from HTML. The fix is straightforward once you understand why it happens.
This guide covers everything you need to render Japanese text correctly in PDF output: how fonts work in PDF engines, how to load Google Fonts (Noto Sans JP), CSS best practices, how FUNBREW PDF handles Japanese fonts, common problems and their solutions, font subsetting for performance, and a complete Japanese invoice template.
Why Japanese Characters Show as Tofu in PDFs
Understanding the mechanism makes debugging much easier.
The Server-Side Font Problem
When a browser renders HTML, it uses fonts installed on the user's operating system. macOS has Hiragino Sans. Windows has Yu Gothic. Chromium on macOS renders Japanese text beautifully because those fonts exist locally.
But when a PDF engine runs on a Linux server, those OS fonts are not installed. The CSS declaration below looks fine locally — but breaks on a server:
body {
font-family: 'Hiragino Sans', 'Yu Gothic', sans-serif;
}
On the server:
Hiragino Sans— not foundYu Gothic— not foundsans-seriffallback — resolves to DejaVu Sans or FreeSerif, which have no Japanese glyphs- Result: every Japanese character renders as □
PDF Engine Comparison
| Engine | Japanese Font Handling |
|---|---|
| Chromium headless | Uses OS fonts. Japanese breaks if fonts not installed on server |
| wkhtmltopdf | WebKit engine. Same OS font dependency |
| FUNBREW PDF | Noto Sans JP pre-installed. Japanese works without configuration |
FUNBREW PDF's quality engine has Noto Sans JP bundled at the server level, so Japanese text renders correctly without any extra setup. If you're running Puppeteer or wkhtmltopdf yourself, follow the steps below to configure fonts explicitly.
Google Fonts: Loading Noto Sans JP for PDF
Noto Sans JP from Google Fonts is the most widely used Japanese font for PDF generation. It covers hiragana, katakana, kanji (CJK Unified Ideographs), punctuation, and common symbols. It's open source (OFL license) and comes in 9 weights.
Load via HTML <link> Tag
The simplest approach is embedding Google Fonts in the HTML <head>:
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Noto+Sans+JP:wght@400;700&display=swap" rel="stylesheet">
<style>
body {
font-family: 'Noto Sans JP', sans-serif;
}
</style>
</head>
<body>
<p>日本語テキストが正しく表示されます。Japanese text renders correctly.</p>
</body>
</html>
Note: this requires the PDF engine server to reach fonts.googleapis.com. If the server has network restrictions, use self-hosted fonts instead (covered below).
Load via CSS @import
For CSS-only setups:
@import url('https://fonts.googleapis.com/css2?family=Noto+Sans+JP:wght@300;400;500;700;900&display=swap');
body {
font-family: 'Noto Sans JP', sans-serif;
font-weight: 400;
}
h1, h2, h3 {
font-weight: 700;
}
.light {
font-weight: 300;
}
Available Japanese Google Fonts
| Font | Style | Best For |
|---|---|---|
| Noto Sans JP | Gothic / sans-serif | General documents, invoices, reports |
| Noto Serif JP | Mincho / serif | Contracts, formal documents |
| BIZ UDPGothic | UD gothic | Government docs, accessibility-first design |
| BIZ UDPMincho | UD mincho | Formal government-style documents |
| M PLUS 1p | Modern gothic | Design-forward documents |
| Kosugi Maru | Rounded gothic | Friendly, approachable tone |
CSS Best Practices for Japanese PDFs
Building the Font Stack
Japanese text is often paired with a Latin font for numbers and Roman characters. List the Latin font first, then the Japanese font as fallback:
body {
/* Latin: Inter → Japanese: Noto Sans JP → generic fallback */
font-family: 'Inter', 'Noto Sans JP', sans-serif;
font-size: 14px;
line-height: 1.7;
color: #1a1a1a;
}
h1 {
font-family: 'Noto Sans JP', sans-serif;
font-weight: 700;
font-size: 24px;
letter-spacing: -0.02em;
}
/* Monospace for code, invoice numbers, amounts */
code, .invoice-number, .amount {
font-family: 'JetBrains Mono', 'Noto Sans Mono', monospace;
}
When the browser renders a character, it uses the first font in the stack that has a glyph for that character. Latin characters come from Inter; Japanese characters fall through to Noto Sans JP. This gives you sharp Latin typography alongside proper Japanese rendering.
Always Specify font-weight Explicitly
This is the most common mistake. Google Fonts only loads the weights you specify in the URL. If you request only weight 400 but use font-weight: 700 in your CSS, the browser synthesizes a bold — and PDF engines often render synthesized bold poorly or not at all:
<!-- BAD: Only 400 loaded. font-weight: 700 will not render correctly -->
<link href="https://fonts.googleapis.com/css2?family=Noto+Sans+JP&display=swap" rel="stylesheet">
<!-- GOOD: List every weight you use -->
<link href="https://fonts.googleapis.com/css2?family=Noto+Sans+JP:wght@300;400;500;700;900&display=swap" rel="stylesheet">
Self-Hosted Fonts with @font-face
If you can't use Google Fonts CDN (security restrictions, air-gapped environments, reliability requirements), host the font files yourself:
@font-face {
font-family: 'NotoSansJP';
src: url('/fonts/NotoSansJP-Regular.woff2') format('woff2'),
url('/fonts/NotoSansJP-Regular.woff') format('woff');
font-weight: 400;
font-style: normal;
font-display: swap;
}
@font-face {
font-family: 'NotoSansJP';
src: url('/fonts/NotoSansJP-Bold.woff2') format('woff2'),
url('/fonts/NotoSansJP-Bold.woff') format('woff');
font-weight: 700;
font-style: normal;
font-display: swap;
}
body {
font-family: 'NotoSansJP', sans-serif;
}
Download font files from Google's google/fonts GitHub repository or the Noto fonts website.
Base64 Font Embedding: data:font/woff2;base64
For air-gapped environments, secure servers with no outbound internet access, or any situation where you need a completely self-contained HTML document, you can embed the font directly into your CSS using a Base64-encoded data: URI. This is the technique developers search for as data:font/woff2;base64 noto sans jp.
With Base64 embedding, the font data lives inside the HTML string itself. No external requests are made — making Japanese text rendering 100% deterministic regardless of network conditions.
How It Works
@font-face {
font-family: 'NotoSansJP';
/* Replace the placeholder below with the actual Base64 string */
src: url('data:font/woff2;base64,d09GMgABAAA...(Base64 string)...') format('woff2');
font-weight: 400;
font-style: normal;
}
body {
font-family: 'NotoSansJP', sans-serif;
}
The data:font/woff2;base64, prefix is followed immediately by the Base64-encoded contents of the .woff2 file. For Noto Sans JP Regular, this is typically around 2MB of Base64 characters embedded in the CSS — but it eliminates all network round trips.
Converting a .woff2 File to Base64
Command line (Linux / macOS):
# Standard base64 (GNU coreutils)
base64 -w 0 NotoSansJP-Regular.woff2 > NotoSansJP-Regular.b64.txt
# macOS without GNU coreutils
base64 < NotoSansJP-Regular.woff2 | tr -d '\n' > NotoSansJP-Regular.b64.txt
Node.js:
const fs = require('fs');
const woff2Buffer = fs.readFileSync('./NotoSansJP-Regular.woff2');
const base64String = woff2Buffer.toString('base64');
const fontFaceCSS = `@font-face {
font-family: 'NotoSansJP';
src: url('data:font/woff2;base64,${base64String}') format('woff2');
font-weight: 400;
font-style: normal;
}`;
fs.writeFileSync('./font-face-embedded.css', fontFaceCSS);
console.log('Base64 CSS written. Length:', fontFaceCSS.length);
Python:
import base64
with open('NotoSansJP-Regular.woff2', 'rb') as f:
woff2_bytes = f.read()
base64_str = base64.b64encode(woff2_bytes).decode('utf-8')
font_face_css = f"""@font-face {{
font-family: 'NotoSansJP';
src: url('data:font/woff2;base64,{base64_str}') format('woff2');
font-weight: 400;
font-style: normal;
}}"""
with open('font-face-embedded.css', 'w') as f:
f.write(font_face_css)
print(f'Base64 CSS written. Characters: {len(base64_str):,}')
Complete Example: Base64 Font with FUNBREW PDF API
Here is a full working example sending an HTML document with an embedded Base64 Noto Sans JP font to the FUNBREW PDF API:
const fs = require('fs');
// Load and encode both weights
const regularBase64 = fs.readFileSync('./NotoSansJP-Regular.woff2').toString('base64');
const boldBase64 = fs.readFileSync('./NotoSansJP-Bold.woff2').toString('base64');
const html = `<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<style>
@font-face {
font-family: 'NotoSansJP';
src: url('data:font/woff2;base64,${regularBase64}') format('woff2');
font-weight: 400;
font-style: normal;
}
@font-face {
font-family: 'NotoSansJP';
src: url('data:font/woff2;base64,${boldBase64}') format('woff2');
font-weight: 700;
font-style: normal;
}
body {
font-family: 'NotoSansJP', sans-serif;
font-size: 14px;
line-height: 1.7;
margin: 40px;
}
h1 { font-weight: 700; font-size: 24px; margin-bottom: 16px; }
</style>
</head>
<body>
<h1>Zero-Dependency Japanese PDF</h1>
<p>This PDF was generated with a Base64-embedded Noto Sans JP font.</p>
<p>ひらがな・カタカナ・漢字・記号(!?…①②③)が完全対応。</p>
</body>
</html>`;
const response = await fetch('https://pdf.funbrew.cloud/api/pdf/generate', {
method: 'POST',
headers: {
'Authorization': 'Bearer sk-your-api-key',
'Content-Type': 'application/json',
},
body: JSON.stringify({
html,
options: {
engine: 'quality',
format: 'A4',
// waitForFonts is not needed — Base64 fonts are inline, no network request
},
}),
});
const result = await response.json();
console.log('PDF URL:', result.data.download_url);
Comparison: Base64 Embedding vs. Other Methods
| Method | Network Required | HTML Size Impact | Reliability | Recommended When |
|---|---|---|---|---|
| FUNBREW PDF pre-installed | None | No change | Highest | Using FUNBREW PDF (default) |
data:font/woff2;base64 |
None | +2–5 MB per weight | Very High | Air-gapped / self-hosted environments |
| Self-hosted CDN (woff2) | Yes | No change | High | Stable CDN available |
| Google Fonts CDN | Yes | No change | Network-dependent | Development / prototyping |
Note for FUNBREW PDF users: Noto Sans JP is pre-installed on FUNBREW PDF servers, so Base64 embedding is unnecessary when using the managed API. Base64 embedding is the right solution when you self-host Puppeteer or wkhtmltopdf in a network-restricted environment, or when you need a single portable HTML file that works anywhere.
Japanese Fonts in FUNBREW PDF
FUNBREW PDF runs on a Chromium-based engine with Noto Sans JP, Noto Serif JP, and BIZ UDPGothic pre-installed at the server level. You get reliable Japanese rendering without external network calls.
Using Pre-installed Fonts (Recommended)
Just reference the font name in your CSS — no loading required:
// JavaScript (Node.js)
const response = await fetch('https://pdf.funbrew.cloud/api/pdf/generate', {
method: 'POST',
headers: {
'Authorization': 'Bearer sk-your-api-key',
'Content-Type': 'application/json',
},
body: JSON.stringify({
html: `
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<style>
body {
font-family: 'Noto Sans JP', sans-serif;
font-size: 14px;
line-height: 1.7;
}
h1 { font-weight: 700; }
</style>
</head>
<body>
<h1>日本語タイトル</h1>
<p>ひらがな、カタカナ、漢字がすべて正しく表示されます。</p>
<p>Numbers: 1,234,567 | Symbols: ①②③ ㎡ ㈱</p>
</body>
</html>
`,
options: {
engine: 'quality',
format: 'A4',
},
}),
});
const result = await response.json();
console.log('PDF URL:', result.data.download_url);
Using Google Fonts with FUNBREW PDF
If you need a specific font not pre-installed, enable waitForFonts to ensure the font finishes loading before PDF rendering begins:
const response = await fetch('https://pdf.funbrew.cloud/api/pdf/generate', {
method: 'POST',
headers: {
'Authorization': 'Bearer sk-your-api-key',
'Content-Type': 'application/json',
},
body: JSON.stringify({
html: `
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=M+PLUS+1p:wght@400;700&display=swap" rel="stylesheet">
<style>
body { font-family: 'M PLUS 1p', sans-serif; }
</style>
</head>
<body>
<p>カスタムフォントのテスト</p>
</body>
</html>
`,
options: {
engine: 'quality',
waitForFonts: true,
},
}),
});
Python Example
import requests
html_content = """
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<style>
body {
font-family: 'Noto Sans JP', sans-serif;
margin: 40px;
color: #1a1a1a;
}
.title {
font-size: 24px;
font-weight: 700;
margin-bottom: 16px;
}
.body-text {
font-size: 14px;
line-height: 1.8;
}
</style>
</head>
<body>
<div class="title">日本語PDF生成テスト</div>
<div class="body-text">
<p>FUNBREW PDF APIを使用して生成されたPDFです。</p>
<p>ひらがな・カタカナ・漢字・記号(!?…)が正しく表示されます。</p>
</div>
</body>
</html>
"""
response = requests.post(
'https://pdf.funbrew.cloud/api/pdf/generate',
headers={'Authorization': 'Bearer sk-your-api-key'},
json={
'html': html_content,
'options': {
'engine': 'quality',
'format': 'A4',
},
},
)
print('PDF URL:', response.json()['data']['download_url'])
For the full API reference, see the API documentation.
Common Problems and Solutions
Problem 1: Bold Text Not Working
Symptom: font-weight: 700 or font-weight: bold has no visible effect in the PDF.
Cause: The bold weight font file was never loaded.
Solution: List all weights you need in the Google Fonts URL:
<!-- Before: Only weight 400 loaded -->
<link href="https://fonts.googleapis.com/css2?family=Noto+Sans+JP&display=swap" rel="stylesheet">
<!-- After: Explicit weight list -->
<link href="https://fonts.googleapis.com/css2?family=Noto+Sans+JP:wght@400;500;700;900&display=swap" rel="stylesheet">
Then confirm your CSS actually references those weights:
h1 { font-weight: 700; }
.light { font-weight: 300; } /* Will only work if 300 is in the URL */
Problem 2: Specific Characters Still Showing as Tofu
Symptom: Regular hiragana and kanji render fine, but special symbols (①②③, ㎡, ㈱, NEC-defined characters) or emoji appear as □.
Cause: The chosen font doesn't contain glyphs for those specific code points.
Solution: Add Noto Color Emoji for emoji, or expand the font stack:
body {
/* Noto Color Emoji handles emoji; Noto Sans JP handles Japanese text */
font-family: 'Noto Sans JP', 'Noto Color Emoji', sans-serif;
}
For specific problem characters, target them with a separate class:
.special-chars {
font-family: 'Noto Color Emoji', 'Twemoji Mozilla', sans-serif;
}
NEC special characters (NEC拡張文字) and IBM extension characters require fonts that include those code points — consider Noto Sans JP which has broad CJK coverage.
Problem 3: Line Spacing or Character Spacing Looks Wrong
Symptom: Japanese text looks cramped or overly spaced.
Cause: CSS defaults are tuned for Latin text. Japanese text benefits from slightly different values.
Solution: Explicitly set typography values for Japanese:
body {
font-family: 'Noto Sans JP', sans-serif;
line-height: 1.7; /* Japanese body text: 1.6–1.8 */
letter-spacing: 0.04em; /* Slight tracking for readability */
word-break: break-all; /* Break long strings (URLs, numbers) */
overflow-wrap: break-word;
}
h1, h2 {
line-height: 1.4; /* Tighter line height for headings */
letter-spacing: -0.02em;
}
p {
text-align: justify;
text-justify: inter-ideograph; /* Improve justification for CJK */
}
Problem 4: Vertical Text (縦書き) Not Rendering
Symptom: writing-mode: vertical-rl is set but text appears horizontal, or the layout breaks.
Cause: Vertical writing mode support varies by engine. wkhtmltopdf has incomplete support.
Solution: Use FUNBREW PDF's quality mode (Chromium-based), which fully supports vertical writing:
.vertical-text {
writing-mode: vertical-rl; /* Right-to-left vertical */
text-orientation: mixed; /* Rotate Latin characters 90° */
height: 300px;
font-family: 'Noto Sans JP', serif;
}
.vertical-upright {
writing-mode: vertical-rl;
text-orientation: upright; /* Latin characters upright in vertical flow */
}
<div class="vertical-text">
<p>縦書きのサンプルテキスト。<br>日本語が縦方向に表示されます。</p>
</div>
For a detailed engine comparison, see wkhtmltopdf vs Chromium.
Problem 5: Intermittent Font Load Failures
Symptom: PDFs usually render correctly, but occasionally Japanese characters are missing.
Cause: Google Fonts CDN request timed out during PDF generation. The engine rendered without waiting for the font.
Solution A: Self-host fonts for guaranteed availability:
@font-face {
font-family: 'NotoSansJP';
src: url('https://your-cdn.example.com/fonts/NotoSansJP-Regular.woff2') format('woff2');
font-weight: 400;
}
Solution B: Use FUNBREW PDF's pre-installed fonts:
/* No network request needed. Most reliable approach. */
body {
font-family: 'Noto Sans JP', sans-serif;
}
FUNBREW PDF's pre-installed fonts eliminate all font-loading network latency, making Japanese PDF generation both faster and more reliable.
Font Subsetting and Performance
Japanese font files are large — Noto Sans JP's full version with all glyphs can reach 2MB–5MB per weight. At scale, font loading time becomes a bottleneck.
Google Fonts Automatic Subsetting
Google Fonts supports a text= parameter to load only glyphs for specific characters. For Japanese PDFs where the content isn't known in advance, this generally isn't practical:
<!-- Only useful for fixed text like UI labels -->
<link href="https://fonts.googleapis.com/css2?family=Noto+Sans+JP&text=申請書類"
rel="stylesheet">
CSS unicode-range to Limit Glyph Loading
Use unicode-range to split font loading by character range. This is useful when you want a separate Latin font (Inter, etc.) for numbers and a Japanese font for CJK:
@font-face {
font-family: 'NotoSansJP';
src: url('/fonts/NotoSansJP-Regular.woff2') format('woff2');
font-weight: 400;
/* Hiragana + Katakana + punctuation + basic CJK */
unicode-range:
U+3000-303F, /* Japanese punctuation */
U+3040-309F, /* Hiragana */
U+30A0-30FF, /* Katakana */
U+4E00-9FFF; /* CJK Unified Ideographs (basic) */
}
@font-face {
font-family: 'Inter';
src: url('/fonts/Inter-Regular.woff2') format('woff2');
font-weight: 400;
unicode-range: U+0000-00FF, U+0100-024F; /* Latin */
}
body {
/* Inter handles Latin; NotoSansJP handles Japanese automatically */
font-family: 'Inter', 'NotoSansJP', sans-serif;
}
Font Loading Performance Comparison
| Font Source | Added Latency | Reliability |
|---|---|---|
| Pre-installed (FUNBREW PDF) | 0ms | Highest |
| Self-hosted CDN (woff2) | 50–150ms | High |
| Google Fonts CDN | 100–400ms | Network-dependent |
For high-volume PDF generation, pre-installed fonts are significantly faster. See the PDF API production guide for broader performance optimization strategies.
Font Embedding in the Output PDF
Chromium-based engines embed fonts in the generated PDF by default. This is the correct behavior — without embedding, the PDF relies on the viewer's installed fonts, which recreates the same problem on the reader's machine:
// Puppeteer: font embedding is on by default
const pdf = await page.pdf({
format: 'A4',
// No special flag needed — Chromium embeds fonts automatically
});
Do not disable font embedding. The slight increase in file size is worth the guaranteed rendering consistency.
Complete Example: Japanese Invoice Template
Here's a production-ready Japanese invoice template incorporating all the font best practices above.
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<!-- Load Noto Sans JP with all weights we'll use: 400 (body) and 700 (headings) -->
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Noto+Sans+JP:wght@400;700&display=swap" rel="stylesheet">
<style>
/* ===== Reset & Base ===== */
* {
box-sizing: border-box;
margin: 0;
padding: 0;
}
body {
font-family: 'Noto Sans JP', sans-serif;
font-size: 13px;
line-height: 1.7;
color: #1a1a1a;
background: #ffffff;
}
/* ===== Print / PDF Settings ===== */
@page {
size: A4;
margin: 15mm 20mm;
}
@media print {
body { font-size: 11pt; }
}
/* ===== Layout ===== */
.page {
max-width: 210mm;
margin: 0 auto;
padding: 20px;
}
/* ===== Header ===== */
.invoice-header {
display: flex;
justify-content: space-between;
align-items: flex-start;
margin-bottom: 40px;
padding-bottom: 20px;
border-bottom: 3px solid #1a56db;
}
.invoice-title {
font-size: 32px;
font-weight: 700;
color: #1a56db;
letter-spacing: 0.1em;
}
.invoice-meta {
text-align: right;
font-size: 12px;
color: #6b7280;
line-height: 1.8;
}
.invoice-meta strong {
color: #1a1a1a;
font-weight: 700;
font-size: 14px;
}
/* ===== Parties ===== */
.parties {
display: flex;
justify-content: space-between;
margin-bottom: 32px;
}
.to-section .company-name {
font-size: 20px;
font-weight: 700;
margin-bottom: 4px;
}
.from-section {
text-align: right;
font-size: 12px;
color: #6b7280;
line-height: 1.8;
}
.from-section .company-name {
font-size: 16px;
font-weight: 700;
color: #1a1a1a;
}
/* ===== Total Highlight ===== */
.total-highlight {
background: #eff6ff;
border: 2px solid #1a56db;
border-radius: 8px;
padding: 16px 24px;
margin-bottom: 32px;
display: flex;
align-items: center;
justify-content: space-between;
}
.total-label {
font-size: 16px;
font-weight: 700;
color: #1e40af;
}
.total-amount {
font-size: 28px;
font-weight: 700;
color: #1e40af;
letter-spacing: -0.02em;
}
/* ===== Line Items Table ===== */
.line-items {
width: 100%;
border-collapse: collapse;
margin-bottom: 24px;
}
.line-items thead th {
background: #1a56db;
color: #ffffff;
font-weight: 700;
padding: 10px 12px;
text-align: left;
font-size: 12px;
}
/* Prevent table rows from splitting across pages */
.line-items tbody tr {
break-inside: avoid;
page-break-inside: avoid;
}
/* Repeat header on every page for long tables */
thead { display: table-header-group; }
tfoot { display: table-footer-group; }
.line-items tbody tr:nth-child(even) {
background: #f8fafc;
}
.line-items tbody td {
padding: 10px 12px;
border-bottom: 1px solid #e5e7eb;
}
.line-items tbody td.amount {
text-align: right;
font-variant-numeric: tabular-nums; /* Align decimal points */
}
/* ===== Summary ===== */
.summary {
width: 280px;
margin-left: auto;
margin-bottom: 32px;
}
.summary-row {
display: flex;
justify-content: space-between;
padding: 8px 0;
border-bottom: 1px solid #e5e7eb;
}
.summary-row.grand-total {
font-size: 18px;
font-weight: 700;
border-bottom: 3px double #1a1a1a;
padding: 12px 0;
}
/* ===== Payment Info ===== */
.payment-info {
background: #f9fafb;
border-radius: 6px;
padding: 16px 20px;
margin-bottom: 24px;
break-inside: avoid;
page-break-inside: avoid;
}
.payment-info h3 {
font-size: 12px;
font-weight: 700;
color: #6b7280;
margin-bottom: 8px;
text-transform: uppercase;
letter-spacing: 0.05em;
}
/* ===== Footer ===== */
.invoice-footer {
margin-top: 32px;
padding-top: 16px;
border-top: 1px solid #e5e7eb;
font-size: 11px;
color: #9ca3af;
text-align: center;
break-inside: avoid;
}
</style>
</head>
<body>
<div class="page">
<!-- Header -->
<div class="invoice-header">
<div class="invoice-title">請 求 書</div>
<div class="invoice-meta">
<strong>Invoice #: INV-2026-0042</strong><br>
発行日: 2026年4月5日<br>
お支払い期限: 2026年4月30日
</div>
</div>
<!-- Bill To / From -->
<div class="parties">
<div class="to-section">
<div class="company-name">株式会社サンプルクライアント 御中</div>
<div style="color: #6b7280; font-size: 12px; margin-top: 4px;">担当: 山田 太郎 様</div>
</div>
<div class="from-section">
<div class="company-name">株式会社FUNBREW</div>
東京都渋谷区渋谷1-1-1<br>
渋谷ビル 5F<br>
TEL: 03-1234-5678<br>
billing@funbrew.example.com<br>
登録番号: T1234567890123
</div>
</div>
<!-- Total highlight -->
<div class="total-highlight">
<div class="total-label">今回ご請求金額(税込)</div>
<div class="total-amount">¥165,000</div>
</div>
<!-- Line items -->
<table class="line-items">
<thead>
<tr>
<th style="width: 40%;">品目・内容</th>
<th style="width: 15%; text-align: right;">数量</th>
<th style="width: 20%; text-align: right;">単価(税抜)</th>
<th style="width: 25%; text-align: right;">小計(税抜)</th>
</tr>
</thead>
<tbody>
<tr>
<td>Webシステム開発(2026年3月分)</td>
<td class="amount">1式</td>
<td class="amount">¥100,000</td>
<td class="amount">¥100,000</td>
</tr>
<tr>
<td>デザイン作成<br>
<span style="font-size: 11px; color: #9ca3af;">トップページリニューアル</span>
</td>
<td class="amount">1式</td>
<td class="amount">¥30,000</td>
<td class="amount">¥30,000</td>
</tr>
<tr>
<td>保守・サポート費用</td>
<td class="amount">1ヶ月</td>
<td class="amount">¥20,000</td>
<td class="amount">¥20,000</td>
</tr>
</tbody>
</table>
<!-- Subtotal / tax / total -->
<div class="summary">
<div class="summary-row">
<span>小計(税抜)</span>
<span>¥150,000</span>
</div>
<div class="summary-row">
<span>消費税(10%)</span>
<span>¥15,000</span>
</div>
<div class="summary-row grand-total">
<span>合計(税込)</span>
<span>¥165,000</span>
</div>
</div>
<!-- Payment info -->
<div class="payment-info">
<h3>お振込先 / Bank Transfer</h3>
<p>
サンプル銀行 渋谷支店(支店番号: 123)<br>
普通預金 口座番号: 1234567<br>
口座名義: カ)ファンブリュー<br>
※振込手数料はご負担ください。
</p>
</div>
<!-- Notes -->
<div class="payment-info">
<h3>備考 / Notes</h3>
<p>
お支払い期限を過ぎた場合は、年利14.6%の遅延損害金が発生します。<br>
ご不明点はお気軽にご連絡ください。
</p>
</div>
<!-- Footer -->
<div class="invoice-footer">
<p>本請求書は電子発行されています。印影・押印は省略しています。</p>
<p style="margin-top: 4px;">Generated by FUNBREW PDF</p>
</div>
</div>
</body>
</html>
To turn this into a dynamic template with variables like {{customer_name}} and {{total}}, see the invoice PDF automation guide. You can also paste this HTML directly into the Playground to preview the PDF output instantly.
Summary
Japanese text in HTML-to-PDF conversion breaks because PDF engines run on servers that don't have Japanese fonts installed. Here's the complete checklist:
- Root cause: PDF engine can't render Japanese characters if server-side fonts are missing
- Fastest fix: Use FUNBREW PDF or another service with Noto Sans JP pre-installed
- DIY approach: Load Google Fonts (Noto Sans JP) in HTML — specify all weights you use
- Bold not working: List
wght@400;700(or all weights) in the Google Fonts URL - Intermittent failures: Self-host fonts or use pre-installed fonts to eliminate CDN dependency
- Vertical text: Requires Chromium-based engine (FUNBREW PDF
qualitymode) - Performance: Pre-installed fonts add 0ms latency; Google Fonts CDN adds 100–400ms
Test your font setup in the Playground before deploying to production.
Related Articles
- CSS Tips for HTML to PDF — Page breaks, margins, and table layout
- wkhtmltopdf vs Chromium — Choosing the right PDF engine
- Automate Invoice PDF Generation — Full invoice automation tutorial
- HTML to PDF Complete Guide — Overview of all PDF conversion methods
- PDF API Production Guide — Performance optimization at scale
- Playground — Preview PDF output in real time
- API Reference — Full endpoint documentation