CoreText со слишком длинным арабским текстом

#ios #swift #core-text #arabic-support

#iOS #swift #основной текст #Арабский-поддержка

Вопрос:

Я пытался следовать руководству по CoreText и тому, как рисовать текст, и я реализовал эту функцию в своем CustomView.

 override func draw(_ rect: CGRect) {         
 guard let context = UIGraphicsGetCurrentContext() else { return }       
 let path = CGMutablePath()
 path.addRect(bounds)
 let attrString = NSAttributedString(string: "Hello World")
 let framesetter = CTFramesetterCreateWithAttributedString(attrString as CFAttributedString)
 let frame = CTFramesetterCreateFrame(framesetter, CFRangeMake(0, attrString.length), path, nil) 
 CTFrameDraw(frame, context)
}
 

Он отлично работает, когда текст короткий, но когда текст становится более 10 тыс. букв, он отображается неправильно. Есть ли какое-либо решение для этого?

ПРИМЕЧАНИЕ: это происходит, когда текст на арабском языке, а не на английском.

Вот рендеринг, когда текст маленький:
введите описание изображения здесь

Вот текстовый рендеринг, когда количество текста слишком велико, превышает 10 КБ, он выглядит разрозненным и перевернутым:

введите описание изображения здесь

Комментарии:

1. Это настоящая ошибка. Я воспроизвел его точно так, как вы описываете, где-то около 10 тысяч букв. Я вижу ту же проблему, если вы пропустите CTFramesetter и используете CTLine и CTTypesetter напрямую. Кроме «сделай свои струны короче», я не уверен, что здесь можно сделать, кроме как поговорить с Apple. Я бы, наверное, использовал для этого DTS, если они у вас остались в этом году. (нажмите «Поддержка на уровне кода» на панели «Учетная запись» по адресу developer.apple.com ) Это действительно интересная ошибка.

2. Привет @RobNapier, большое спасибо за ваш повтор, как вы рекомендовали, я отправил проблему в DTS, и они ответили следующим: в конкретном примере, который вы предоставили, на самом деле может быть не столько размер текста, сколько единственное английское слово в середине текста, котороеможет потребовать дополнительной обработки в Юникоде. Но по умолчанию те части CoreText, которые отображают сложный Юникод, по умолчанию отключены, чтобы избежать низкой производительности и других потенциальных проблем. Это контролируется kCTTypesetterOptionAllowUnboundedLayout (по умолчанию false) Опция набора текста.

3. @RobNapier, итак, как я могу включить такое значение, и, как вы знаете, я не использовал класс Typesetter?

4. Я воспроизвел эту проблему, используя текст из istizada.com/arabic-lorem-ipsum (многократное копирование абзаца). В нем определенно не должно было быть текста со смешанным биди (ltr и rtl). И это определенно происходит, когда я пересекаю определенный размер, поэтому я не верю их теории «единого английского слова». Но kCTTypesetterOptionAllowUnboundedLayout, похоже, исправляет это. Я взломал свой тестовый пример, и он не выполняет правильную компоновку, но я напишу правильный пример позже этим утром. (Обратите внимание, что я думаю attrString.draw(in: rect) , что здесь будет работать использование вместо основного текста, но я понимаю, что вы хотите изучить CT)

5. Да, я тоже не согласен с ними по поводу этой строки «одно английское слово», большое спасибо @RobNapier

Ответ №1:

Основываясь на ваших отзывах от Apple, кажется, что CTFramesetter отключает расширенный макет, когда строка длиннее определенного размера. (Их предположение о том, что это связано с тем, что текст со смешанным направлением звучит неправильно; я определенно воспроизвел это только на арабском языке.)

Чтобы исправить это, вам необходимо создать фреймсеттер с пользовательским набором текста, который всегда выполняет расширенный макет (kCTTypesetterOptionAllowUnboundedLayout). К счастью, это просто.

Сначала создайте пользовательский набор текста, а затем используйте его для создания набора фреймов:

 let typesetterOptions = [kCTTypesetterOptionAllowUnboundedLayout: true] as CFDictionary
let typesetter = CTTypesetterCreateWithAttributedStringAndOptions(attrString,
                                                                  typesetterOptions)!
let framesetter = CTFramesetterCreateWithTypesetter(typesetter)