gettalong / hexapdf

Versatile PDF creation and manipulation for Ruby
https://hexapdf.gettalong.org
Other
1.21k stars 69 forks source link

font.on_missing_glyph strange behaviour on edited document #277

Closed MorneL closed 8 months ago

MorneL commented 8 months ago

Hi there,

If change the glyph string with a caught font.on_missing_glyph, I'm getting strange behaviour in some pdf viewers. In the video below I'm using mac's preview app. When I click on the field the emoji's are showing, but when I click away it hides again. If it edit the field the emoji's stay there.

https://github.com/gettalong/hexapdf/assets/24522538/9d7ecf17-dc7c-4acc-a6d1-444019ff4fd5

code used to change out the character

  hexa_pdf_document.config['font.on_missing_glyph'] = lambda do |character, font_wrapper|
    font_wrapper.custom_glyph(:space, character)
  end
gettalong commented 8 months ago

Ah, thanks for the explanation!

So what is going on here is that you set the text value of the field to the string with the emojis. When HexaPDF tries to render that string value while creating the appearance stream, it finds that the used font doesn't support the emoji characters and uses 'font.on_missing_glyph' to switch to another glyph, in your case the space character.

This means that the string value differs from the visual representation. When you click inside the form field, the PDF viewer uses the text value with the emojis. If you don't edit the text, the PDF viewer won't regenerate the visual appearance. However, if you do edit the text, the visual appearance is regenerated.

I will have to think about how to remedy this situation.

gettalong commented 8 months ago

@MorneL The next version of HexaPDF supports fallback glyphs via the new 'font.on_invalid_glyph' configuration option. The option is similar to 'font.on_missing_glyph' but whereas this option must return a glyph in the same font, the 'font.on_invalid_glyph' configuration option can return zero, one or more glyphs in any font.

So you would use the new configuration option to provide fallback glyph support for those that aren't available in the set font. This is similar to how PDF readers work when filling out a form field and the font with which to fill out doesn't contain the necessary glyphs.

Here is an example where the ZapfDingbats font is used as fallback. You could, however, use any TrueType font as fallback or even multiple fallback fonts if necessary:

require 'hexapdf'

doc = HexaPDF::Document.new
doc.config['font.on_invalid_glyph'] = lambda do |codepoint, _|
  [doc.fonts.add('ZapfDingbats').decode_codepoint(codepoint)]
end

form = doc.acro_form(create: true)
field = form.create_text_field('Field1', font_size: 16)
field.create_widget(doc.pages.add, Rect: [20, 700, 300, 720])
field.field_value = "Fallback glyphs ✂ ✐ and ✈"

doc.write('gh277.pdf')

And here is the output of the rendered form field: image And here how it looks when editing: image

The changes to run the example are live on the devel branch.