<?xml version="1.0" encoding="utf-8"?> 
<rss version="2.0"
  xmlns:itunes="http://www.itunes.com/dtds/podcast-1.0.dtd"
  xmlns:atom="http://www.w3.org/2005/Atom">

<channel>

<title>Блоги: заметки с тегом юникод</title>
<link>https://blogengine.me/blogs/tags/yunikod/</link>
<description>Автоматически собираемая лента заметок, написанных в блогах на Эгее</description>
<author></author>
<language>ru</language>
<generator>Aegea 11.0 (v4079e)</generator>

<itunes:subtitle>Автоматически собираемая лента заметок, написанных в блогах на Эгее</itunes:subtitle>
<itunes:image href="" />
<itunes:explicit>no</itunes:explicit>

<item>
<title>Поддержка Юникода во «Флиппере Зеро»</title>
<guid isPermaLink="false">124655</guid>
<link>https://bolknote.ru/all/podderzhka-yunikoda-vo-flippere-zero/</link>
<pubDate>Thu, 30 Nov 2023 19:41:50 +0500</pubDate>
<author>Евгений Степанищев</author>
<comments>https://bolknote.ru/all/podderzhka-yunikoda-vo-flippere-zero/</comments>
<description>
&lt;p&gt;&lt;a href="https://bolknote.ru/"&gt;Евгений Степанищев&lt;/a&gt;:&lt;/p&gt;
&lt;p&gt;Если помните, какое-то время назад я &lt;a href="https://bolknote.ru/all/pishem-na-flipper-zero-po-russki/"&gt;разбирался&lt;/a&gt; как обстоит дело с поддержкой Юникода во «Флиппере Зеро». Если вкратце: плохо, но жить можно. Исследование этой проблемы даже подтолкнуло меня к написанию игры, в которой я мужественно поборол все трудности, связанные с ограниченностью АПИ в этом вопросе.&lt;/p&gt;
&lt;p&gt;Видно, что авторы не подумали о поддержке Юникода, поэтому он там работает кое-как и по чистой случайности, просто потому что его поддерживает сторонний фреймворк «&lt;i&gt;U∞g2&lt;/i&gt;», используемый для вывода на экран.&lt;/p&gt;
&lt;p&gt;Ситуацию можно было бы немного улучшить простым патчем — надо всего лишь поменять в функции &lt;tt&gt;canvas_glyph_width&lt;/tt&gt; тип входного параметра с &lt;tt&gt;char&lt;/tt&gt; на &lt;tt&gt;uint16_t&lt;/tt&gt; (эта функция возвращает ширину символа в пикселях) и она начнёт работать с кодировкой &lt;i&gt;UCS-2&lt;/i&gt;.&lt;/p&gt;
&lt;p&gt;Две недели назад я подумал — почему бы не попробовать предложить этот патч авторам прошивки. Если не примут, то может быть хоть в дискуссию вступят, всё польза. Скачал разработческую версию ядра, сделал правку и &lt;a href="https://github.com/flipperdevices/flipperzero-firmware/pull/3226"&gt;закинул&lt;/a&gt; патч.&lt;/p&gt;
&lt;p&gt;Сегодня патч приняли без лишних слов. Вот это да, не ожидал! Справедливости ради, это небольшое изменение, которое не должно ничего сломать. Теперь хорошо бы подумать как сделать так, чтобы в остальном АПИ поддержка Юникода заработала. А там, кажется, малой кровью не обойдёшься.&lt;/p&gt;
&lt;div class="e2-text-picture"&gt;
&lt;img src="https://bolknote.ru/pictures/2023.11.30@2x.jpg" width="1000" height="384" alt="" /&gt;
&lt;div class="e2-text-caption"&gt;Внезапно мой небольшой коммит для поддержки Юникода функцией вычисления ширины символа приняли&lt;/div&gt;
&lt;/div&gt;
</description>
</item>

<item>
<title>Пишем на «Флиппере Зеро» по-русски</title>
<guid isPermaLink="false">123899</guid>
<link>https://bolknote.ru/all/pishem-na-flipper-zero-po-russki/</link>
<pubDate>Thu, 26 Oct 2023 21:29:37 +0500</pubDate>
<author>Евгений Степанищев</author>
<comments>https://bolknote.ru/all/pishem-na-flipper-zero-po-russki/</comments>
<description>
&lt;p&gt;&lt;a href="https://bolknote.ru/"&gt;Евгений Степанищев&lt;/a&gt;:&lt;/p&gt;
&lt;p class="remark"&gt;Вкратце: в русским текстом работать можно, но сложно, код лежит на «&lt;a href="https://github.com/bolknote/Flipper-Zero-Privet-Mir"&gt;Гитхабе&lt;/a&gt;».&lt;/p&gt;
&lt;p&gt;Не мог не обратить внимание, что несмотря на приличное количество русскоязычных авторов программ для «Флиппера Зеро» и российское происхождение устройства, в софте под него нигде не обнаружилось ни одной буквы по-русски.&lt;/p&gt;
&lt;!-- Flipper Zero --&gt;&lt;p&gt;Стало интересно — а есть ли вообще в устройстве поддержка русского языка? Сел вечерком за исходники прошивки и оказалось, что однозначного ответа на заданный вопрос нет. Частичная поддержка есть и её можно заставить работать, но она работает как будто по недосмотру — никто её не собирался делать, так случайно вышло.&lt;/p&gt;
&lt;div class="e2-text-picture"&gt;
&lt;img src="https://bolknote.ru/pictures/2023.10.26@2x.jpg" width="500" height="234" alt="" /&gt;
&lt;div class="e2-text-caption"&gt;Вывод русских букв (трансляция с экрана через оболочку &lt;i&gt;qFlipper&lt;/i&gt;)&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Сейчас расскажу, что я имею ввиду и постараюсь покороче, никто сейчас не любит длинные тексты.&lt;/p&gt;
&lt;p&gt;Внутри «Флиппера» довольно много всякого АПИ для вывода текста, от незатейливого, выводящего строку в заданные координаты, до довольно навороченного, умеющего переносить и выравнивать текст. К сожалению, с русскими буквами оно работать не умеет.&lt;/p&gt;
&lt;p&gt;Под капотом у этого АПИ — очень известная в своей нише библиотека &lt;a href="https://github.com/olikraus/u8g2/"&gt;&lt;i&gt;U∞g2&lt;/i&gt;&lt;/a&gt;, у которой по историческим, видимо, причинам есть два набора функций для работы со строками. Одни принимают только однобайтовую кодировку &lt;i&gt;ASCII&lt;/i&gt;, другие работают в Юникоде.&lt;/p&gt;
&lt;p&gt;АПИ «Флиппера», к сожалению, использует функции, которые с Юникодом работать не умеют. К счастью, по какой-то случайности, упомянутое АПИ содержит два вызова, которые помогают решить эту проблему, правда придётся помучаться.&lt;/p&gt;
&lt;p&gt;Первый из них — &lt;tt&gt;canvas_set_custom_u8g2_font&lt;/tt&gt;.&lt;/p&gt;
&lt;p&gt;Чтобы что-то написать русскими буквами, нужен шрифт, который их содержит. Стандартный вывод &lt;tt&gt;canvas_set_font&lt;/tt&gt; позволяет выбрать один из нескольких шрифтов, ни одни из которых кириллицу не поддерживает.  Тут и приходит на помощь &lt;tt&gt;canvas_set_custom_u8g2_font&lt;/tt&gt;. Он позволяет выбрать произвольный шрифт пакета &lt;i&gt;U∞g2&lt;/i&gt;, — внутри их целая куча и некоторые в названии содержат слово «&lt;i&gt;cyrillic&lt;/i&gt;».&lt;/p&gt;
&lt;p&gt;Вот как я это делаю:&lt;/p&gt;
&lt;pre class="e2-text-code"&gt;&lt;code class="cpp"&gt;#include &amp;lt;u8g2/u8g2_fonts.c&amp;gt;
// …
canvas_set_custom_u8g2_font(canvas, u8g2_font_haxrcorp4089_t_cyrillic);&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Файл &lt;tt&gt;u8g2/u8g2_fonts.c&lt;/tt&gt; внутри себя содержит шрифты в особом формате, можно открыть его прямо в текстовом виде и посмотреть как всё устроено.&lt;/p&gt;
&lt;p&gt;Шрифт мы выбрали, теперь надо что-нибудь вывести. Тут на помощь приходит &lt;tt&gt;canvas_draw_glyph&lt;/tt&gt;. «Под капотом» у него лежит &lt;tt&gt;u8g2_DrawGlyph&lt;/tt&gt;, которая понимает символы Юникода почти без танцев с бубном. Думаю, это чистое везение — наверняка эта функция появилась сильно позднее, поэтому у неё нет двух вариантов для работы с разными кодировками.&lt;/p&gt;
&lt;p&gt;Плохая новость в том, что строки в коде мы обычно пишем в кодировке &lt;i&gt;UTF-8&lt;/i&gt;, а &lt;tt&gt;canvas_draw_glyph&lt;/tt&gt; ожидает их в &lt;i&gt;UCS-2&lt;/i&gt;. Но есть и хорошая новость — в прошивке доступно АПИ для перекодирования, как обычно, великолепно описанное на языке Си.&lt;/p&gt;
&lt;p&gt;Но &lt;tt&gt;canvas_draw_glyph&lt;/tt&gt;, как можно понять из названия, выводит только один символ. Как же вывести строку? Достаточно просто — надо вывести её посимвольно, сдвигая координату на ширину символа. Звучит просто, но как это сделать?&lt;/p&gt;
&lt;p&gt;Тут нас ждёт очередная засада. Дело в том, что &lt;tt&gt;canvas_glyph_width&lt;/tt&gt;, которая должна бы это делать, принимает на вход только &lt;tt&gt;char&lt;/tt&gt;, при том, что нижележащая &lt;tt&gt;u8g2_GetGlyphWidth&lt;/tt&gt; преспокойно работает с Юникодом.&lt;/p&gt;
&lt;p&gt;Поэтому я сделал просто — выдрал реализацию &lt;tt&gt;u8g2_GetGlyphWidth&lt;/tt&gt; себе в исходники. В итоге мякотка моего решения выглядит вот так:&lt;/p&gt;
&lt;pre class="e2-text-code"&gt;&lt;code class="cpp"&gt;void unicode_draw_utf8_str(Canvas* canvas, uint8_t x, uint8_t y, char* str) {
    FuriStringUTF8State state = FuriStringUTF8StateStarting;
    FuriStringUnicodeValue value = 0;

    for(; *str; str++) {
        furi_string_utf8_decode(*str, &amp;amp;state, &amp;amp;value);
        if(state == FuriStringUTF8StateError) furi_crash(NULL);

        if(state == FuriStringUTF8StateStarting) {
            canvas_draw_glyph(canvas, x, y, value);
            x += unicode_GetGlyphWidth(&amp;amp;canvas-&amp;gt;fb, value);
        }
    }
}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Тут &lt;tt&gt;unicode_GetGlyphWidth&lt;/tt&gt; — скопированная реализация &lt;tt&gt;u8g2_GetGlyphWidth&lt;/tt&gt;, а &lt;tt&gt;furi_string_utf8_decode&lt;/tt&gt; используется для кодирования &lt;i&gt;UTF-8&lt;/i&gt;-строки в последовательные символы &lt;tt&gt;UCS-2&lt;/tt&gt;.&lt;/p&gt;
</description>
</item>

<item>
<title>I � Unicode</title>
<guid isPermaLink="false">125660</guid>
<link>https://bolknote.ru/all/2092/</link>
<pubDate>Wed, 15 Apr 2009 13:26:00 +0500</pubDate>
<author>Евгений Степанищев</author>
<comments>https://bolknote.ru/all/2092/</comments>
<description>
&lt;p&gt;&lt;a href="https://bolknote.ru/"&gt;Евгений Степанищев&lt;/a&gt;:&lt;/p&gt;
&lt;p&gt;Давайте я вам про Unicode ещё раз расскажу?&lt;/p&gt;
&lt;p&gt;Как известно, в памяти компьютера числа представлены битами, которые группируются в байты. Один байт может хранить одно из 256 значений (поскольку состоит из восьми бит, каждый из которых может хранить одно из двух). Следовательно, числа, значения которых &amp;gt; 255 хранятся в больше, чем одном байте.&lt;/p&gt;
&lt;p&gt;Например, числа до 65535 можно уместить уже в двух байтах: в так называемом старшем записывается сколько раз полных 256 содержится в числе, а в младшем — остаток: старший × 256 + младший.&lt;/p&gt;
&lt;p&gt;В зависимости от типа процессора, порядок, в котором записаны в памяти старшие и младшие байты, различается. Собственно, мне хорошо известны только две системы: младший записывается первым (как в процессорах Intel) и старший записывается первым (в процессорах ARM, которые стоят в смартфонах). Есть ещё смешанная, но с ней я не сталкивался. Системы эти носят имена: little-endian и big-endian (системы со смешанным порядком называются middle-endian и термин не указывает на то как именно «мешается» этот порядок). Краткая запись названий — LE и BE.&lt;/p&gt;
&lt;p&gt;Есть ещё системы, которые умеют переключать порядок (те же ARM) и называются bi-endian.&lt;/p&gt;
&lt;p&gt;Термины little-endian и big-endian пришли к нам из «Приключений Гулливера» и на русский переводятся как «тупоконечный» и «остроконечный». Те, кто читали, те помнят (война по поводу того с какой стороны разбивать яйца). Информатика тут какбэ намекает. Хотя у каждой системы есть свои достоинства и (не удержался) мне ближе LE.&lt;/p&gt;
&lt;p&gt;Сюрприз для непрограммистов: буквы в памяти компьютера тоже представлены числом. Это просто номер по порядку в компьютерном алфавите. Так девочки в нашем классе «кодировали» записки: вместо букв ставили номер позиции в алфавите. В чём-то они были правы, но только не в том, что это шифр.&lt;/p&gt;
&lt;p&gt;Когда-то компьютеры победивших сейчас систем использовали всего 256 символов и всем было хорошо — туда умещались все символы, которые присутствовали в том мире, где эти компьютеры создавались. Экспансия привела к тому, что 256 значений для символов перестало хватать.&lt;/p&gt;
&lt;p&gt;Было принято очевидное решение — выделять на символ не один байт, а несколько. Так появился стандарт Unicode, где огромному количеству символов дано своё число и закреплены начертания, стандарт дополняется и новые версии выходят почти каждый год.&lt;/p&gt;
&lt;p&gt;Система кодирования Unicode, где выделяются два байта, называется UTF-16 (16 бит на символ), там где четыре байта — UTF-32 (32 бита). Название UCS-4 (четыре байта) является синонимом UTF-32, а UCS-2 (два байта) подмножеством UTF-16. UCS-2 &lt;a href="http://www.unicode.org/faq/basic_q.html#14"&gt;отличается от UTF-16&lt;/a&gt; отсутствием так называемых «суррогатных пар» (которые появились только в Unicode 2.0, вы не хотите знать что это) и является устаревшим стандартом, можете про него забыть.&lt;/p&gt;
&lt;p&gt;Так как способов хранения чисел, не умещающихся в памяти у нас несколько, то системы подразделяются на UTF-16BE, UTF-16LE, UTF-32BE и UTF-32LE. Отсюда видно, что UCS-4LE это тоже, что и UTF-32LE. Если порядок байт не указан, то &lt;a href="http://unicode.org/faq/utf_bom.html#gen7"&gt;принято считать&lt;/a&gt;, что используется big-endian.&lt;/p&gt;
&lt;p&gt;Первого апреля 2005-го года были предложены шуточные «стандарты» &lt;a href="http://tools.ietf.org/html/rfc4042"&gt;UTF-9 и UTF-18&lt;/a&gt;, отношения к рассматриваемой проблеме они имеют. Для телеграфа и прочего слоновьего гуано, разрабатывались UTF-5 и UTF-6, но о их судьбе мне ничего не известно. Так же есть &lt;a href="http://en.wikipedia.org/wiki/UTF-7"&gt;UTF-7&lt;/a&gt;, который в стандарт не вошёл, но реально применяется (в модифицированном виде) внутри почтового протокола &lt;a href="http://tools.ietf.org/html/rfc3501"&gt;IMAP4&lt;/a&gt;, про него я рассказывать не буду, мне он стал известен из-за &lt;a href="http://old.antichat.ru/txt/utf7/"&gt;оригинального способа его использования&lt;/a&gt; для XSS-атак в IE (в частности, решением этой проблемы я занимался в PEAR PHP &lt;a href="http://pear.php.net/package/HTML_Safe"&gt;классе HTML_Safe&lt;/a&gt;). Можно упомянуть ещё &lt;a href="http://en.wikipedia.org/wiki/UTF-1"&gt;UTF-1&lt;/a&gt;, но с ней я не сталкивался в работе.&lt;/p&gt;
&lt;p&gt;BOM. BOM расшифровывается как «byte order mark» (признак порядка байт) и ставится внутри файлов упомянутых двух- и четырёхбайтных кодировок. &lt;a href="http://unicode.org/faq/utf_bom.html#gen6"&gt;Если BOM внутри файла не встретился, принимается порядок big-endian&lt;/a&gt;. У BOM есть значание. В UCS-2 это 65279 (для программистов — FEFF), для UCS-4/UTF-32 — это 4278124544 (FEFF0000). Число выбрано так, чтобы старшие и младшие байты у них не совпадали и по их порядку можно было бы определить какой порядок байт используется. К сожалению, BOM не даёт возможности определить использутеся двух- или четырёхбайтная кодировка.&lt;/p&gt;
&lt;p&gt;Теперь непрограммистам будет трудно.&lt;/p&gt;
&lt;p&gt;Пока всё было достаточно просто, но человечество придумало ещё одну кодировку — UTF-8, с плавающим размером. Хорошие новости заключаются в том, что порядок следования байт тут определён и никаких LE и BE рядом с UTF-8 не ставится. Соотвественно и BOM тут не нужен. Он может использоваться только для того, чтобы указать программе, что это именно UTF-8 и &lt;a href="http://unicode.org/faq/utf_bom.html#BOM"&gt;имеет номер 15711167 (EF BB BF)&lt;/a&gt;. Откуда можно сделать вывод (дорогие писатели редакторов), что использование в UTF-8 BOM от UTF-16 — ошибка.&lt;/p&gt;
&lt;p&gt;Трудность в том, что UTF-8, по сути это ещё один способ записи многобайтовых чисел (а каждая буква в стандарте Unicode — многобайтовое число). У системы есть целых два плюса (ирония!): старая однобайтовая кодировка совместима с UTF-8, а значит буржуинам не нужно переделывать свои программы, если они не используют в них буквы и других языков (например, на любом старом англоязычном сайте как бы уже используется кодировка UTF-8), второй плюс — латиница записывается компактнее (в один символ). Минусы — чисто программисткие: работа с кодировкой требует больше ресурсов из-за плавающего размера.&lt;/p&gt;
&lt;p&gt;Итак. Каждый символ в кодировке занимает от 1 до 4-х байт. Вообще, формат устроен так, что можно было бы взять и более длинные цепочки, но в Unicode нет столько символов, чтобы записывать их более длинными последовательностями.&lt;/p&gt;
&lt;p&gt;Тут надо вспомнить что такое биты. Бит — единица информации, мельче не бывает, у него всего два значения — 0 или 1. Байт состоит из 8 битов, биты очень удобно записывать в позиционной двоичной системе: 00001011. «Позиционная» тут означает, что значение числа зависит от его позиции. Кстати, это привычная нам система. В числе «22» две двойки, но у первой значение в &lt;i&gt;десять&lt;/i&gt; раз больше, чем у второй. Это &lt;i&gt;десятичная&lt;/i&gt; позиционная система. В двоичной, каждая более левая однёрка будет больше в два раза своей соседки.&lt;/p&gt;
&lt;p&gt;Таким образом число 1011 расшифровывается из двоичной как 1 × 2&lt;sup&gt;3&lt;/sup&gt; + 0 × 2&lt;sup&gt;2&lt;/sup&gt; + 1 × 2&lt;sup&gt;1&lt;/sup&gt; + 1 × 2&lt;sup&gt;0&lt;/sup&gt; = 1 × 8 + 1 × 2 + 1 × 1 = 8 + 2 + 1 = 11 в десятичной системе.&lt;/p&gt;
&lt;p&gt;UTF-8 устроен следующим образом. Пусть, мы двигаемся по строке, содержащей два байта: 208 и 159. В битах это 11010000 и 10011111. (Немного осталось, потерпите).&lt;/p&gt;
&lt;p&gt;В первом символе нужно посчитать количество бит со значением «1» до первого нуля. Это общее количество байт, которым записан данный символ. Если количество байт — один (это вроде как специальный признак), то вы нашли не первый байт символа.&lt;/p&gt;
&lt;p&gt;У нас в примере количество бит до первого нуля — два. Значит, буква записана двумя символами — первый это тот, на которым мы находимся и второй — который следует за ним. Каждый байт в UTF-8 разбит на две части — до первого нулевого бита. Первая часть — общая длина байт последовательности, а оставшаяся — значение. Биты из значения записывают последовательно (у нас это 10000 011111) и смотрят какое число получилось (у нас это — 1055, это номер &lt;a href="http://www.fileformat.info/info/unicode/char/041f/index.htm"&gt;буквы «П»&lt;/a&gt; в Unicode).&lt;/p&gt;
&lt;p&gt;Могу рассказать про UTF-7 и UTF-1, если интересно. Или про суррогатные пары.&lt;/p&gt;
</description>
</item>


</channel>
</rss>