[Top][All Lists]
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Margin kerning, patch #2
From: |
Ludovic Courtès |
Subject: |
Margin kerning, patch #2 |
Date: |
Fri, 01 Jul 2005 15:03:12 +0200 |
User-agent: |
Gnus/5.1007 (Gnus v5.10.7) Emacs/21.4 (gnu/linux) |
Hello,
address@hidden (Ludovic Courtès) writes:
> Another solution might be to add heuristics to avoid margin-kerning
> glyphs that are "unreasonably wide". "Unreasonably wide" could be
> defined as "wider than `x'" for instance. This same heuristic could be
> applied to subsequent characters also. For instance, a quote followed
> by a comma may be narrower than `x' and therefore eligible as a whole
> for margin kerning. On the contrary, the ellipsis glyph may be too wide
> and will never be margin-kerned.
I implemented the idea above which is actually based on Jeff's
suggestion. So now, subsequent "small" glyphs can be margin-kerned,
i.e. margin kerning is not limited to one character. Additionally,
characters "too wide" (i.e. wider than `x'), like ellipses, are not
margin-kerned.
How this impacts the sample I posted can be seen here:
http://www.laas.fr/~lcourtes/software/lout/margin-kerning-test.pdf
http://www.laas.fr/~lcourtes/software/lout/margin-kerning-test.old.pdf
For the sake of readability and maintainability, I've also separated the
margin kerning code.
Below is a brand new patch against Lout 3.30 + the ligature patch.
Please, let me know whether this looks good on actual documents.
Thanks,
Ludovic.
--- orig/externs.h
+++ mod/externs.h
@@ -574,6 +574,8 @@
#define STR_BREAK_NOLAST AsciiToFull("unbreakablelast")
#define STR_BREAK_LAST AsciiToFull("breakablelast")
#define STR_BREAK_SETOUTDENT AsciiToFull("setoutdent")
+#define STR_BREAK_MARGINKERNING AsciiToFull("marginkerning")
+#define STR_BREAK_NOMARGINKERNING AsciiToFull("nomarginkerning")
#define STR_SPACE_LOUT AsciiToFull("lout")
#define STR_SPACE_COMPRESS AsciiToFull("compress")
@@ -674,6 +676,7 @@
BOOLEAN onobreaklast : 1; /* no break after last line of para */
BOOLEAN obaselinemark : 1; /* baseline metrics */
BOOLEAN oligatures : 1; /* use ligatures */
+ BOOLEAN omarginkerning : 1; /* perform margin kerning */
} STYLE;
#define line_gap(x) (x).osu1.oline_gap
@@ -695,35 +698,37 @@
#define nobreaklast(x) (x).onobreaklast
#define baselinemark(x) (x).obaselinemark
#define ligatures(x) (x).oligatures
+#define marginkerning(x) (x).omarginkerning
#define yunit(x) (x).oyunit
#define zunit(x) (x).ozunit
#define outdent_len(x) (x).ooutdent_len
#define smallcaps_len(x)(x).osmallcaps_len
-#define StyleCopy(x, y)
\
-( GapCopy(line_gap(x), line_gap(y)), \
- hyph_style(x) = hyph_style(y), \
- fill_style(x) = fill_style(y), \
- display_style(x) = display_style(y), \
- small_caps(x) = small_caps(y), \
- GapCopy(space_gap(x), space_gap(y)), \
- font(x) = font(y), \
- colour(x) = colour(y), \
- texture(x) = texture(y), \
- outline(x) = outline(y), \
- language(x) = language(y), \
- nobreakfirst(x) = nobreakfirst(y), \
- nobreaklast(x) = nobreaklast(y), \
- baselinemark(x) = baselinemark(y), \
- ligatures(x) = ligatures(y), \
- vadjust(x) = vadjust(y), \
- hadjust(x) = hadjust(y), \
- padjust(x) = padjust(y), \
- space_style(x) = space_style(y), \
- yunit(x) = yunit(y), \
- zunit(x) = zunit(y), \
- outdent_len(x) = outdent_len(y), \
- smallcaps_len(x) = smallcaps_len(y) \
+#define StyleCopy(x, y) \
+( GapCopy(line_gap(x), line_gap(y)), \
+ hyph_style(x) = hyph_style(y), \
+ fill_style(x) = fill_style(y), \
+ display_style(x) = display_style(y), \
+ small_caps(x) = small_caps(y), \
+ GapCopy(space_gap(x), space_gap(y)), \
+ font(x) = font(y), \
+ colour(x) = colour(y), \
+ texture(x) = texture(y), \
+ outline(x) = outline(y), \
+ language(x) = language(y), \
+ nobreakfirst(x) = nobreakfirst(y), \
+ nobreaklast(x) = nobreaklast(y), \
+ marginkerning(x) = marginkerning(y), \
+ baselinemark(x) = baselinemark(y), \
+ ligatures(x) = ligatures(y), \
+ vadjust(x) = vadjust(y), \
+ hadjust(x) = hadjust(y), \
+ padjust(x) = padjust(y), \
+ space_style(x) = space_style(y), \
+ yunit(x) = yunit(y), \
+ zunit(x) = zunit(y), \
+ outdent_len(x) = outdent_len(y), \
+ smallcaps_len(x) = smallcaps_len(y) \
)
@@ -3379,6 +3384,8 @@
extern void FontAdvanceCurrentPage(void);
extern void FontPageUsed(OBJECT face);
extern BOOLEAN FontNeeded(FILE *fp);
+extern FULL_LENGTH FontGlyphHeight(FONT_NUM fnum, FULL_CHAR chr);
+extern FULL_LENGTH FontGlyphWidth(FONT_NUM fnum, FULL_CHAR chr);
/***** z38.c Character Mappings **************************************/
extern MAP_VEC MapTable[];
--- orig/z07.c
+++ mod/z07.c
@@ -136,6 +136,17 @@
{ OBJECT res;
NewWord(res, typ, StringLength(str), pos);
StringCopy(string(res), str);
+
+ word_font(res) = 0;
+ word_colour(res) = 0;
+ word_texture(res) = 0;
+ word_outline(res) = 0;
+ word_language(res) = 0;
+ word_baselinemark(res) = 0;
+ word_ligatures(res) = 0;
+ word_hyph(res) = 0;
+ underline(res) = 0;
+
FposCopy(fpos(res), *pos);
debug4(DOS, DDD, "MakeWord(%s, %s, %s) returning %s",
Image(typ), str, EchoFilePos(pos), EchoObject(res));
--- orig/z11.c
+++ mod/z11.c
@@ -84,6 +84,7 @@
sprintf(buff, ":C%d:P%d", colour(*style), texture(*style));
StringCat(res, AsciiToFull(buff));
StringCat(res, AsciiToFull("]"));
+ /* FIXME: Handle `marginkerning'. */
return res;
} /* end EchoStyle */
#endif
@@ -212,6 +213,10 @@
nobreaklast(*style) = TRUE;
else if( StringEqual(string(x), STR_BREAK_LAST) )
nobreaklast(*style) = FALSE;
+ else if( StringEqual(string(x), STR_BREAK_MARGINKERNING) )
+ marginkerning(*style) = TRUE;
+ else if( StringEqual(string(x), STR_BREAK_NOMARGINKERNING) )
+ marginkerning(*style) = FALSE;
else Error(11, 5, "found unknown option to %s symbol (%s)",
WARN, &fpos(x), KW_BREAK, string(x));
}
--- orig/z14.c
+++ mod/z14.c
@@ -176,7 +176,7 @@
\
/* set right link and calculate badness of the new interval */ \
if( rlink != x ) \
- { \
+ { \
assert( Up(newg) == LastUp(newg), "MoveRightToGap: newg!" ); \
/* set save_space(newg) now so that it is OK to forget right */ \
debug0(DOF, DDD, " MoveRightToGap setting save_space(newg)"); \
@@ -211,7 +211,7 @@
{ if( hyph_allowed ) \
{
\
/* hyphenation is allowed, so add hyph_word to nat_width */ \
- if( is_word(type(right)) && \
+ if( is_word(type(right)) && \
!(string(right)[StringLength(string(right))-1] == CH_HYPHEN) ) \
{ \
/* make sure hyph_word exists and is of the right font */ \
@@ -234,7 +234,8 @@
} \
\
mode(gap(newg)) = ADD_HYPH; \
- I.nat_width += size(hyph_word, COLM); \
+ if( marginkerning(save_style(x)) == FALSE ) \
+ I.nat_width += size(hyph_word, COLM); \
debug0(DOF, DDD, " adding hyph_word from nat_width"); \
} \
}
\
@@ -256,7 +257,7 @@
if( unbreakable_at_right ) I.class = UNBREAKABLE_RIGHT; \
else if( I.class == TIGHT && mode(gap(newg)) == TAB_MODE ) \
I.class = TOO_TIGHT, I.badness = TOO_TIGHT_BAD; \
- debug0(DOF, DDD, "MoveRightToGap returning.");
\
+ debug0(DOF, DDD, "MoveRightToGap returning."); \
}
/*@::IntervalInit(), IntervalShiftRightEnd()@*********************************/
@@ -300,7 +301,7 @@
/* */
/*****************************************************************************/
-#define IntervalShiftRightEnd(I, x, hyph_word, max_width, etc_width) \
+#define IntervalShiftRightEnd(I, x, hyph_word, max_width, etc_width) \
{ OBJECT rlink, g, right = nilobj; \
assert( I.class != AT_END, "IntervalShiftRightEnd: AT_END!" ); \
rlink = I.rlink; \
@@ -317,7 +318,9 @@
/* if hyphenation case, must take away width of hyph_word */ \
/* and increase the badness to discourage breaks at this point */ \
if( mode(gap(g)) == ADD_HYPH ) \
- { I.nat_width -= size(hyph_word,COLM); \
+ { if( marginkerning(save_style(x)) == FALSE ) \
+ I.nat_width -= size(hyph_word,COLM); \
+ \
save_badness(g) += HYPH_BAD_INCR;
\
debug0(DOF, DDD, " subtracting hyph_word from nat_width"); \
} \
@@ -496,6 +499,237 @@
#endif
+/* Return true if CHAR's glyph height in font FONTNUM is "small". A glyph's
+ height is considered small when it is lower than or equal to three fourth
+ of the height of the glyph for `x'. Initially, I considered that anything
+ strictly smaller than `x' would be enough, but some fonts (namely
+ Palatino) have a glyph for `v' which is slightly smaller than that for
+ `x', hence weird results. */
+#define SmallGlyphHeight(_fontnum, _char) \
+ ((FontGlyphHeight((_fontnum), (_char))) \
+ <= (3 * (FontGlyphHeight((_fontnum), 'x') >> 2)))
+
+
+/address@hidden ()@*****************************************************/
+/* */
+/* Perform left margin kerning of word FIRST_ON_LINE, whose parent is */
+/* PARENT. If FIRST_ON_LINE's first glyph(s) deserve margin kerning, then */
+/* a new word object containing this/these glyph(s) is prepended to */
+/* FIRST_ON_LINE in PARENT. */
+/* */
+/*****************************************************************************/
+static void KernWordLeftMargin(OBJECT first_on_line, OBJECT parent)
+{
+ /* It's a word: look at its first characters' glyph height. */
+ FONT_NUM font;
+ FULL_CHAR *word_content, *wordp;
+ FULL_CHAR kerned_glyphs[20];
+ FULL_LENGTH kerned_glyphs_width = 0;
+ unsigned kerned_glyph_count = 0, word_len;
+
+ assert( is_word( type(first_on_line) ), "KernWordLeftMargin");
+
+ font = word_font(first_on_line);
+ word_content = string(first_on_line);
+ word_len = StringLength(word_content);
+
+ if(font >= 1)
+ {
+ /* Determine how many subsequent characters beginning WORD_CONTENT
+ deserve margin kerning. Glyphs wider than the glyph for `x' will
+ _not_ be kerned at all, and only up to the width of the glyph for `x'
+ can be protruded in other cases. */
+ FULL_LENGTH x_width = FontGlyphWidth(font, 'x');
+
+ for(wordp = word_content;
+ (*wordp) && (SmallGlyphHeight(font, *wordp));
+ wordp++, kerned_glyph_count++)
+ {
+ FULL_LENGTH glyph_width;
+
+ glyph_width = FontGlyphWidth(font, *wordp);
+ if(kerned_glyphs_width + glyph_width > x_width)
+ break;
+ if(kerned_glyph_count >= sizeof(kerned_glyphs))
+ break;
+
+ kerned_glyphs_width += glyph_width;
+ kerned_glyphs[kerned_glyph_count] = *wordp;
+ }
+
+ kerned_glyphs[kerned_glyph_count] = '\0';
+ }
+
+ if(kerned_glyph_count > 0)
+ { OBJECT z;
+ FULL_CHAR *unacc = NULL;
+ FULL_LENGTH glyph_width;
+
+ debug2(DOF, DD, " margin-kerning %u glyph from "
+ "word \"%s\" (left margin)",
+ kerned_glyph_count, word_content);
+
+ /* Get font information. */
+ if(finfo[font].font_table)
+ {
+ MAPPING m;
+ m = font_mapping(finfo[font].font_table);
+ unacc = MapTable[m]->map[MAP_UNACCENTED];
+ }
+
+ /* Add the first characters. */
+ z = MakeWord(WORD, kerned_glyphs, &fpos(first_on_line));
+ word_font(z) = word_font(first_on_line);
+ word_colour(z) = word_colour(first_on_line);
+ word_texture(z) = word_texture(first_on_line);
+ word_outline(z) = word_outline(first_on_line);
+ word_language(z) = word_language(first_on_line);
+ word_baselinemark(z) = word_baselinemark(first_on_line);
+ word_ligatures(z) = word_ligatures(first_on_line);
+ word_hyph(z) = hyph_style(save_style(z)) == HYPH_ON;
+ underline(z) = underline(first_on_line);
+ FontWordSize(z);
+
+ /* Make it zero-width.
+ FIXME: This awful trick allows Z to expand outside of the
+ paragraph itself while still appearing as having a null
+ width. */
+ glyph_width = size(z, COLM);
+ back(z, COLM) = -glyph_width;
+ fwd(z, COLM) = glyph_width;
+ Link(parent, z);
+
+ /* Add a zero-width gap object. */
+ New(z, GAP_OBJ);
+ vspace(z) = 0;
+ if ((word_content[kerned_glyph_count]) && (unacc))
+ hspace(z) = FontKernLength(font, unacc,
+ word_content[kerned_glyph_count - 1],
+ word_content[kerned_glyph_count]);
+ else
+ hspace(z) = 0;
+ underline(z) = underline(first_on_line);
+ SetGap(gap(z), TRUE, FALSE, TRUE, FIXED_UNIT, EDGE_MODE, 0);
+ Link(parent, z);
+
+ /* Remove the first char from FIRST_ON_LINE and recompute its
+ size. */
+ {
+ unsigned s;
+ for (s = 0; s < word_len - kerned_glyph_count; s++)
+ word_content[s] = word_content[s + kerned_glyph_count];
+ word_content[word_len - kerned_glyph_count] = '\0';
+ }
+ FontWordSize(first_on_line);
+ }
+}
+
+/address@hidden ()@*****************************************************/
+/* */
+/* Perform right margin kerning of word LAST_ON_LINE, whose parent is */
+/* PARENT. If LAST_ON_LINE's first glyph(s) deserve margin kerning, then */
+/* a new word object containing this/these glyph(s) is prepended to */
+/* LAST_ON_LINE in PARENT. */
+/* */
+/*****************************************************************************/
+static void KernWordRightMargin(OBJECT last_on_line, OBJECT parent)
+{
+ FONT_NUM font;
+ FULL_CHAR *word_content, *wordp;
+ FULL_CHAR kerned_glyphs[20];
+ FULL_LENGTH kerned_glyphs_width = 0;
+ unsigned kerned_glyph_count = 0, word_len;
+
+ assert( is_word( type(last_on_line) ), "KernWordRightMargin");
+
+ font = word_font(last_on_line);
+ word_content = string(last_on_line);
+ word_len = StringLength(word_content);
+
+ if(font >= 1)
+ {
+ /* Determine how many subsequent characters ending WORD_CONTENT deserve
+ margin kerning. Glyphs wider than the glyph for `x' will _not_ be
+ kerned at all, and only up to the width of the glyph for `x' can be
+ protruded in other cases. */
+ FULL_LENGTH x_width = FontGlyphWidth(font, 'x');
+
+ for(wordp = &word_content[word_len - 1];
+ (wordp >= word_content) && (SmallGlyphHeight(font, *wordp));
+ wordp--, kerned_glyph_count++)
+ {
+ FULL_LENGTH glyph_width;
+
+ glyph_width = FontGlyphWidth(font, *wordp);
+ if(kerned_glyphs_width + glyph_width > x_width)
+ break;
+ if(kerned_glyph_count >= sizeof(kerned_glyphs))
+ break;
+
+ kerned_glyphs_width += glyph_width;
+ kerned_glyphs[kerned_glyph_count] = *wordp;
+ }
+
+ kerned_glyphs[kerned_glyph_count] = '\0';
+ }
+
+ if(kerned_glyph_count > 0)
+ { OBJECT z;
+ FULL_CHAR *unacc = NULL;
+
+ /* Get font information. */
+ if (finfo[font].font_table)
+ {
+ MAPPING m;
+ m = font_mapping(finfo[font].font_table);
+ unacc = MapTable[m]->map[MAP_UNACCENTED];
+ }
+
+ debug2(DOF, DD, " margin-kerning %u glyph from "
+ "word \"%s\" (right margin)",
+ kerned_glyph_count, word_content);
+
+ /* Add a zero-width gap object. */
+ New(z, GAP_OBJ);
+ vspace(z) = 0;
+ if((word_len > 1) && (unacc))
+ hspace(z) = FontKernLength(font, unacc,
+ word_content[word_len - kerned_glyph_count -
1],
+ word_content[word_len - kerned_glyph_count]);
+ else
+ hspace(z) = 0;
+ underline(z) = underline(last_on_line);
+ SetGap(gap(z), TRUE, FALSE, TRUE, FIXED_UNIT, EDGE_MODE, 0);
+ Link(parent, z);
+
+ /* Add the last characters. */
+ z = MakeWord(WORD, &word_content[word_len - kerned_glyph_count],
+ &fpos(last_on_line));
+ word_font(z) = word_font(last_on_line);
+ word_colour(z) = word_colour(last_on_line);
+ word_texture(z) = word_texture(last_on_line);
+ word_outline(z) = word_outline(last_on_line);
+ word_language(z) = word_language(last_on_line);
+ word_baselinemark(z) = word_baselinemark(last_on_line);
+ word_ligatures(z) = word_ligatures(last_on_line);
+ word_hyph(z) = hyph_style(save_style(last_on_line)) == HYPH_ON;
+ underline(z) = underline(last_on_line);
+
+ FontWordSize(z);
+
+ /* Make it zero-width. */
+ fwd(z, COLM) = 0;
+ back(z, COLM) = 0;
+
+ /* Remove the last char from LAST_ON_LINE and recompute its
+ size. */
+ word_content[word_len - kerned_glyph_count] = '\0';
+ FontWordSize(last_on_line);
+
+ Link(parent, z);
+ }
+}
+
/*@::FillObject()@************************************************************/
/* */
/* FillObject(x, c, multi, can_hyphenate, allow_shrink, extend_unbreakable, */
@@ -748,6 +982,18 @@
back(y, COLM) = 0;
fwd(y, COLM) = max_width;
+ if( marginkerning( save_style(x) ) )
+ { /* Margin kerning: look at this line's first character. */
+ OBJECT first_on_line, parent;
+
+ /* Get the first object on this line. */
+ parent = NextDown(llink);
+ Child(first_on_line, parent);
+
+ if( is_word( type(first_on_line) ) )
+ KernWordLeftMargin(first_on_line, parent);
+ }
+
/* if outdented paragraphs, add 2.0f @Wide & to front of new line */
if( display_style(save_style(x)) == DISPLAY_OUTDENT ||
display_style(save_style(x)) == DISPLAY_ORAGGED )
@@ -784,17 +1030,37 @@
Child(lgap, llink);
if( mode(gap(lgap)) == ADD_HYPH )
{ OBJECT z, tmp;
+ FONT_NUM font;
+ FULL_CHAR *unacc = NULL, *word_content;
+ unsigned word_len;
/* find word hyphen attaches to, since need its underline and font */
Child(tmp, PrevDown(LastDown(x))); /* last is lgap, so one before */
debug2(DOF, D, "tmp = %s %s", Image(type(tmp)), EchoObject(tmp));
assert(is_word(type(tmp)), "FillObject: !is_word(type(tmp))!");
+ word_content = string(tmp);
+ word_len = StringLength(word_content);
+
+ /* get font information */
+ font = word_font(tmp);
+ if (finfo[font].font_table)
+ {
+ MAPPING m;
+ m = font_mapping(finfo[font].font_table);
+ unacc = MapTable[m]->map[MAP_UNACCENTED];
+ }
/* add zero-width gap object */
New(z, GAP_OBJ);
debug0(DOF, DD, " adding hyphen");
debug0(DOF, DD, "");
- hspace(z) = vspace(z) = 0;
+ vspace(z) = 0;
+ if (unacc)
+ hspace(z) = FontKernLength(font, unacc,
+ word_content[word_len - 1], CH_HYPHEN);
+ else
+ hspace(z) = 0;
+
underline(z) = underline(tmp);
SetGap(gap(z), TRUE, FALSE, TRUE, FIXED_UNIT, EDGE_MODE, 0);
Link(x, z);
@@ -810,9 +1076,27 @@
word_ligatures(z) = word_ligatures(tmp);
word_hyph(z) = hyph_style(save_style(x)) == HYPH_ON;
underline(z) = underline(tmp);
+
FontWordSize(z);
+ if( marginkerning(save_style(x)) )
+ {
+ /* Margin kerning: set the hyphen's width to zero. */
+ back(z, COLM) = 0;
+ fwd(z, COLM) = 0;
+ }
+
Link(x, z);
}
+ else if( marginkerning(save_style(x)) )
+ {
+ /* Margin kerning: look at the height of this line's last char. */
+ OBJECT last_on_line;
+
+ /* Get the last object on this line. */
+ Child(last_on_line, PrevDown(LastDown(x)));
+ if( is_word(type(last_on_line)) )
+ KernWordRightMargin(last_on_line, x);
+ }
/* attach y to res, recycle lgap for gap separating the two lines */
Link(NextDown(res), y);
@@ -906,7 +1190,7 @@
word_baselinemark(prev) == word_baselinemark(next) &&
word_ligatures(prev) == word_ligatures(next) &&
underline(prev) == underline(next) )
- {
+ {
debug2(DOF, DD, "joining %s with %s", EchoObject(prev),
EchoObject(next));
typ = type(prev) == QWORD || type(next) == QWORD ? QWORD : WORD;
@@ -919,7 +1203,9 @@
word_baselinemark(tmp) = word_baselinemark(prev);
word_ligatures(tmp) = word_ligatures(prev);
word_hyph(tmp) = word_hyph(prev);
+
FontWordSize(tmp);
+
underline(tmp) = underline(prev);
MoveLink(ylink, tmp, CHILD);
DisposeChild(Up(prev));
--- orig/z18.c
+++ mod/z18.c
@@ -89,6 +89,7 @@
smallcaps_len(InitialStyle) = 0.7 * FR; /* i.e. 0.7 scale */
nobreakfirst(InitialStyle) = FALSE;
nobreaklast(InitialStyle) = FALSE;
+ marginkerning(InitialStyle) = FALSE; /* disabled by dft */
baselinemark(InitialStyle) = FALSE; /* i.e. not baseline */
ligatures(InitialStyle) = TRUE; /* i.e. ligatures */
--- orig/z37.c
+++ mod/z37.c
@@ -1984,3 +1984,44 @@
}
return first_need;
} /* end FontNeeded */
+
+
+/* FIXME: Obviously, the next two functions should rather be inlined. */
+
+/*@::FontGlyphHeight()@*******************************************************/
+/* */
+/* SHORT_LENGTH FontGlyphHeight(fnum, chr) */
+/* */
+/* Return the height of the glyph that corresponds to character chr in */
+/* font fnum. */
+/* */
+/*****************************************************************************/
+
+FULL_LENGTH FontGlyphHeight(FONT_NUM fnum, FULL_CHAR chr)
+{
+ struct metrics *fnt;
+
+ assert ((fnum >= 1) && (fnum <= font_count), "FontGlyphHeight");
+
+ fnt = finfo[fnum].size_table;
+ return (fnt ? fnt[chr].up - fnt[chr].down : 0);
+}
+
+/*@::FontGlyphWidth()@*******************************************************/
+/* */
+/* SHORT_LENGTH FontGlyphWidth(fnum, chr) */
+/* */
+/* Return the width of the glyph that corresponds to character chr in */
+/* font fnum. */
+/* */
+/*****************************************************************************/
+
+FULL_LENGTH FontGlyphWidth(FONT_NUM fnum, FULL_CHAR chr)
+{
+ struct metrics *fnt;
+
+ assert ((fnum >= 1) && (fnum <= font_count), "FontGlyphWidth");
+
+ fnt = finfo[fnum].size_table;
+ return (fnt ? fnt[chr].right - fnt[chr].left : 0);
+}
--- orig/z46.c
+++ mod/z46.c
@@ -180,7 +180,10 @@
New(opt_constraints(hd), ACAT);
StyleCopy(save_style(opt_components(hd)), *style);
if( gall_dir(hd) == ROWM )
+ {
hyph_style(save_style(opt_components(hd))) = HYPH_OFF;
+ marginkerning(save_style(opt_components(hd))) = 0;
+ }
debug0(DOG, D, "SetOptimize returning:");
ifdebug(DOG, D, DebugOptimize(hd));
- Margin kerning, patch #2,
Ludovic Courtès <=