This commit is contained in:
Vincent Wei
2019-03-25 12:15:23 +08:00
parent 390c5de6ef
commit 7dfe5954f5
9 changed files with 249 additions and 136 deletions
+2 -1
View File
@@ -12730,7 +12730,8 @@ typedef struct _LAYOUTLINE LAYOUTLINE;
MG_EXPORT LAYOUTINFO* GUIAPI CreateLayoutInfo(
const TEXTRUNSINFO* run_info, Uint32 render_flags,
const BreakOppo* break_oppos, BOOL persist_lines,
int letter_spacing, int word_spacing, int tab_size);
int letter_spacing, int word_spacing, int tab_size,
int* tabs, int nr_tabs);
/**
* Destroy the specified layout information structure.
+5 -4
View File
@@ -43,10 +43,11 @@
#ifndef GUI_UNICODE_OPS_H
#define GUI_UNICODE_OPS_H
#define UCHAR_SPACE 0x0020
#define UCHAR_SHY 0x00AD
#define UCHAR_IDSPACE 0x3000
#define UCHAR_TAB 0x0009
#define UCHAR_TAB 0x0009
#define UCHAR_SPACE 0x0020
#define UCHAR_SHY 0x00AD
#define UCHAR_IDSPACE 0x3000
#define UCHAR_LINE_SEPARATOR 0x2028
#define PAREN_STACK_DEPTH 128
-20
View File
@@ -353,24 +353,6 @@ static void shape_ellipsis (EllipsizeState *state)
state->ellipsis_width += glyphs->glyphs[i].width;
}
#if 0
/* Helper function to advance a PangoAttrIterator to a particular
* byte index.
*/
static void advance_iterator_to (PangoAttrIterator *iter, int new_index)
{
int start, end;
do
{
pango_attr_iterator_range (iter, &start, &end);
if (end > new_index)
break;
}
while (pango_attr_iterator_next (iter));
}
#endif
/* Updates the shaping of the ellipsis if necessary when we move the
* position of the start of the gap.
*
@@ -609,7 +591,6 @@ static void get_run_list (EllipsizeState *state, LAYOUTLINE* line)
if (run_iter->end_char != run_info->run->lrun->len) {
partial_end_run = run_info->run;
run_info->run = __mg_glyph_run_split (run_info->run,
state->layout->truninfo->ucs,
run_iter->end_index - run_info->run->lrun->si);
}
@@ -617,7 +598,6 @@ static void get_run_list (EllipsizeState *state, LAYOUTLINE* line)
run_iter = &state->gap_start_iter.run_iter;
if (run_iter->start_char != 0) {
partial_start_run = __mg_glyph_run_split (run_info->run,
state->layout->truninfo->ucs,
run_iter->start_index - run_info->run->lrun->si);
}
+11 -4
View File
@@ -124,6 +124,15 @@ void __mg_release_logfont_for_layout(const LAYOUTINFO* layout,
ReleaseRes(Str2Key(my_fontname));
}
/*
* Not like Pango, we simply use the properties of the referenced TextRun
* for the new orphan LayoutRun, including the direction, the orientation,
* the embedding level, and the language, except the script type.
*
* Therefore, we assume that the text of the new LayoutRun are all
* in the same script, same direction, and same orientation. This is
* enough for the ellipsis.
*/
LayoutRun* __mg_layout_run_new_orphan(const LAYOUTINFO* layout,
const TextRun* trun, const Uchar32* ucs, int nr_ucs)
{
@@ -142,7 +151,7 @@ LayoutRun* __mg_layout_run_new_orphan(const LAYOUTINFO* layout,
lrun->si = trun->si;
lrun->len = trun->len;
lrun->lc = trun->lc;
lrun->st = trun->st;
lrun->st = UCharGetScriptType(ucs[0]);
lrun->el = trun->el;
lrun->dir = trun->dir;
lrun->ort = trun->ort;
@@ -339,7 +348,6 @@ void __mg_glyph_run_free(GlyphRun* run)
/**
* __mg_glyph_run_split:
* @orig: a #GlyphRun
* @text: text to which positions in @orig apply
* @split_index: byte index of position to split item, relative to the start of the item
*
* Modifies @orig to cover only the text after @split_index, and
@@ -357,8 +365,7 @@ void __mg_glyph_run_free(GlyphRun* run)
* @split_index, which should be freed
* with pango_glyph_run_free().
**/
GlyphRun *__mg_glyph_run_split (GlyphRun *orig,
const Uchar32 *text, int split_index)
GlyphRun *__mg_glyph_run_split (GlyphRun *orig, int split_index)
{
GlyphRun *new_grun;
int i;
+188 -69
View File
@@ -56,7 +56,8 @@
LAYOUTINFO* GUIAPI CreateLayoutInfo(
const TEXTRUNSINFO* truninfo, Uint32 render_flags,
const BreakOppo* break_oppos, BOOL persist_lines,
int letter_spacing, int word_spacing, int tab_size)
int letter_spacing, int word_spacing, int tab_size,
int* tabs, int nr_tabs)
{
LAYOUTINFO* layout;
@@ -75,6 +76,8 @@ LAYOUTINFO* GUIAPI CreateLayoutInfo(
layout->ls = letter_spacing;
layout->ws = word_spacing;
layout->ts = tab_size;
layout->tabs = tabs;
layout->nr_tabs = nr_tabs;
INIT_LIST_HEAD(&layout->lines);
layout->nr_left_ucs = truninfo->nr_ucs;
@@ -152,10 +155,8 @@ struct _LayoutState {
const TextRun* trun;
// Current layout run
LayoutRun* lrun;
// Start index of line in current lrun */
int start_index_in_lrun;
// the number of not fit uchars in current text run
int left_ucs_in_lrun;
// Start index of layout run in current text run */
int start_index_in_trun;
// Goal width of line currently processing; < 0 is infinite
int line_width;
@@ -180,11 +181,134 @@ static BOOL should_ellipsize_current_line(LAYOUTINFO *layout,
int __mg_shape_layout_run(const TEXTRUNSINFO* info, const LayoutRun* run,
GlyphString* glyphs)
{
return 0;
if (!info->sei.shape(info->sei.inst, info, run, glyphs))
return 0;
return glyphs->nr_glyphs;
}
static void ensure_tab_width(LAYOUTINFO *layout)
{
if (layout->ts == -1) {
/* Find out how wide 8 spaces are in the context's default
* font.
*/
// TODO
/* We need to make sure the ts is > 0 so finding tab positions
* terminates. This check should be necessary only under extreme
* problems with the font.
*/
if (layout->ts <= 0)
layout->ts = 50; /* pretty much arbitrary */
}
}
/* For now we only need the tab position, we assume
* all tabs are left-aligned.
*/
static int get_tab_pos(LAYOUTINFO *layout, int index, BOOL *is_default)
{
int n_tabs;
if (layout->tabs) {
n_tabs = layout->nr_tabs;
if (is_default)
*is_default = FALSE;
}
else {
n_tabs = 0;
if (is_default)
*is_default = TRUE;
}
if (index < n_tabs) {
return layout->tabs[index];
}
if (n_tabs > 0) {
/* Extrapolate tab position, repeating the last tab gap to
* infinity.
*/
int last_pos = 0;
int next_to_last_pos = 0;
int ts;
last_pos = layout->tabs[n_tabs - 1];
if (n_tabs > 1)
next_to_last_pos = layout->tabs[n_tabs - 2];
else
next_to_last_pos = 0;
if (last_pos > next_to_last_pos) {
ts = last_pos - next_to_last_pos;
}
else {
ts = layout->ts;
}
return last_pos + ts * (index - n_tabs + 1);
}
else {
/* No tab array set, so use default tab width
*/
return layout->ts * index;
}
}
static int get_line_width(LAYOUTLINE *line)
{
struct list_head *l;
int i;
int width = 0;
/* Compute the width of the line currently - inefficient, but easier
* than keeping the current width of the line up to date everywhere
*/
list_for_each(l, &line->gruns) {
GlyphRun *run = (GlyphRun*)l;
for (i = 0; i < run->gs->nr_glyphs; i++)
width += run->gs->glyphs[i].width;
}
return width;
}
static void shape_tab(LAYOUTLINE *line, GlyphString *glyphs)
{
int i, space_width;
int current_width = get_line_width(line);
__mg_glyph_string_set_size (glyphs, 1);
glyphs->glyphs[0].gv = INV_GLYPH_VALUE;
glyphs->glyphs[0].x_off = 0;
glyphs->glyphs[0].y_off = 0;
glyphs->glyphs[0].is_cluster_start = 1;
glyphs->log_clusters[0] = 0;
ensure_tab_width (line->layout);
space_width = line->layout->ts / 8;
for (i=0; ; i++) {
BOOL is_default;
int tab_pos = get_tab_pos (line->layout, i, &is_default);
/* Make sure there is at least a space-width of space between
* tab-aligned text and the text before it. However, only do
* this if no tab array is set on the layout, ie. using default
* tab positions. If use has set tab positions, respect it to
* the pixel.
*/
if (tab_pos >= current_width + (is_default ? space_width : 1)) {
glyphs->glyphs[0].width = tab_pos - current_width;
break;
}
}
}
static void shape_shape(const Uchar32* ucs, int nr_ucs,
@@ -207,9 +331,11 @@ static void shape_shape(const Uchar32* ucs, int nr_ucs,
static void shape_full(const Uchar32* lrun_ucs, int nr_lrun_ucs,
const Uchar32* para_ucs, int nr_para_ucs,
const TEXTRUNSINFO* truninfo, LayoutRun* textrun,
const TEXTRUNSINFO* info, LayoutRun* lrun,
GlyphString* glyphs)
{
// TODO
info->sei.shape(info->sei.inst, info, lrun, glyphs);
}
static void distribute_letter_spacing (int letter_spacing,
@@ -230,7 +356,7 @@ static GlyphString* shape_run(LAYOUTLINE *line, LayoutState *state,
LAYOUTINFO *layout = line->layout;
GlyphString *glyphs = __mg_glyph_string_new ();
if (layout->truninfo->ucs[lrun->si] == '\t')
if (layout->truninfo->ucs[lrun->si] == UCHAR_TAB)
shape_tab(line, glyphs);
else {
if (state->shape_set)
@@ -377,10 +503,6 @@ BreakResult process_text_run(LAYOUTINFO *layout,
int i;
BOOL processing_new_lrun = FALSE;
/* Only one character has type UCHAR_CATEGORY_LINE_SEPARATOR in
* Unicode 12.0; update this if that changes. */
#define LINE_SEPARATOR 0x2028
if (!state->glyphs) {
state->glyphs = shape_run (line, state, lrun);
@@ -391,17 +513,16 @@ BreakResult process_text_run(LAYOUTINFO *layout,
}
if (!layout->single_paragraph &&
layout->truninfo->ucs[lrun->si] == LINE_SEPARATOR &&
layout->truninfo->ucs[lrun->si] == UCHAR_LINE_SEPARATOR &&
!should_ellipsize_current_line (layout, state)) {
insert_run (line, state, lrun, TRUE);
insert_run(line, state, lrun, TRUE);
state->log_widths_offset += lrun->len;
return BREAK_LINE_SEPARATOR;
}
if (state->remaining_width < 0 && !no_break_at_end) /* Wrapping off */
{
if (state->remaining_width < 0 && !no_break_at_end) {
/* Wrapping off */
insert_run (line, state, lrun, TRUE);
return BREAK_ALL_FIT;
}
@@ -449,7 +570,8 @@ retry_break:
if (width > state->remaining_width && break_num_chars < lrun->len)
break;
/* If there are no previous runs we have to take care to grab at least one char. */
/* 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) &&
(num_chars > 0 || !list_empty(&line->gruns)))
@@ -466,9 +588,11 @@ retry_break:
* XXX Currently it doesn't quite match the logic there. We don't check
* the cluster here. But should be fine in practice. */
if (break_num_chars > 0 && break_num_chars < lrun->len &&
layout->bos[state->start_offset + break_num_chars - 1] & BOV_WHITESPACE)
layout->bos[state->start_offset + break_num_chars - 1] &
BOV_WHITESPACE)
{
break_width -= state->log_widths[state->log_widths_offset + break_num_chars - 1];
break_width -= state->log_widths[state->log_widths_offset +
break_num_chars - 1];
}
if ((layout->rf & GRF_OVERFLOW_WRAP_MASK) == GRF_OVERFLOW_WRAP_NORMAL
@@ -483,8 +607,8 @@ retry_break:
goto retry_break;
}
if (force_fit || break_width <= state->remaining_width) /* Successfully broke the lrun */
{
if (force_fit || break_width <= state->remaining_width) {
/* Successfully broke the lrun */
if (state->remaining_width >= 0) {
state->remaining_width -= break_width;
state->remaining_width = MAX (state->remaining_width, 0);
@@ -498,13 +622,16 @@ retry_break:
return BREAK_EMPTY_FIT;
}
else {
LayoutRun* new_lrun;
GlyphRun *grun;
new_lrun = __mg_layout_run_split(lrun, break_num_chars);
/* Add the width back, to the line, reshape,
subtract the new width */
state->remaining_width += break_width;
grun = insert_run (line, state, lrun, FALSE);
break_width = __mg_glyph_string_get_width (grun->gs);
grun = insert_run (line, state, new_lrun, FALSE);
break_width = __mg_glyph_string_get_width(grun->gs);
state->remaining_width -= break_width;
state->log_widths_offset += break_num_chars;
@@ -1076,7 +1203,8 @@ static void layout_line_postprocess (LAYOUTLINE *line,
(wrapped || ellipsized)) {
/* if we ellipsized, we don't have remaining_width set */
if (state->remaining_width < 0)
state->remaining_width = state->line_width - layout_line_get_width (line);
state->remaining_width = state->line_width -
layout_line_get_width (line);
justify_words (line, state);
}
@@ -1085,32 +1213,13 @@ static void layout_line_postprocess (LAYOUTLINE *line,
line->layout->is_ellipsized |= ellipsized;
}
static void add_line (LAYOUTLINE *line, LayoutState *state)
{
#if 0
LAYOUTINFO *layout = line->layout;
list_add_tail(&line->list, &layout->lines);
layout->nr_lines++;
if (layout->height >= 0) {
PangoRectangle logical_rect;
pango_layout_line_get_extents (line, NULL, &logical_rect);
state->remaining_height -= logical_rect.height;
state->remaining_height -= layout->spacing;
state->line_height = logical_rect.height;
}
#else
// do nothing
#endif
}
static LAYOUTLINE* check_next_line(LAYOUTINFO* layout, LayoutState* state)
{
LAYOUTLINE *line;
struct list_head* t;
BOOL have_break = FALSE; // If we've seen a possible break yet
int break_remaining_width = 0;// Remaining width before adding run with break
int break_remaining_width = 0;// Left width before adding run with break
int break_start_offset = 0; // Start offset before adding run with break
struct list_head *break_link = NULL; // Link holding run before break
BOOL wrapped = FALSE; // If we had to wrap the line
@@ -1125,21 +1234,32 @@ static LAYOUTLINE* check_next_line(LAYOUTINFO* layout, LayoutState* state)
else
state->remaining_width = state->line_width;
while (state->lrun) {
LayoutRun *lrun = state->lrun;
list_for_each_ex(t, &layout->truninfo->truns, &state->trun->list) {
state->trun = (const TextRun*)t;
LayoutRun *lrun;
BreakResult result;
int old_num_chars;
int old_remaining_width;
BOOL first_lrun_in_line;
if (state->start_index_in_trun > 0) {
lrun = __mg_layout_run_new_from_offset(layout,
state->trun, state->start_index_in_trun);
}
else {
lrun = __mg_layout_run_new_from(layout, state->trun);
}
state->lrun = lrun;
old_num_chars = lrun->len;
old_remaining_width = state->remaining_width;
first_lrun_in_line = !list_empty(&line->gruns);
first_lrun_in_line = list_empty(&line->gruns);
result = process_text_run(layout, line, state, !have_break, FALSE);
switch (result) {
case BREAK_ALL_FIT:
if (can_break_in (layout, state->start_offset, old_num_chars, first_lrun_in_line))
if (can_break_in (layout, state->start_offset,
old_num_chars, first_lrun_in_line))
{
have_break = TRUE;
break_remaining_width = old_remaining_width;
@@ -1198,7 +1318,6 @@ static LAYOUTLINE* check_next_line(LAYOUTINFO* layout, LayoutState* state)
done:
layout_line_postprocess (line, state, wrapped);
add_line (line, state);
state->line_of_par++;
state->line_start_index += line->len;
return line;
@@ -1221,7 +1340,14 @@ LAYOUTLINE* GUIAPI LayoutNextLine(
if (layout->persist && layout->nr_left_ucs == 0) {
// already laid out
if (prev_line && &prev_line->list != &layout->lines) {
if (list_empty(&layout->lines))
return NULL;
if (prev_line == NULL) {
next_line = (LAYOUTLINE*)layout->lines.next;
goto out;
}
else if ((struct list_head*)prev_line != &layout->lines) {
next_line = (LAYOUTLINE*)prev_line->list.next;
goto out;
}
@@ -1243,30 +1369,24 @@ LAYOUTLINE* GUIAPI LayoutNextLine(
state.log_widths_offset = 0;
if (prev_line == NULL) {
state.line_start_index = 0;
if (list_empty(&layout->truninfo->truns)) {
next_line = layout_line_new(layout);
next_line->si = state.line_start_index;
next_line->is_paragraph_start = TRUE;
line_set_resolved_dir(next_line, layout->truninfo->run_dir);
goto next_line;
// empty line
next_line = NULL;
goto out;
}
state.line_start_index = 0;
state.start_index_in_trun = 0;
state.trun = (TextRun*)&layout->truninfo->truns.next;
state.lrun = __mg_layout_run_new_from(layout, state.trun);
state.start_index_in_lrun = 0;
state.left_ucs_in_lrun = state.lrun->len;
}
else {
state.start_offset = layout->truninfo->nr_ucs - layout->nr_left_ucs;
state.line_start_index = layout->truninfo->nr_ucs - layout->nr_left_ucs;
state.trun = __mg_textruns_get_by_offset(layout->truninfo,
state.start_offset, &state.start_index_in_lrun);
state.start_offset, &state.start_index_in_trun);
if (state.trun == NULL) {
return NULL;
next_line = NULL;
goto out;
}
state.lrun = __mg_layout_run_new_from_offset(layout,
state.trun, state.start_index_in_lrun);
state.left_ucs_in_lrun = state.lrun->len - state.start_index_in_lrun;
}
state.line_width = max_extent;
@@ -1274,7 +1394,6 @@ LAYOUTLINE* GUIAPI LayoutNextLine(
next_line = check_next_line(layout, &state);
next_line:
if (next_line) {
if (layout->persist) {
list_add_tail(&next_line->list, &layout->lines);
@@ -1283,7 +1402,7 @@ next_line:
release_line(prev_line);
}
layout->nr_lines ++;
layout->nr_lines++;
layout->nr_left_ucs -= next_line->len;
}
+3 -2
View File
@@ -105,6 +105,7 @@ struct _LAYOUTLINE {
struct _LAYOUTINFO {
const TEXTRUNSINFO* truninfo;
const BreakOppo* bos;
const int* tabs; // tabstop array
Uint32 rf; // rendering flags
int ls; // letter spacing
@@ -113,6 +114,7 @@ struct _LAYOUTINFO {
struct list_head lines; // the list head of lines
int nr_tabs; // number of tabstops
int nr_left_ucs;// the number of chars not laied out
int nr_lines; // the number of lines
@@ -146,8 +148,7 @@ LOGFONT* __mg_create_logfont_for_layout(const LAYOUTINFO* layout,
void __mg_release_logfont_for_layout(const LAYOUTINFO* layout,
const char* fontname, GlyphOrient ort);
GlyphRun *__mg_glyph_run_split (GlyphRun *orig,
const Uchar32 *text, int split_index);
GlyphRun *__mg_glyph_run_split (GlyphRun *orig, int split_index);
void __mg_glyph_run_free(GlyphRun* run);
LayoutRun* __mg_layout_run_new_orphan(const LAYOUTINFO* layout,
+36 -3
View File
@@ -77,7 +77,7 @@ static void reverse_shaped_glyphs(ShapedGlyph* glyphs, int len)
}
}
static BOOL shape_text_run(SEInstance* inst,
static BOOL shape_layout_run(SEInstance* inst,
const TEXTRUNSINFO* info, const LayoutRun* run,
GlyphString* gs)
{
@@ -254,11 +254,44 @@ out:
return ok;
}
BOOL GUIAPI InitBasicShapingEngine(TEXTRUNSINFO* info)
struct _SEInstance {
const char* name;
int ref_count;
};
static struct _SEInstance shaping_engine_basic = {
"Basic Shapping Engine", 0
};
static BOOL destroy_instance(SEInstance* instance)
{
info->sei.shape = shape_text_run;
if (instance == &shaping_engine_basic) {
shaping_engine_basic.ref_count--;
return TRUE;
}
_ERR_PRINTF("%s: you are destroying a non-basic shaping engine instance.\n",
__FUNCTION__);
return FALSE;
}
BOOL GUIAPI InitBasicShapingEngine(TEXTRUNSINFO* info)
{
shaping_engine_basic.ref_count++;
info->sei.inst = &shaping_engine_basic;
info->sei.shape = shape_layout_run;
info->sei.free = destroy_instance;
return TRUE;
}
#ifdef _MGDEVEL_MODE
int GetBasicShapingEngineRefCount(void)
{
return shaping_engine_basic.ref_count;
}
#endif /* _MGDEVEL_MODE */
#endif /* _MGCHARSET_UNICODE */
+2 -31
View File
@@ -236,11 +236,7 @@ static BOOL state_process_run (TextRunState *state)
for (p = state->run_start; p < state->run_end; p++) {
Uchar32 uc = *p;
/* Only one character has the category LINE_SEPARATOR in Unicode 12.0;
* update this if that changes. */
#define LINE_SEPARATOR 0x2028
BOOL is_forced_break = (uc == '\t' || uc == LINE_SEPARATOR);
BOOL is_forced_break = (uc == UCHAR_TAB || uc == UCHAR_LINE_SEPARATOR);
BOOL no_shaping;
no_shaping = is_uchar_no_shaping(uc);
@@ -516,32 +512,6 @@ TEXTRUNSINFO* GUIAPI CreateTextRunsInfo(Uchar32* ucs, int nr_ucs,
INIT_LIST_HEAD(&runinfo->truns);
runinfo->nr_runs = 0;
#if 0
int i, j;
BidiLevel max_level = 0;
BidiLevel level_or, level_and;
// make the embedding levels of the bidi marks to be -1.
level_or = 0, level_and = 1;
j = 0;
for (i = 0; i < nr_ucs; i++) {
if (BIDI_IS_EXPLICIT_OR_BN (bidi_ts[i])) {
els[i] = -1;
}
else {
level_or |= els[j];
level_and &= els[j];
j++;
}
}
// check for all even or odd
/* If none of the levels had the LSB set, all chars were even. */
runinfo->all_even = (level_or & 0x1) == 0;
/* If all of the levels had the LSB set, all chars were odd. */
runinfo->all_odd = (level_and & 0x1) == 1;
#endif
if (!create_text_runs(runinfo, els)) {
_ERR_PRINTF("%s: failed to call create_text_runs.\n",
__FUNCTION__);
@@ -572,6 +542,7 @@ BOOL GUIAPI SetFontInTextRuns(TEXTRUNSINFO* runinfo,
if (list_empty(&runinfo->truns))
return FALSE;
// TODO
return FALSE;
}
+2 -2
View File
@@ -49,7 +49,7 @@ typedef struct _GlyphString GlyphString;
typedef struct _SEInstance SEInstance;
typedef struct _TextAttrMap TextAttrMap;
typedef BOOL (*CB_SHAPE_TEXT_RUN)(SEInstance* instance,
typedef BOOL (*CB_SHAPE_LAYOUT_RUN)(SEInstance* instance,
const TEXTRUNSINFO* info, const LayoutRun* run,
GlyphString* gs);
@@ -62,7 +62,7 @@ struct _ShapingEngineInfo {
SEInstance* inst;
/* callback to shap a text run */
CB_SHAPE_TEXT_RUN shape;
CB_SHAPE_LAYOUT_RUN shape;
/* callback to destroy the shaping engine instance */
CB_DESTROY_INSTANCE free;