Perhaps it is useful to indicate how the existing dx[] adjustments map into
the more generalized gpos[][] adjustments. Given a dx[] array or a gpos[][]
array or neither, they are reconciled as follows:

int[][] reconcileAdjustments(int[] dx, int[][] gpos) {
  if ( dx == null ) {
    return gpos;
  } else {
    if ( gpos == null ) {
      gpos = new int[dx.length][4];
    for ( int i = 0; i < dx.length; i++ ) {
      int d = dx[i];
      if ( dx != 0 ) {
        gpos[i][0] += d; // reconcile x placement adjustment

        gpos[i][2] += d; // reconcile x advance adjustment


The current semantics of dx[i] cause the current point to be adjusted to
curPointX + dx[i] prior to rendering glyph[i], after which the implicit x
advancement (width) of glyph[i] is added to curPointX. In the reconciliation
of dx[] and gpos[][] above, dx[i] is added to both the x placement
adjustment and the x advance adjustment of glyph[i], the net effect of which
is that the x origin of glyph[i] is translated by dx[i], and then the
current point x coordinate is advanced by the sum of the implicit glyph
advancement and the x advance adjustment. Together, these yield the same
behavior as presently indicated by the non-generalized form of drawText.

There are a number of places in the existing code where this reconciliation
could take place, including:

(1) just prior to creating a word area, in which case a word area would
carry only the gpos[][] adjustments;
(2) just prior to calling IFPainter.drawText, i.e., in

In both cases, the externalized, AT form of WordArea changes. In case (1),
the letter-adjust attribute is effectively merged into a new
position-adjustattribute, with
letter-adjust being removed. In case (2), a new position-adjust attribute is
added to hold gpos[][], but the existing letter-adjust attribute is retained
as is.

I have tentatively decided to use case (2), since this does not create any
test regressions for the handful of tests that make reference to the
letter-adjust attribute. It could always be moved further back, e.g., to
(1), at a future date.



On Mon, Aug 30, 2010 at 8:19 PM, Glenn Adams <> wrote:

> Folks,
> I'd like to mention a change I will implement on IFPainter#drawText method
> in order to accommodate complex scripts (as well as non-complex script usage
> in a variety of written languages). I'm bringing this up now so there can be
> discussion ahead of time if needed.
> Basically, the change is to generalize the int[] dx parameter to be a two
> dimensional array of glyph placement/advancement adjustments.
> The current interface is:
> void drawText(int x, int y, int letterSpacing, int wordSpacing, int[] dx,
> String text) throws IFException;
> The modified method interface would read as follows:
> void drawText(int x, int y, int letterSpacing, int wordSpacing, int[][]
> adjustments, String text) throws IFException;
> The adjustments array is optional (in which case it is null). If non-null,
> it is effectively typed as int[][4], i.e., an array of int[4] arrays, where
> the four elements of each row are:
> a[0] = x placement adjustment
> a[1] = y placement adjustment
> a[2] = x advance adjustment
> a[3] = y advance adjustment
> The [x,y] placement adjustments are added to the current point to determine
> the effective glyph (char) origin, and the [x,y] advance adjustments are
> applied to the current point after rendering the glyph (char) and performing
> the default (implicit) advance.
> To be more explicit, the algorithm using these adjustments is effectively
> as follows (ignoring word and letter spacing for the moment):
> int curPointX = x;
> int curPointY = y;
> for ( int i = 0, n = glyphs.length; i < n; i++ ) {
>   int g = glyphs [ i ];
>   int gx = curPointX;
>   int gy = curPointY;
>   int[] a = ( adjustments != null ) ? adjustments[i] : null;
>   if ( a != null ) {
>     gx += a[0];
>     gy += a[1];
>   }
>   drawGlyph ( g, gx, gy );
>   curPointX += font.getGlyphAdvanceX ( g );
>   curPointY += font.getGlyphAdvanceY ( g );
>   if ( a != null ) {
>     curPointX += a[2];
>     curPointY += a[3];
>   }
> }
> It is mandatory to provide this generality in order to support not only
> complex scripts, but also non-complex scripts (e.g, Latin, Greek, Cyrillic,
> CJK, etc) when used with non-spacing marks (in many written languages) and
> also for other advanced typographic effects.
> Attached is a simple example of the use of this feature in order to adjust
> the placement (and advance) of U+064E ARABIC FATHA and U+0650 ARABIC KASRA,
> respectively, the upper and lower non-spacing marks shown in this example.
> Regards,
> Glenn

Reply via email to