If you have ever tried to pull bold or italic text out of a PDF, you probably started with a regex. It feels like the obvious move. Search the font name for the word “Bold,” flag the text, and move on. That strategy works just long enough to give you false confidence. Then someone exports the same document from a different version of Acrobat, or from LibreOffice, or from a print-to-PDF driver, and every assumption you coded falls apart.
Why Font Names Lie
PDF.js will hand you strings like ABCDEF+TimesNewRomanPS-BoldMT. The first six characters are a random prefix injected during export, and they change every time the file is regenerated. Betting your parser on that prefix is betting on noise. Other exporters are even less helpful. Some emit bare identifiers like Font12 or F1. These labels carry no semantic meaning at all; they are internal resource tags that happened to be nearby when the file was written.
Because the Portable Document Format was never designed to make downstream text extraction easy, font names are simply references to embedded or subsetted resources. They were never intended to act as a stable API for style detection. When you write a regex that looks for the substring Bold or Italic, you are scraping a label that the creator application was free to format however it pleased. You are not reading the actual typographic properties. You are reading a file-naming convention, and conventions are not contracts.
Read the Descriptor, Not the Label
The ground truth lives elsewhere. In PDF.js, every page object exposes commonObjs, a map that holds the real font descriptors needed to render the page. When the library parses a page, it populates this map with genuine font objects. Those objects expose boolean properties for bold and italic. Those booleans are not inferred from the name string. They originate from the font descriptor embedded inside the PDF, derived from glyph metrics, OS/2 table flags, and the symbolic descriptors that the typesetter wrote into the document.
That means you can stop guessing. Before you walk the text items on a page, create a fontStyleMap by iterating over page.commonObjs. For each font ID, store an object that records the actual .bold and .italic properties provided by the font object. Later, when you process each text item, look up its font reference in your map and read the precomputed flags. You suddenly have a deterministic answer that does not change between exports.
You can keep a fallback. If the descriptor is somehow absent or incomplete, clean the font name strip the prefix, drop the random tags, and run a conservative regex against what remains. But this should be your last resort, not your primary logic. The difference in reliability is dramatic. Where name-based parsing fractures across exporters, descriptor-based parsing holds steady because it asks the file what it actually contains.
When the Font Lies Too: Synthetic Styles
Even the descriptor can miss a trick. Some PDF creators do not bother embedding a separate italic typeface. Instead, they take the upright roman font and slant it with a transform matrix. This is common in files generated by design tools or older word processors that favor file size over typographic purity.
Every text item in PDF.js carries a transform array, a six-element affine matrix that maps the glyph coordinate system into the page coordinate system. The third element of that array controls horizontal shear. When that value is non-zero, the text is being mechanically slanted by the renderer. If you only trust the font descriptor, you will classify this text as upright roman. If you inspect the matrix, you catch the synthetic italic and mark it correctly. The same logic applies to synthetic bold created by overprinting, though that is harder to detect from geometry alone. For slanted text, the shear value is your smoking gun.
Underlines Are Drawn, Not Declared
Bold and italic are font properties. Underline is not. In PDF, an underline is a vector path. The renderer issues a drawing command for a thin horizontal segment positioned near the text baseline. It is a graphical element that happens to sit underneath glyphs, not a character attribute stored in a cmap.
این تمایز اهمیت دارد زیرا هیچ میزان خواندن توصیفگرهای فونت (font descriptor) نمیتواند زیرخط (underline) را آشکار کند. شما باید به عملگرهای ترسیم خام (raw drawing operators) در صفحه، یا به خروجی در سطح هندسی (geometry-level)، برای یافتن قطعات خطی افقی کوتاهی که موازی با خط پایه (baseline) و در فاصله مناسب قرار دارند، نگاه کنید. وقتی موتور استخراج شما چنین قطعهای را زیر یک رشته متنی شناسایی میکند، آن رشته را به عنوان زیرخطدار (underlined) برچسبگذاری میکنید. برخورد با این موضوع به عنوان یک لایه تشخیص مجزا، مدل دادهای شما را دقیق و قابل اعتماد نگه میدارد: bold و italic ویژگیهای ذاتی فونت هستند، در حالی که underline یک تزئین بیرونی است که توسط سند رندر میشود.
یک خط لوله عملیاتی
یک سیستم استخراج تمیز، وظایف را به لایههای متمایز تقسیم میکند. ابتدا، یک پردازشگر هندسی (geometry worker) صفحه را پیمایش میکند. این پردازشگر page.commonObjs را برای ساخت fontStyleMap شما پرسوجو میکند، آرایه transform هر آیتم متنی را برای شناسایی ایتالیکهای مصنوعی (synthetic italics) بررسی میکند و مسیرهای برداری (vector paths) نزدیک را برای یافتن زیرخطها اسکن میکند. خروجی آن یک ساختار میانی تمیز است که میتوان آن را textMeta نامید، که در آن هر رشته متنی دارای سه مقدار بولین ساده است: bold، italic و underline.
سپس، یک بازساز متن (text rebuilder) آن ساختار را مصرف کرده و خروجی نشانهگذاریشده (marked-up) را منتشر میکند. ترتیب تو در تو بودن (nesting order) در اینجا مهم است. سلسلهمراتب صحیح، زیرخط را در بیرونیترین لایه، سپس ایتالیک و در نهایت ضخیم (bold) را در درونیترین لایه قرار میدهد. این بدان معناست که یک رشته با استایل کامل به <u><i><b>text</b></i></u> تبدیل میشود. این ترتیب از همپوشانیهای نامعتبر HTML جلوگیری کرده و رندرینگ را در مرورگرها و مبدلهای سند یکسان نگه میدارد. همچنین منطق تایپوگرافی را منعکس میکند: تزئینات، تأکید معنایی را در بر میگیرند و تأکید معنایی، وزن ساختاری را در بر میگیرد.
قدرت این رویکرد در این است که تنها از آنچه PDF از خودش میداند استفاده میکند. هیچ OCR، هیچ سرویس بینایی ابری و هیچ مدل یادگیری ماشینی که سبکها را از پیکسلهای رستری حدس بزند، در کار نیست. شما در حال خواندن لایه معنایی خودِ فایل هستید که از طریق هندسه و متادیتایی که اپلیکیشن سازنده قبلاً محاسبه کرده، در دسترس است. نتیجه، روشی سریع، قطعی (deterministic) و دقیق در میان فضای پرآشوب تولیدکنندگان PDF است.
نکته اصلی و کاربردی
استفاده از Regex برای نام فونتها یک تله است. این کار شبیه یک میانبر به نظر میرسد زیرا روی همان یک فایلی که تست کردهاید کار میکند، اما با کمترین فشارِ یک خروجی (export) دوم، از هم میپاشد. اطلاعات واقعی از قبل داخل PDF قرار دارد؛ در توصیفگرها (descriptors)، ماتریسها و مسیرهای برداری. خط لوله خود را بر اساس این واقعیتها بسازید. از شیء فونت بپرسید که آیا bold است یا خیر. ماتریس transform را برای شیب (shear) بررسی کنید. قطعات ترسیم شده را برای یافتن زیرخطها نگاه کنید. اگر به جای استخراج سطحی از برچسبها، از ساختار مهندسی خودِ سند پرسوجو کنید، به سبکهایی دست مییابید که از یک تولیدکننده PDF به تولیدکننده دیگر منتقل میشوند.
