Commit 61c0e055 authored by Paul Asmuth's avatar Paul Asmuth
Browse files

re-order bidirectional text runs within a line according to the unicode rules

parent 1ce0f827
Loading
Loading
Loading
Loading
+7 −15
Original line number Diff line number Diff line
@@ -23,6 +23,8 @@ ReturnCode text_analyze_bidi_line(
    const TextSpan* text_end,
    TextDirection text_direction_base,
    TextLine* text_line) {
  text_line->text_direction_base = text_direction_base;

  FriBidiParType fb_basedir;
  switch (text_direction_base) {
    case TextDirection::LTR:
@@ -73,7 +75,10 @@ ReturnCode text_analyze_bidi_line(

  // find the bidi runs in the output
  std::vector<size_t> run_bounds;
  int level_max = 0;
  for (size_t i = 0; i < fb_str_len; ++i) {
    level_max = std::max(level_max, int(fb_levels[i]));

    // split on level change
    if (i > 0 && fb_levels[i] != fb_levels[i - 1]) {
      run_bounds.emplace_back(i);
@@ -87,6 +92,7 @@ ReturnCode text_analyze_bidi_line(
    }
  }

  std::vector<int> levels;
  for (size_t i = 0; i < run_bounds.size() + 1; ++i) {
    auto run_begin = i == 0 ? 0 : run_bounds[i - 1];
    auto run_end = i == run_bounds.size() ? fb_str_len : run_bounds[i];
@@ -99,25 +105,11 @@ ReturnCode text_analyze_bidi_line(
        run_len,
        run.data()));

    auto run_direction =
        int(fb_levels[run_begin]) & 1 ?
            TextDirection::RTL :
            TextDirection::LTR;

    text_line->text_runs.emplace_back(run);
    text_line->text_directions.emplace_back(run_direction);
    text_line->text_spans.emplace_back(fb_to_span_map[run_begin]);
    text_line->text_bidi_levels.emplace_back(int(fb_levels[run_begin]));
  }

  text_line->text_direction_base = text_direction_base;
  text_line->visual_order.resize(text_line->text_runs.size());
  std::iota(
      text_line->visual_order.begin(),
      text_line->visual_order.end(),
      0);

  // TODO: reorder ranges according to unicode algorithm

  return OK;
}

+71 −3
Original line number Diff line number Diff line
@@ -16,6 +16,8 @@
#include "graphics/text_layout.h"
#include "graphics/text_shaper.h"

#include <numeric>

namespace fviz {
namespace text {

@@ -69,16 +71,22 @@ Status text_layout_hline(
    double dpi,
    std::vector<GlyphSpan>* glyph_spans,
    Rectangle* bbox) {
  auto visual_order = text_get_visual_order(text_line);

  double line_top = 0.0;
  double line_bottom = 0.0;
  double line_length = 0;
  for (auto i : text_line.visual_order) {
  for (auto i : visual_order) {
    TextDirection text_direction_run =
        text_line.text_bidi_levels[i] & 1 ?
            TextDirection::RTL :
            TextDirection::LTR;

    double span_length = 0.0;
    std::vector<GlyphPlacement> span_glyphs;

    auto rc = text_layout_hrun(
        text_line.text_runs[i],
        text_line.text_directions[i],
        text_direction_run,
        text_line.text_spans[i]->language,
        text_line.text_spans[i]->script,
        font_info,
@@ -177,6 +185,66 @@ Status text_measure_span(
      bbox);
}

// source: https://unicode.org/reports/tr9/#Reordering_Resolved_Levels
//
//   "From the highest level found in the text to the lowest odd level on each
//    line, including intermediate levels not actually present in the text,
//    reverse any contiguous sequence of characters that are at that level or
//    higher."
//
std::vector<size_t> text_get_visual_order(const TextLine& line) {
  std::vector<size_t> visual_order(line.text_runs.size(), 0);

  std::iota(
      visual_order.begin(),
      visual_order.end(),
      0);

  size_t level_max = *std::max_element(
      line.text_bidi_levels.begin(),
      line.text_bidi_levels.end());

  for (size_t level_cur = level_max; level_cur >= 1; --level_cur) {
    for (size_t range_begin = 0; range_begin < line.text_runs.size(); ) {
      // find the next contiguous range where level >= level_cur starting at
      // begin
      auto range_end = range_begin;
      for (;
          line.text_bidi_levels[range_end] >= level_cur &&
          range_end != line.text_runs.size();
          ++range_end);

      // if no such sequence starts at begin, try searching from the next index
      if (range_end == range_begin) {
        ++range_begin;
        continue;
      }

      // reverse runs in the range
      std::reverse(
          visual_order.begin() + range_begin,
          visual_order.begin() + range_end);

      // continue searching from the end of the swapped range
      range_begin = range_end;
    }
  }

  // if the base direction is RTL, reverse the direction of all runs so that
  // the "first" element in the visual order array is the one at the begining
  // of the line in our base writing direction
  switch (line.text_direction_base) {
    case TextDirection::LTR:
      break;
    case TextDirection::RTL:
      std::reverse(
          visual_order.begin(),
          visual_order.end());
  }

  return visual_order;
}

} // namespace text
} // namespace fviz
+6 −2
Original line number Diff line number Diff line
@@ -70,9 +70,8 @@ struct TextSpan {
struct TextLine {
  std::vector<std::string> text_runs;
  TextDirection text_direction_base;
  std::vector<TextDirection> text_directions;
  std::vector<const TextSpan*> text_spans;
  std::vector<size_t> visual_order;
  std::vector<int> text_bidi_levels;
};


@@ -135,5 +134,10 @@ Status text_measure_span(
    Rectangle* rect);


/**
 * Determine the visual order of text runs in a line
 */
std::vector<size_t> text_get_visual_order(const TextLine& line);

} // namespace fviz::text
+0 −2
Original line number Diff line number Diff line
@@ -34,8 +34,6 @@ Status text_shape_run(
    double font_size,
    double dpi,
    std::vector<GlyphInfo>* glyphs) {
  std::cerr << "shape: " << text << std::endl;

  /* get freetype font */
  auto ft_font = static_cast<FT_Face>(font_get_freetype(font));
  auto font_size_ft = font_size * (72.0 / dpi) * 64;
+8 −8
Original line number Diff line number Diff line
@@ -39,17 +39,17 @@ void draw_test(
  std::vector<text::TextSpan> text;
  {
    text::TextSpan ts;
    ts.text = "test";
    ts.text = "helllo תל אביב 123 ";
    text.push_back(ts);
  }

  {
    text::TextSpan ts;
    ts.text += "∞ⁱ Ω תל אביב , 北京市, القاهرة = testend";
    ts.script = "Arabic";
    ts.language = "Arabic";
    text.push_back(ts);
  }
  //{
  //  text::TextSpan ts;
  //  ts.text += "∞ⁱ Ω תל אביב , 北京市, القاهرة = testend";
  //  ts.script = "Arabic";
  //  ts.language = "Arabic";
  //  text.push_back(ts);
  //}

  StrokeStyle ss;
  ss.line_width = from_unit(1);