summaryrefslogtreecommitdiff
path: root/pango
diff options
context:
space:
mode:
authorBehdad Esfahbod <behdad@gnome.org>2007-05-02 23:21:48 +0000
committerBehdad Esfahbod <behdad@src.gnome.org>2007-05-02 23:21:48 +0000
commite3eee491d7434dda314b78fad127880943d9c1ba (patch)
treeaac65921f24f0f2846c73f048c660e284a93d95d /pango
parent86c5228a67df1d3028439fa1b97310e613e36fee (diff)
downloadpango-e3eee491d7434dda314b78fad127880943d9c1ba.tar.gz
Fix letter-spacing with justification. We now distribute letter-spacing
2007-05-02 Behdad Esfahbod <behdad@gnome.org> * pango/pango-glyph-item.c (pango_glyph_item_letter_space): * pango/pango-item.c (pango_item_split): * pango/pango-layout.c (distribute_letter_spacing), (shape_run), (debug), (process_item), (process_line), (pad_glyphstring_right), (pad_glyphstring_left), (zero_line_final_space), (adjust_line_letter_spacing), (pango_layout_line_postprocess): Fix letter-spacing with justification. We now distribute letter-spacing equally on both sides of clusters, instead of putting it all on one side. svn path=/trunk/; revision=2253
Diffstat (limited to 'pango')
-rw-r--r--pango/pango-glyph-item.c43
-rw-r--r--pango/pango-item.c1
-rw-r--r--pango/pango-layout.c173
3 files changed, 175 insertions, 42 deletions
diff --git a/pango/pango-glyph-item.c b/pango/pango-glyph-item.c
index 6d5c5d98..157bb150 100644
--- a/pango/pango-glyph-item.c
+++ b/pango/pango-glyph-item.c
@@ -615,19 +615,50 @@ pango_glyph_item_letter_space (PangoGlyphItem *glyph_item,
int letter_spacing)
{
PangoGlyphItemIter iter;
+ PangoGlyphInfo *glyphs = glyph_item->glyphs->glyphs;
gboolean have_cluster;
+ int space_left, space_right;
+
+ space_left = letter_spacing / 2;
+
+ /* hinting */
+ if ((letter_spacing & (PANGO_SCALE - 1)) == 0)
+ {
+ space_left = PANGO_UNITS_ROUND (space_left);
+ }
+
+ space_right = letter_spacing - space_left;
for (have_cluster = _pango_glyph_item_iter_init_start (&iter, glyph_item, text);
have_cluster;
have_cluster = _pango_glyph_item_iter_next_cluster (&iter))
{
- if (iter.start_char > 0 &&
- log_attrs[iter.start_char].is_cursor_position)
+ if (!log_attrs[iter.start_char].is_cursor_position)
+ continue;
+
+ if (iter.start_glyph < iter.end_glyph) /* LTR */
+ {
+ if (iter.start_char > 0)
+ {
+ glyphs[iter.start_glyph].geometry.width += space_left ;
+ glyphs[iter.start_glyph].geometry.x_offset += space_left ;
+ }
+ if (iter.end_char < glyph_item->item->num_chars)
+ {
+ glyphs[iter.end_glyph-1].geometry.width += space_right;
+ }
+ }
+ else /* RTL */
{
- if (iter.start_glyph < iter.end_glyph) /* LTR */
- glyph_item->glyphs->glyphs[iter.start_glyph - 1].geometry.width += letter_spacing;
- else /* RTL */
- glyph_item->glyphs->glyphs[iter.start_glyph].geometry.width += letter_spacing;
+ if (iter.start_char > 0)
+ {
+ glyphs[iter.start_glyph].geometry.width += space_right;
+ }
+ if (iter.end_char < glyph_item->item->num_chars)
+ {
+ glyphs[iter.end_glyph+1].geometry.x_offset += space_left ;
+ glyphs[iter.end_glyph+1].geometry.width += space_left ;
+ }
}
}
}
diff --git a/pango/pango-item.c b/pango/pango-item.c
index 177d60d0..4ad76eee 100644
--- a/pango/pango-item.c
+++ b/pango/pango-item.c
@@ -137,7 +137,6 @@ pango_item_split (PangoItem *orig,
PangoItem *new_item;
g_return_val_if_fail (orig != NULL, NULL);
- g_return_val_if_fail (orig->length > 0, NULL);
g_return_val_if_fail (split_index > 0, NULL);
g_return_val_if_fail (split_index < orig->length, NULL);
g_return_val_if_fail (split_offset > 0, NULL);
diff --git a/pango/pango-layout.c b/pango/pango-layout.c
index d2d96f48..0993adc2 100644
--- a/pango/pango-layout.c
+++ b/pango/pango-layout.c
@@ -2976,6 +2976,20 @@ can_break_in (PangoLayout *layout,
return FALSE;
}
+static inline void
+distribute_letter_spacing (int letter_spacing,
+ int *space_left,
+ int *space_right)
+{
+ *space_left = letter_spacing / 2;
+ /* hinting */
+ if ((letter_spacing & (PANGO_SCALE - 1)) == 0)
+ {
+ *space_left = PANGO_UNITS_ROUND (*space_left);
+ }
+ *space_right = letter_spacing - *space_left;
+}
+
typedef enum
{
BREAK_NONE_FIT,
@@ -3026,6 +3040,7 @@ shape_run (PangoLayoutLine *line,
if (state->properties.letter_spacing)
{
PangoGlyphItem glyph_item;
+ int space_left, space_right;
glyph_item.item = item;
glyph_item.glyphs = glyphs;
@@ -3035,11 +3050,11 @@ shape_run (PangoLayoutLine *line,
layout->log_attrs + state->start_offset,
state->properties.letter_spacing);
- /* We put all the letter spacing after the last glyph, then
- * will go back and redistribute it at the beginning and the
- * end in a post-processing step over the whole line.
- */
- glyphs->glyphs[glyphs->num_glyphs - 1].geometry.width += state->properties.letter_spacing;
+ distribute_letter_spacing (state->properties.letter_spacing, &space_left, &space_right);
+
+ glyphs->glyphs[0].geometry.width += space_left;
+ glyphs->glyphs[0].geometry.x_offset += space_left;
+ glyphs->glyphs[glyphs->num_glyphs - 1].geometry.width += space_right;
}
}
@@ -3073,6 +3088,35 @@ insert_run (PangoLayoutLine *line,
line->length += run_item->length;
}
+#if 0
+# define DEBUG debug
+void
+debug (const char *where, PangoLayoutLine *line, ParaBreakState *state)
+{
+ int line_width = 0;
+
+ GSList *tmp_list;
+
+ tmp_list = line->runs;
+ while (tmp_list)
+ {
+ PangoLayoutRun *run = tmp_list->data;
+
+ line_width += pango_glyph_string_get_width (run->glyphs);
+
+ tmp_list = tmp_list->next;
+ }
+
+ g_message ("rem %d + line %d = %d %s",
+ state->remaining_width,
+ line_width,
+ state->remaining_width + line_width,
+ where);
+}
+#else
+# define DEBUG(where, line, state) do { } while (0)
+#endif
+
/* Tries to insert as much as possible of the item at the head of
* state->items onto @line. Five results are possible:
*
@@ -3139,8 +3183,7 @@ process_item (PangoLayout *layout,
width = 0;
if (processing_new_item)
{
- for (i = 0; i < state->glyphs->num_glyphs; i++)
- width += state->glyphs->glyphs[i].geometry.width;
+ width = pango_glyph_string_get_width (state->glyphs);
}
else
{
@@ -3171,13 +3214,6 @@ process_item (PangoLayout *layout,
pango_glyph_string_get_logical_widths (state->glyphs,
layout->text + item->offset, item->length, item->analysis.level,
state->log_widths);
-
- /* The extra run letter spacing is actually divided after
- * the last and before the first, but it works to
- * account it all on the last
- */
- if (item->num_chars > 0)
- state->log_widths[item->num_chars - 1] += state->properties.letter_spacing;
}
retry_break:
@@ -3186,7 +3222,7 @@ process_item (PangoLayout *layout,
*/
while (--num_chars >= 0)
{
- width -= (state->log_widths)[state->log_widths_offset + num_chars];
+ width -= state->log_widths[state->log_widths_offset + num_chars];
/* If there are no previous runs we have to take care to grab at least one char. */
if (can_break_at (layout, state->start_offset + num_chars, retrying_with_char_breaks) &&
@@ -3231,13 +3267,21 @@ process_item (PangoLayout *layout,
else
{
PangoItem *new_item;
+ int new_break_width;
length = g_utf8_offset_to_pointer (layout->text + item->offset, break_num_chars) - (layout->text + item->offset);
new_item = pango_item_split (item, length, break_num_chars);
+
+ /* reshaping may slightly change the item width */
+
+ state->remaining_width += break_width;
insert_run (line, state, new_item, FALSE);
+ break_width = pango_glyph_string_get_width (((PangoGlyphItem *)(line->runs->data))->glyphs);
+ state->remaining_width -= break_width;
+
state->log_widths_offset += break_num_chars;
/* Shaped items should never be broken */
@@ -3338,6 +3382,7 @@ process_line (PangoLayout *layout,
state->remaining_width = (layout->indent >= 0) ? layout->width - layout->indent : layout->width;
else
state->remaining_width = (layout->indent >= 0) ? layout->width : layout->width + layout->indent;
+ DEBUG ("starting to fill line", line, state);
while (state->items)
{
@@ -4604,10 +4649,43 @@ get_item_letter_spacing (PangoItem *item)
}
static void
-adjust_final_space (PangoGlyphString *glyphs,
- int adjustment)
+pad_glyphstring_right (PangoGlyphString *glyphs,
+ ParaBreakState *state,
+ int adjustment)
{
- glyphs->glyphs[glyphs->num_glyphs - 1].geometry.width += adjustment;
+ int glyph = glyphs->num_glyphs - 1;
+
+ while (glyph >= 0 && glyphs->glyphs[glyph].geometry.width == 0)
+ glyph--;
+
+ if (glyph < 0)
+ return;
+
+ state->remaining_width -= adjustment;
+ glyphs->glyphs[glyph].geometry.width += adjustment;
+ if (glyphs->glyphs[glyph].geometry.width < 0)
+ {
+ state->remaining_width += glyphs->glyphs[glyph].geometry.width;
+ glyphs->glyphs[glyph].geometry.width = 0;
+ }
+}
+
+static void
+pad_glyphstring_left (PangoGlyphString *glyphs,
+ ParaBreakState *state,
+ int adjustment)
+{
+ int glyph = 0;
+
+ while (glyph < glyphs->num_glyphs && glyphs->glyphs[glyph].geometry.width == 0)
+ glyph++;
+
+ if (glyph == glyphs->num_glyphs)
+ return;
+
+ state->remaining_width -= adjustment;
+ glyphs->glyphs[glyph].geometry.width += adjustment;
+ glyphs->glyphs[glyph].geometry.x_offset += adjustment;
}
static gboolean
@@ -4640,6 +4718,7 @@ zero_line_final_space (PangoLayoutLine *line,
if (p != layout->text + item->offset + glyphs->log_clusters[glyph])
return;
+ state->remaining_width += glyphs->glyphs[glyph].geometry.width;
glyphs->glyphs[glyph].geometry.width = 0;
}
@@ -4656,7 +4735,8 @@ zero_line_final_space (PangoLayoutLine *line,
* cases.
*/
static void
-adjust_line_letter_spacing (PangoLayoutLine *line)
+adjust_line_letter_spacing (PangoLayoutLine *line,
+ ParaBreakState *state)
{
PangoLayout *layout = line->layout;
gboolean reversed;
@@ -4700,7 +4780,7 @@ adjust_line_letter_spacing (PangoLayoutLine *line)
if (is_tab_run (layout, run))
{
- adjust_final_space (run->glyphs, tab_adjustment);
+ pad_glyphstring_right (run->glyphs, state, tab_adjustment);
tab_adjustment = 0;
}
else
@@ -4708,19 +4788,32 @@ adjust_line_letter_spacing (PangoLayoutLine *line)
PangoLayoutRun *visual_next_run = reversed ? last_run : next_run;
PangoLayoutRun *visual_last_run = reversed ? next_run : last_run;
int run_spacing = get_item_letter_spacing (run->item);
- int adjustment = run_spacing / 2;
+ int space_left, space_right;
- if (visual_last_run && !is_tab_run (layout, visual_last_run))
- adjust_final_space (visual_last_run->glyphs, adjustment);
- else
- tab_adjustment += adjustment;
+ distribute_letter_spacing (run_spacing, &space_left, &space_right);
- if (visual_next_run && !is_tab_run (layout, visual_next_run))
- adjust_final_space (run->glyphs, - adjustment);
- else
+ if (run->glyphs->glyphs[0].geometry.width == 0)
+ {
+ /* we've zeroed this space glyph at the end of line, now remove
+ * the letter spacing added to its adjacent glyph */
+ pad_glyphstring_left (run->glyphs, state, - space_left);
+ }
+ else if (!visual_last_run || is_tab_run (layout, visual_last_run))
{
- adjust_final_space (run->glyphs, - run_spacing);
- tab_adjustment += run_spacing - adjustment;
+ pad_glyphstring_left (run->glyphs, state, - space_left);
+ tab_adjustment += space_left;
+ }
+
+ if (run->glyphs->glyphs[run->glyphs->num_glyphs - 1].geometry.width == 0)
+ {
+ /* we've zeroed this space glyph at the end of line, now remove
+ * the letter spacing added to its adjacent glyph */
+ pad_glyphstring_right (run->glyphs, state, - space_right);
+ }
+ else if (!visual_next_run || is_tab_run (layout, visual_next_run))
+ {
+ pad_glyphstring_right (run->glyphs, state, - space_right);
+ tab_adjustment += space_right;
}
}
@@ -4838,6 +4931,8 @@ pango_layout_line_postprocess (PangoLayoutLine *line,
*/
line->runs = g_slist_reverse (line->runs);
+ DEBUG ("postprocessing", line, state);
+
/* Ellipsize the line if necessary
*/
if (_pango_layout_line_ellipsize (line, state->attrs))
@@ -4848,18 +4943,26 @@ pango_layout_line_postprocess (PangoLayoutLine *line,
if (wrapped)
zero_line_final_space (line, state, last_run);
+ DEBUG ("after removing final space", line, state);
+
/* Now convert logical to visual order
*/
pango_layout_line_reorder (line);
+ DEBUG ("after reordering", line, state);
+
/* Fixup letter spacing between runs
*/
- adjust_line_letter_spacing (line);
+ adjust_line_letter_spacing (line, state);
+
+ DEBUG ("after letter spacing", line, state);
+
+ /* Distribute extra space between words if justifying and line was wrapped
+ */
+ if (wrapped && line->layout->justify)
+ justify_words (line, state);
- /* Distribute extra space between words if justifying and line was wrapped
- */
- if (wrapped && line->layout->justify)
- justify_words (line, state);
+ DEBUG ("after justification", line, state);
}
static void