mirror of
				https://github.com/RGBCube/serenity
				synced 2025-10-31 19:42:43 +00:00 
			
		
		
		
	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()
This commit is contained in:
		
							parent
							
								
									c9d48bbca4
								
							
						
					
					
						commit
						bd74447dba
					
				
					 2 changed files with 50 additions and 7 deletions
				
			
		|  | @ -246,11 +246,24 @@ PDFErrorOr<NonnullRefPtr<CFF>> CFF::create(ReadonlyBytes const& cff_bytes, RefPt | |||
| 
 | ||||
|     for (size_t i = 0; i < glyphs.size(); i++) { | ||||
|         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; | ||||
|         } | ||||
|         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(); | ||||
| 
 | ||||
|  |  | |||
|  | @ -47,8 +47,8 @@ PDFErrorOr<NonnullOwnPtr<CIDFontType0>> CIDFontType0::create(Document* document, | |||
|         if (font_file_dict->contains(CommonNames::Subtype)) | ||||
|             subtype = font_file_dict->get_name(CommonNames::Subtype)->name(); | ||||
|         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 { | ||||
|             // FIXME: Add support for /OpenType.
 | ||||
|             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)))); | ||||
| } | ||||
| 
 | ||||
| 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
 | ||||
|     // "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 "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"
 | ||||
|     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 { | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Nico Weber
						Nico Weber