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, msg, ...)
where aptr and bptr 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. They 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).
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
_______________________________________________
HarfBuzz mailing list
[email protected]
http://lists.freedesktop.org/mailman/listinfo/harfbuzz