Hi Alexis,

(Apologies if anyone is getting this twice.)

On 1/8/22 17:10, Alexis King wrote:

In my experiments so far, I have been getting access to these metrics by importing |racket/draw/unsafe/pango| and |racket/draw/private/local| and using private APIs to make the necessary FFI calls myself. However, this is obviously not a great long-term solution, especially since some of the information I’d like to get my hands on isn’t even directly accessible through the private APIs. Therefore, I would like to add public APIs for somehow accessing this information using |racket/draw|.

This raises some API design questions, as there is not much precedent in |racket/draw| for exposing these nitty-gritty details of fonts. The |font%| class is really closer to a font description than a concrete font face, which is why even the most rudimentary methods for getting information about fonts, such as |glyph-exists?|, are actually methods of |dc<%>|, not |font%| itself. What’s more, such methods do not query font information directly: even |glyph-exists?| may perform font substitution.

Much of this complexity is inherent to the problem of text layout and rendering, and |racket/draw| generally tries to hide it as much as possible. Unfortunately, those abstractions are at least somewhat at odds with my goal of implementing a math renderer, since I need to get fairly low-level access to font information. This leaves me considering two possible ways forward:

1.

    Expose public but explicitly *unsafe* direct access to Cairo and
    Pango contexts, and allow third-party libraries to make the
    necessary FFI calls themselves.

2.

    Implement a safe API in |racket/draw| that provides the necessary
    low-level access to fonts and glyphs.

The second option sounds compelling, since safety is obviously preferable to unsafety, but the required API surface area would be pretty large: it would essentially amount to exposing a significant portion of HarfBuzz. That would create a significant backwards compatibility burden for |racket/draw|, and probably for relatively little gain, since most users have no need to get at any of this information. I’m therefore leaning towards the former, but I’d like to know if this sounds like a reasonable conclusion to others before doing this work.

I definitely see the tension between these two options, and especially the potential downsides of having compatibility deeply intertwined with an external dependency as large and complex as HarfBuzz. I don't have an answer, but here are some stray thoughts, anyway.

I have definitely wanted more low-level control over fonts, ideally (of course) in a safe way, and I remember other discussions when people have wanted similar things. I think one of the more common requests has been more control over font loading and resolution, e.g. to load a font from a file rather than relying exclusively on the system's installed fonts, or perhaps more control over fallback behavior when a given font doesn't have some glyph. I'm not deeply familiar with the boundaries between Pango, Fontconfig, HarfBuzz, etc.: I think much of that would be higher level than what you need, but maybe there's some overlap.

I'm not sure exactly how low-level my own desires go. At maximum, if I have the time some day,  the current state-of-the-art system for typesetting medieval plainchant is essentially a DSL that compiles to LuaTeX <https://gregorio-project.github.io/>, and I would love to make it into a #lang with better means of abstraction.

More concretely, I've noticed from time to time that racket/draw/unsafe/glib, mred/private/wx/gtk/utils, and maybe a few other places have Glib FFI utilities that aren't specifically tied to drawing or GUI contexts. I think it might be useful to move those into a new public ffi/unsafe/glib module, analogous to ffi/unsafe/objc, either in a new package or in "draw-lib", especially since racket/draw/unsafe/glib sets up logging with Racket's private glib-log-message primitive. (At a glance, it looks like there aren't breaking changes in Glib associated with Gtk4, but it would probably be worth confirming that any new public functionality is not deprecated.)

The Unsafe Libraries <https://docs.racket-lang.org/draw/unsafe.html> chapter of the racket/draw docs leaves open the possibility that the representation of handles may "change if the racket/draw library is implemented differently in the future." It seems reasonable to me to provide weaker compatibility guarantees for low-level functionality, whether safe or unsafe, than for high-level functionality. If programmers /want/ to implement safe APIs, it seems unsatisfying as a general principle to push them towards unsafe functionality, instead, when the underlying issue is not a matter of safety but that the external world may not share Racket's usual commitment to long-term compatibility. (I feel like there's some kind of analogy to the Separate Compilation Guarantee to be made here.) Maybe there's a way of organizing module and/or package boundaries so that racket/draw can keep it's current compatibility guarantees, something like (bad name idea) racket/cairo+pango+harfbuzz could provide safe or somewhat-safe implementation-dependent functionality, and racket/draw can simply continue leave open the possibility that it may in the future adopt an implementation incompatible with racket/cairo+pango+harfbuzz.

I'm not sure how useful any of that is, but there are some thoughts, at least.

-Philip

--
You received this message because you are subscribed to the Google Groups "Racket 
Developers" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to racket-dev+unsubscr...@googlegroups.com.
To view this discussion on the web visit 
https://groups.google.com/d/msgid/racket-dev/f4ce1cc8-67c5-c892-3771-285dc2c0f05d%40philipmcgrath.com.

Reply via email to