瀏覽代碼

LibPDF: Initial support for drawing CFF-based Type0 fonts

Together with the already-merged #23122, #23128, #23135, #23136, #23162,
and #23167, #23179, #23190, #23194 this adds initial support for
rendering some CFF-based Type0 fonts :^)

There's a long list of things that still need improving after this:

* A small number of CFF programs contain the charstring command 0,
  which is invalid. Currently, this makes us reject the whole font.

* Type1FontProgram::rasterize_glyph() is name-based. For CID-based
  fonts, we want a version that takes CIDs (character IDs) instead.
  For now, I'm printing the CID to a string and using that, yuck.
  (I looked into doing this nicely. I do want to do that, but I
  need to read up on how the `seac` type1 charstring command uses
  character names to identify parts of an accented character.
  Also, it looks like `seac`'s accented character handling moved
  over to `endchar` in type2 charstring commands (i.e. in CFF data),
  and it looks like we don't implement that at all. So I need to do
  more reading first, and I didn't want to block this on that.)

* The name for the first string in name-based CFF fonts looks wrong;
  added a FIXME for that for now.

* This supports the named Identity-H cmap only for now. Identity-H
  maps UTF16-BE values to glyph IDs with the idenity function, and
  assumes it's horizontal text. Other named cmaps in my test files are
  UniJIS-UCS2-H, UniCNS-UCS2-H, Identity-V, UniGB-UCS2-H, UniKS-UCS2-H.
  (There are also 2 files using the stream-based cmaps instead of the
  name-based ones.)

  * In particular, we can't draw vertical text (`-V`) yet

* Passing in the encoding to CFF::create() is awkward (it's nullptr
  for CID-keyed fonts), and it's also not necessary since
  `Type1Font::draw_glyph()` already does the "take encoding from PDF,
  and only from font if the PDF doesn't store one" dance.

* This doesn't cache glyphs but re-rasterizes them each time. Easy
  to add, but maybe I want to look at rotation first. And things
  don't feel glacial as-is.

* Type0Font::draw_glyph() is pretty similar to second half of
  Type1Font::draw_glyph()
Nico Weber 1 年之前
父節點
當前提交
bd74447dba
共有 2 個文件被更改,包括 50 次插入7 次删除
  1. 16 3
      Userland/Libraries/LibPDF/Fonts/CFF.cpp
  2. 34 4
      Userland/Libraries/LibPDF/Fonts/Type0Font.cpp

+ 16 - 3
Userland/Libraries/LibPDF/Fonts/CFF.cpp

@@ -246,11 +246,24 @@ PDFErrorOr<NonnullRefPtr<CFF>> CFF::create(ReadonlyBytes const& cff_bytes, RefPt
 
 
     for (size_t i = 0; i < glyphs.size(); i++) {
     for (size_t i = 0; i < glyphs.size(); i++) {
         if (i == 0) {
         if (i == 0) {
-            TRY(cff->add_glyph(0, move(glyphs[0])));
+            if (top_dict.is_cid_keyed) {
+                // FIXME: Do better than printing the cid to a string.
+                auto cid = 0;
+                TRY(cff->add_glyph(ByteString::formatted("{}", cid), move(glyphs[0])));
+            } else {
+                // FIXME: Shouldn't this use resolve_sid(0, strings) (".notdef") as name?
+                TRY(cff->add_glyph(0, move(glyphs[0])));
+            }
             continue;
             continue;
         }
         }
-        auto const& name = charset_names[i - 1];
-        TRY(cff->add_glyph(name, move(glyphs[i])));
+        if (top_dict.is_cid_keyed) {
+            // FIXME: Do better than printing the cid to a string.
+            auto cid = charset[i - 1];
+            TRY(cff->add_glyph(ByteString::formatted("{}", cid), move(glyphs[i])));
+        } else {
+            auto const& name = charset_names[i - 1];
+            TRY(cff->add_glyph(name, move(glyphs[i])));
+        }
     }
     }
     cff->consolidate_glyphs();
     cff->consolidate_glyphs();
 
 

+ 34 - 4
Userland/Libraries/LibPDF/Fonts/Type0Font.cpp

@@ -47,8 +47,8 @@ PDFErrorOr<NonnullOwnPtr<CIDFontType0>> CIDFontType0::create(Document* document,
         if (font_file_dict->contains(CommonNames::Subtype))
         if (font_file_dict->contains(CommonNames::Subtype))
             subtype = font_file_dict->get_name(CommonNames::Subtype)->name();
             subtype = font_file_dict->get_name(CommonNames::Subtype)->name();
         if (subtype == CommonNames::CIDFontType0C) {
         if (subtype == CommonNames::CIDFontType0C) {
-            // FIXME: Call CFF::create() and assign the result to font_program once CFF::create() can handle CID-keyed fonts.
-            return Error::rendering_unsupported_error("Type0 font CIDFontType0: support for CIDFontType0C not yet implemented");
+            // FIXME: Stop passing an external encoding to CFF::create().
+            font_program = TRY(CFF::create(font_file_stream->bytes(), /*encoding=*/nullptr));
         } else {
         } else {
             // FIXME: Add support for /OpenType.
             // FIXME: Add support for /OpenType.
             dbgln("CIDFontType0: unsupported FontFile3 subtype '{}'", subtype);
             dbgln("CIDFontType0: unsupported FontFile3 subtype '{}'", subtype);
@@ -64,7 +64,7 @@ PDFErrorOr<NonnullOwnPtr<CIDFontType0>> CIDFontType0::create(Document* document,
     return TRY(adopt_nonnull_own_or_enomem(new (nothrow) CIDFontType0(move(font_program))));
     return TRY(adopt_nonnull_own_or_enomem(new (nothrow) CIDFontType0(move(font_program))));
 }
 }
 
 
-PDFErrorOr<void> CIDFontType0::draw_glyph(Gfx::Painter&, Gfx::FloatPoint, float, u32, Renderer const&)
+PDFErrorOr<void> CIDFontType0::draw_glyph(Gfx::Painter& painter, Gfx::FloatPoint point, float width, u32 cid, Renderer const& renderer)
 {
 {
     // ISO 32000 (PDF 2.0) 9.7.4.2 Glyph selection in CIDFonts
     // ISO 32000 (PDF 2.0) 9.7.4.2 Glyph selection in CIDFonts
     // "When the CIDFont contains an embedded font program that is represented in the Compact Font Format (CFF),
     // "When the CIDFont contains an embedded font program that is represented in the Compact Font Format (CFF),
@@ -75,7 +75,37 @@ PDFErrorOr<void> CIDFontType0::draw_glyph(Gfx::Painter&, Gfx::FloatPoint, float,
     //    The GID value shall then be used to look up the glyph procedure using the CharStrings INDEX table [...]
     //    The GID value shall then be used to look up the glyph procedure using the CharStrings INDEX table [...]
     //  * The "CFF" font program has a Top DICT that does not use CIDFont operators: The CIDs shall be used
     //  * The "CFF" font program has a Top DICT that does not use CIDFont operators: The CIDs shall be used
     //    directly as GID values, and the glyph procedure shall be retrieved using the CharStrings INDEX"
     //    directly as GID values, and the glyph procedure shall be retrieved using the CharStrings INDEX"
-    return Error::rendering_unsupported_error("Type0 font CIDFontType0 not implemented yet");
+
+    // FIXME: We currently only do the first.
+
+    // FIXME: Do better than printing the cid to a string.
+    auto char_name = ByteString::formatted("{}", cid);
+    auto translation = m_font_program->glyph_translation(char_name, width);
+    point = point.translated(translation);
+
+    auto glyph_position = Gfx::GlyphRasterPosition::get_nearest_fit_for(point);
+
+    // FIXME: Cache the font bitmap (but probably want to figure out rotation first).
+    auto bitmap = m_font_program->rasterize_glyph(char_name, width, glyph_position.subpixel_offset);
+    if (!bitmap)
+        return Error::rendering_unsupported_error("Type0 font CIDFontType0: failed to rasterize glyph");
+
+    auto style = renderer.state().paint_style;
+
+    if (style.has<Color>()) {
+        painter.blit_filtered(glyph_position.blit_position, *bitmap, bitmap->rect(), [style](Color pixel) -> Color {
+            return pixel.multiply(style.get<Color>());
+        });
+    } else {
+        style.get<NonnullRefPtr<Gfx::PaintStyle>>()->paint(bitmap->physical_rect(), [&](auto sample) {
+            painter.blit_filtered(glyph_position.blit_position, *bitmap, bitmap->rect(), [&](Color pixel) -> Color {
+                // FIXME: Presumably we need to sample at every point in the glyph, not just the top left?
+                return pixel.multiply(sample(glyph_position.blit_position));
+            });
+        });
+    }
+
+    return {};
 }
 }
 
 
 class CIDFontType2 : public CIDFontType {
 class CIDFontType2 : public CIDFontType {