bangla_pdf 1.4.1 copy "bangla_pdf: ^1.4.1" to clipboard
bangla_pdf: ^1.4.1 copied to clipboard

Correct Bangla (Bengali) text in PDFs: real OpenType shaping for conjuncts, reph and vowel signs, with text that copies back out as clean Unicode.

bangla_pdf

Bangla PDFs that actually look right.
Change one import. Everything else stays exactly as it is.

pub package pub points license platforms

Stand With Palestine


Write Bangla, get Bangla.

pw.Text('আমার সোনার বাংলা, আমি তোমায় ভালোবাসি।')

That is the package:pdf widget you already use, from an import that shapes Bangla properly. Conjuncts join, ি land on the correct side of their consonant, reph sits where it belongs — and the text you copy out of the PDF is the text you put in.

Before and after


📄 Made with it #

Real output from the widgets below — no mockups. Every conjunct, matra and reph in these is placed by the shaper, and every word is selectable and searchable.

চালান · Invoice #

A styled table with Bengali digits and the taka sign, built with pw.TableHelper.fromTextArray.

A Bangla invoice with a styled table, Bengali digits and taka amounts

বিজ্ঞপ্তি · Notice #

Headings, a justified paragraph and a bulleted list — pw.Header, pw.Paragraph and pw.Bullet.

A Bangla government-style notice with headings and bullets

প্রতিবেদন · Report #

Bangla and English in the same paragraphs, in one pass, with no manual splitting.

A report mixing Bangla and English text

All three are generated by tool/dev/make_showcase.dart — run it yourself with dart run tool/dev/make_showcase.dart.


🚀 Get started #

dependencies:
  bangla_pdf: ^1.4.1

Now change one import:

- import 'package:pdf/widgets.dart' as pw;
+ import 'package:bangla_pdf/widgets.dart' as pw;

That is the entire migration. Your existing code is untouched:

import 'package:bangla_pdf/widgets.dart' as pw;

final pdf = pw.Document();

pdf.addPage(
  pw.Page(
    build: (context) => pw.Text('আমার সোনার বাংলা'),
  ),
);

final bytes = await pdf.save();

No font to bundle, nothing to initialise, no new widget names to learn. A Bangla font ships with the package and is used automatically.


🧩 Nothing new to learn #

You keep writing package:pdf code exactly as you always have:

pw.Header(level: 1, text: 'গণপ্রজাতন্ত্রী বাংলাদেশ সরকার')

pw.Paragraph(text: 'একটি অনুচ্ছেদ যেখানে ক্ষ্ম ও কর্ম দুটোই ঠিক আসে।')

pw.Bullet(text: 'প্রথম আইটেম')

pw.TableHelper.fromTextArray(
  headers: ['পণ্য', 'পরিমাণ', 'মূল্য'],
  data: [
    ['কফি', '২', '৳২০'],
  ],
)

Same class names, same parameters, same everything. The import quietly swaps in Bangla-aware versions of the widgets that draw text:

Text · RichText · TextSpan · Header · Paragraph · Bullet · TableHelper · Watermark · TableOfContent · ChartLegend · FixedAxis · TextField · ChoiceField

That is every widget in package:pdf that puts text on a page — so Bangla also comes out right in places you never think about: chart axis labels and legends, watermarks, tables of contents, and the values inside form fields.

Every other widget — pw.Page, pw.Column, pw.Container, pw.MultiPage, images, barcodes — is literally the same class from package:pdf. This package does not wrap or re-implement them.

Bangla, English, digits and mix freely in one string, with nothing to split up by hand:

pw.Text('Invoice #1042 — মোট ৳১২,৫০০.০০ — তারিখ ০১/০৯/২০২৬')

And a string with no Bangla in it never touches this package — it goes straight to package:pdf, so your English pages render exactly as they do today.

Prefer explicitly Bangla-named widgets? Those still exist.

If you would rather see at a glance which widgets are Bangla-aware, import the main library instead and use its own widgets. This is the original 1.0 API and it is not going anywhere:

import 'package:bangla_pdf/bangla_pdf.dart';
import 'package:pdf/widgets.dart' as pw;

Text('বাংলা টেক্সট')
Header('বাংলা শিরোনাম', level: 1)
Paragraph('একটি অনুচ্ছেদ।')
BulletList(items: ['প্রথম আইটেম', 'Second item'])
Table(data: [['পণ্য', 'মূল্য'], ['কফি', '৳২০']])
RichText(spans: [TextSpan('বাংলা বোল্ড ', fontWeight: pw.FontWeight.bold)])

These differ slightly from pw on purpose: Header and Paragraph take their text positionally, Table wraps TableHelper.fromTextArray, and BulletList takes a whole list. Use package:bangla_pdf/widgets.dart when you want the pw shapes instead — the two mix freely in one file.


📥 Reading Bangla back out of a PDF #

Point it at a PDF and get the text:

import 'package:bangla_pdf/extract.dart';

final result = BanglaPdfExtractor.extract(bytes);

print(result.text);
print(result.encodingDetected);   // unicode | bijoy | mixed | none

It also rescues Bijoy documents — the government and newspaper PDFs where copying text gives you Avgvi ‡mvbvi evsjv instead of আমার সোনার বাংলা. Those are detected and converted back for you.

Scanned pages report BanglaTextEncoding.none instead of guessing, and you can plug in whichever OCR you already use:

BanglaPdfExtractor.extract(
  bytes,
  ocrHook: (page) => runTesseract(page.number, language: 'ben'),
);

It is a separate library, so generating PDFs costs nothing if you never import it.


🔤 Want a different font? #

final font = BanglaPdf.loadFont(
  await rootBundle.load('assets/fonts/SolaimanLipi.ttf'),
);

BanglaPdf.configure(defaultFont: font);   // everywhere
Text('বাংলা', banglaFont: font);          // or just here

SolaimanLipi, Siyam Rupali, Noto Sans Bengali and Noto Serif Bengali are all tested. Your own Bijoy font keeps working too — it is detected and handled the old way.


⬆️ Coming from bangla_pdf 1.0? #

Nothing to change. Text(...), banglaStyle:, banglaFont: and the no-setup default all work as before, and the bundled typeface is still Kalpurush — your documents look the same, the Bangla in them is just shaped correctly now. The package:bangla_pdf/widgets.dart entry point above is purely additive; you can adopt it file by file, or never.

What changed, in detail
  • Reph no longer crashes. কর্ম, ধর্ম, বর্ষ, পূর্ব, শর্ত threw an uncaught RangeError in 1.0.6 that aborted pdf.save() outright. 21 of 253 corpus cases crashed; none do now.
  • Conjuncts join. ক্ষ্ম, ঙ্ক্ষ, ত্ত্ব, চ্ছ্ব, ম্ভ্র, স্ত্র্য used to render as a base plus a stray hasanta.
  • Digits and currency survive. ০–৯ silently became 0–9, and became $.
  • Copy, search and screen readers work. The PDF now holds real Unicode instead of Bijoy ANSI.
  • render instead of showing a missing-glyph box, and র‍্য is now distinguished from র্য.
  • One font draws the whole string, so style and banglaStyle no longer give Bangla and Latin different looks inside one widget; banglaStyle wins when both are set. Use two widgets for two looks.
  • AutoText and RichTextItem are deprecated. Text is identical to AutoText — splitting a string by script stopped being necessary once the Bangla font covered Latin and digits too — and RichTextItem was never used by anything. Both still work and are removed in 2.0.0.
  • To get byte-identical 1.0.x output back, call BanglaPdf.configure(shapingMode: BanglaShapingMode.legacy) once at startup.

✅ How well does it work? #

Shaping is compared glyph by glyph against HarfBuzz — the engine behind Chrome, Android and LibreOffice — over a 253-case corpus:

font exact match
Kalpurush (bundled) 234 / 234
SolaimanLipi 234 / 234
Siyam Rupali 234 / 234
Noto Sans Bengali 234 / 234
Noto Serif Bengali 234 / 234

The text survives the round trip too: 249 of 251 cases come back out of a generated PDF identical to what went in, checked with pdftotext. Extraction recovers 8 of 8 fixture documents exactly and correctly reports both scanned ones as having no text layer.

Signature parity is checked too: a script diffs all 14 replacement constructors against their package:pdf counterparts, and all 156 parameters match.

All of it runs on every commit — flutter test is 55 tests.

How the shaping actually works

Bangla is shaped with the font's own OpenType GSUB/GPOS tables, in pure Dart — no FFI and no C toolchain, which is why this also works on Flutter web.

  1. Normalise — NFC and NFD are reconciled, two-part vowels ( ) are split the way font rules expect, and nukta pairs are composed.
  2. Segment the text into Indic syllables.
  3. Find the base consonant by asking the font which consonants it gives below-base or post-base forms to.
  4. Reorder — pre-base matras move in front of the cluster; reph moves after the base and any below-base form.
  5. Apply GSUB in the OpenType Indic order — nukt akhn rphf blwf half pstf vatu cjct, then pres abvs blws psts haln, then calt clig rclt rlig — each under the right per-glyph mask. Both generations of the spec are handled: bng2 fonts write their rules as virama + consonant, beng fonts the other way round.
  6. Apply GPOSdist abvm blwm mark mkmk kern, including mark-to-base, mark-to-ligature and mark-to-mark attachment.
  7. Emit a Type0/Identity-H CID font addressed by glyph id, with an explicit /CIDToGIDMap and a /ToUnicode CMap whose entries may span several codepoints — so a conjunct copies back as its full sequence.
  8. Wrap each line in a /Span <</ActualText …>> BDC … EMC marked-content span. This is what makes copy/paste survive Bengali's glyph reordering: কি draws ি first, so no per-glyph mapping alone can express logical order.

Lines break on syllable boundaries, so a line never splits inside a conjunct or between a vowel sign and its consonant.

Full write-ups live in the repository: the verification report and the 1.0.6 teardown.


⚠️ Known limitations #

  • Extraction cannot rescue every third-party PDF. A document with no /ToUnicode and no /ActualText cannot be recovered — that needs reversing the shaping from glyph ids back to characters, which is not implemented.
  • Encrypted PDFs are not decrypted. ExtractionResult.isEncrypted says so rather than returning nonsense.
  • Emoji need a fallback font. The bundled font covers ASCII, Bengali and common punctuation; emoji render as a placeholder box.
  • The full font is embedded in every PDF (121 KB) — there is no per-document subsetter yet.
  • Five fonts are measured. Others should work but are untested. A font relying on GSUB lookup type 8 or GPOS type 3 would not shape; no Bengali font tested uses either.
  • Form fields shape only their appearance. pw.TextField and pw.ChoiceField draw a shaped value, but once a reader lets someone edit the field it re-renders from the form font. No PDF producer controls that.
  • Rendering is not pixel-diffed, and copy/paste is verified with poppler only — not Adobe Reader, Preview or Chrome.
  • Extraction fixtures are generated, not collected — shaped like real notices, invoices and newspaper pages, but not downloaded from a government website.

🤝 Contributing #

Found Bangla that renders wrong? That is the most useful bug report there is. Add the string to test/corpus/bangla_cases.json with an id, category and notes, and the differential harness picks it up.

flutter test                                  # unit and PDF-structure tests
dart run tool/dev/shape_dump.dart <font.ttf>  # diff against hb-shape

hb-shape (brew install harfbuzz) and pdftotext (brew install poppler) are needed for the verification tooling, not for the package itself.


📄 License #

BSD 3-Clause — see LICENSE.

The bundled Kalpurush is by Md. Tanbin Islam Siyam (Avro Font Development Project, omicronlab.com) under the SIL Open Font License 1.0; its Latin glyphs are from Gentium. Font licences are in LICENSE-FONTS.txt.


💛 Credits #

Maintained by Zaman Sheikh · zaman6545@gmail.com

The legacy Bijoy pipeline kept for backward compatibility (BanglaShapingMode.legacy) descends from the ANSI transcoding approach in AR Rahman's bangla_pdf_fixer 2.x. It is retained only so 1.0.x users can reproduce their old output; the shaping in this package does not use it.

Thanks to every Bangla font creator whose work makes readable Bangla typography possible.

⭐ If this saved you a day of debugging, star the repo.

11
likes
0
points
660
downloads

Documentation

Documentation

Publisher

verified publisherzamansheikh.com

Weekly Downloads

Correct Bangla (Bengali) text in PDFs: real OpenType shaping for conjuncts, reph and vowel signs, with text that copies back out as clean Unicode.

Repository (GitHub)
View/report issues

Topics

#pdf #bangla #unicode #typography #text-shaping

License

unknown (license)

Dependencies

archive, flutter, pdf, plugin_platform_interface

More

Packages that depend on bangla_pdf