Pango: поиск позиций в струнах деванагари

Я использую Pango для набора Devanagari. Рассмотрим строку उम्कन्छौ, состоящую из БУКВЫ ДЕВАНАГАРИ U, БУКВЫ ДЕВАНАГАРИ MA, ЗНАКА ДЕВАНАГАРИ ВИРАМА, БУКВЫ ДЕВАНАГАРИ KA, БУКВЫ ДЕВАНАГАРИ NA, ЗНАКА ДЕВАНАГАРИ ВИРАМА, БУКВЫ ДЕВАНАГАРИ CHA, ЗНАКА ГЛАСНОЙ ДЕВАНАГАРИ AU. При наборе этой строки я хочу знать начальную точку छ (CHA), чтобы поставить визуальную метку.

Для обычных строк я бы взял длину предыдущей части, उम्कन्, но здесь это не работает, поскольку, как вы можете видеть, न् (половина न) сочетается с छ, поэтому результат немного отличается.

Есть ли способ получить правильную начальную точку буквы, когда используются комбинации?

Я пытался запросить макет Pango с помощью index_to_pos(), но, похоже, это работает на уровне байтов (а не символов).

Эта небольшая программа на Perl показывает проблему. Вертикальная линия смещена вправо.

use strict;
use warnings;
use utf8;
use Cairo;
use Pango;

my $surface = Cairo::PdfSurface->create ("out.pdf", 595, 842);
my $cr = Cairo::Context->create ($surface);
my $layout = Pango::Cairo::create_layout($cr);
my $font = Pango::FontDescription->from_string('Lohit Devanagari');
$layout->set_font_description($font);

# Two parts of the phrase. Phrase1 ends in न् (half न).
my $phrase1 = 'उम्कन्';
my $phrase2 = 'छौ';

# Set the first part of the phrase, and get its width.
$layout->set_markup($phrase1);
my $w = ($layout->get_size)[0]/1024;

# Set the complete phrase.
$layout->set_markup($phrase1.$phrase2);

my ($x, $y ) = ( 100, 100 );

# Show phrase.
$cr->move_to( $x, $y );
$cr->set_source_rgba( 0, 0, 0, 1 );
Pango::Cairo::show_layout($cr, $layout);

# Show marker at width.
$cr->set_line_width(0.25);
$cr->move_to( $x + $w, $y-10 );
$cr->line_to( $x + $w, $y+50 );
$cr->stroke;

$cr->show_page;

person Johan Vromans    schedule 15.10.2019    source источник
comment
Вопрос выиграл бы от какой-нибудь минимальной исполняемой программы, показывающей, как визуальная метка ставится не в том месте. Потенциальные ответы на вопросы не должны тратить время на демонстрацию и рисковать получить/понять ее неправильно. Пожалуйста, отредактируйте вопрос, чтобы внести поправки. Также см. раздел Помогите другим воспроизвести проблему в ‹stackoverflow.com/help/how-to-ask.›. --- Если это Perl, я буду рад провести расследование.   -  person daxim    schedule 15.10.2019


Ответы (1)


Вы не можете измерить частичный рендеринг. Вместо этого измерьте весь рендеринг и выполните итерацию по графеме строки, чтобы найти позицию. Также смотрите: https://gankra.github.io/blah/text-hates-you/#style-can-change-mid-ligature

use strict;
use warnings;
use utf8;
use Cairo;
use Pango;
use List::Util qw(uniq);
use Encode qw(encode);

my $surface = Cairo::PdfSurface->create('out.pdf', 595, 842);
my $cr = Cairo::Context->create ($surface);
my $layout = Pango::Cairo::create_layout($cr);
my $font = Pango::FontDescription->from_string('Lohit Devanagari');
$layout->set_font_description($font);
my $phrase = 'उम्कन्छौ';
my @octets = split '', encode 'UTF-8', $phrase; # index_to_pos operates on octets
$layout->set_markup($phrase);
my ($x, $y) = (100, 100);
$cr->move_to($x, $y);
$cr->set_source_rgba(0, 0, 0, 1);
Pango::Cairo::show_layout($cr, $layout);
$cr->set_line_width(0.25);
my @offsets = uniq map { $layout->index_to_pos($_)->{x}/1024 } 0..$#octets;
# (0, 9.859375, 16.09375, 27.796875, 33.953125, 49.1875)
for my $offset (@offsets) {
    $cr->move_to($x+$offset, $y-5);
    $cr->line_to($x+$offset, $y+25);
    $cr->stroke;
}
my @graphemes = $phrase =~ /\X/g; # qw(उ म् क न् छौ)
while (my ($idx, $g) = each @graphemes) {
    if ($g =~ /^छ/) {
        $cr->move_to($x+$offsets[$idx], $y-10);
        $cr->line_to($x+$offsets[$idx], $y+50);
        $cr->stroke;
        last;
    }
}
$cr->show_page;
person daxim    schedule 17.10.2019
comment
Круто и очень полезно! Я обнаружил, что в {многих,большинстве,всех} ситуациях правильные результаты были получены путем измерения длины полной фразы и вычитания длины второй части фразы. Ваше решение является доказуемо правильным. - person Johan Vromans; 17.10.2019