[HarfBuzz] harfbuzz: Branch 'master'

Martin Hosken mhosken at gmail.com
Sun Dec 20 18:01:06 PST 2015


Dear Behdad,

>  buf = hb.buffer_create ()
> +class Debugger(object):
> +	def message (self, buf, font, msg, data, _x_what_is_this):
> +		print(msg)
> +		return True
> +debugger = Debugger()
> +hb.buffer_set_message_func (buf, debugger.message, 1, 0)
>  hb.buffer_add_utf8 (buf, text.encode('utf-8'), 0, -1)
>  hb.buffer_guess_segment_properties (buf)

Yippee. At last, a debug interface :) (Behdad reminds me that I have been asking this once per year for the last 4 years!). Thank you.

OK. Now to make a great debug interface!

There are two ways of doing a debug interface: Event driven and One shot. There are probably more, but those are the only two that come to mind now. One shot sends all the information needed to give all the debug information for a debug point in its message. This allows the debugger not to have to keep state, but just record the results and pass them on. Event driven sends, well, events to the debugger and requires the debugger to keep state.

While one shot seems more inviting and is more in line with what Graphite does. I think for harfbuzz, I would recommend an event based debugger, where you send debug events at the start and end of every lookup, at recursion, during initial reordering and shaping, at dotted circle insertion, etc. and have an enum of events and let the debugger work out what it wants to do with that information.

So, I would add an enum to the debug message to give a debug message event type.

One big question that always needs to be answered in the debugger is: where are we? Where in the buffer are we now processing. This is the idx field of the buffer. I don't think this is exposed in the public buffer interface. So it either needs to be exposed or passed as part of the debug message.

I suggest that rather than relying on a message to give the lookup number, that the lookup number be passed as a separate parameter (or in a struct or whatever). The lookup number can be overloaded based on event type. So we could have a starting high level phase event type and use the lookup to say whether that is initial shaping, GSUB, GPOS, etc. for example. Or we could have different event types for each one. That's up to you.

I think we need to send a message each shaper pause when the pause occurs.

For GPOS we need to be passing parameters like the two points in an attachment or the actual calculated offset in a pair or single adjustment. When doing classed based activities, we should be passing the class values involved or perhaps pointers (or offsets) to the data structures involved so that a debugger can turn cross reference that back to source code.

What does that look like now:

debug_message(type, buf, idx, lkupidx, void *aptr, void *bptr, uint32 aoffset, uint32 boffset, msg, ...)

where aoffset and boffset are defined by type and lkupidx and may point to things like an attachment point record or a lookup record in a class based contextual lookup or somesuch. aptr and bptr may also point to debugger specific data structures (perhaps for an attachment point one needs a pointer to the ap record and 2 floats for the resolved x,y coordinates). Of course this could all end up in a structure of some kind.

You know, if we get this right, we should be able to drop the msg, ... since debuggers really don't want to have to parse textual messages. Yes they are easy for a quick trace, but not for a real debugger. But it's welcome to stay to make such tracing programs' lives easier, but it shouldn't contain anything that isn't in the other parameters. If it does, then we need a way to pass it outside the message.

And yes, while I'm trying to define what the kitchen sink is, I'm also trying to keep this lightweight.

I know the moment I hit send, I'll think of things I've forgotten!

Yours,
Martin


More information about the HarfBuzz mailing list