Программа Word VBA считывает символ Emoji (4 байта) как «12»

Я создал документ всего с одним «большим пальцем вверх» эмодзи (кодовая точка Unicode U+1F44D), который я вставил с помощью стандартного сочетания клавиш Windows+;:

Программа Word VBA считывает символ Emoji (4 байта) как «12»

Но я не могу получить его фактическая кодовая точка с VBA.

Я получаю эти значения (отладка):

text = 12
length = 2
arrBytes = { 49, 0, 50, 0 }

со следующей подпроцедурой:

Sub test()
    Dim text As String
    Dim length As Integer
    Dim arrBytes() As Byte
    text = ActiveDocument.Range.Characters(1).text
    length = Len(ActiveDocument.Range.Characters(1).text)
    arrBytes = ActiveDocument.Range.Characters(1).text
End Sub

Но если бы я вставил тот же эмодзи через меню Insert > Symbol > Шрифт «Segoe UI Emoji» > U+1F44D (большой палец вверх), та же процедура Sub получит ожидаемые значения (в отладке; ?? не являются «настоящими» символами, это суррогатные кодовые точки, которые по отдельности ничего не значат):

text = ??
length = 2
arrBytes = { 61, 216, 77, 220 }

(для информации, этот код декодирует два символа в &#x1F44D)

Как определить реальный символ, если эмодзи вставлен с помощью Windows+;? (просить пользователей выбрать обходной путь, указанный выше, не является частью моего вопроса)

ПРИЛОЖЕНИЕ от 26 мая: решение от @Florent B. работает на всех трех моих компьютерах (ActiveDocument.Content.InsertXML ActiveDocument.Content.XML). Повторная загрузка XML может повлиять на программы VBA, например, она перенумерует изображение «идентификаторы формы», но это уже другая история.

ПРИЛОЖЕНИЕ от 22 мая: для символа, добавленного с помощью Windows+;, я могу найти правильное значение (4 байта { 61, 216, 77, 220 }) только в свойстве XML объекта Range документа, но для этого требуется, чтобы я проанализировал весь XML и определить, какие символы XML соответствуют каким позициям объектов Range, к сожалению, я считаю, что это требует много знаний или предположений. Вот часть XML, где я вижу 4 байта (<w:t>??</w:t> где ?? соответствуют 4 байтам):

  <?xml version = "1.0" standalone = "yes"?>
  <?mso-application progid = "Word.Document"?>
  <w:wordDocument ...>
    ... (around 23.000 characters)
    <w:body>
      <wx:sect>
        <w:p wsp:rsidR = "002703DB" wsp:rsidRDefault = "003926FB">
          <w:r>
            <w:rPr>
              <w:rFonts w:ascii = "Segoe UI Emoji" w:h-ansi = "Segoe UI Emoji"/>
              <wx:font wx:val = "Segoe UI Emoji"/>
            </w:rPr>
            <w:t>??</w:t>
          </w:r>
        </w:p>
        <w:sectPr wsp:rsidR = "002703DB" wsp:rsidSect = "002849CD"><w:pgSz w:w = "11906"
                w:h = "16838"/><w:pgMar w:top = "1417" w:right = "1417" w:bottom = "1417"
                w:left = "1417" w:header = "708" w:footer = "708" w:gutter = "0"/><w:cols
                w:space = "708"/><w:docGrid w:line-pitch = "360"/></w:sectPr>
      </wx:sect>
    </w:body>
  </w:wordDocument>

XML почти такой же, когда я вставляю Emoji в качестве символа, есть еще 2 «rFonts»:

    <w:body>
      <wx:sect>
        <w:p wsp:rsidR = "00CD420D" wsp:rsidRDefault = "00CD420D">
          <w:r>
            <w:rPr>
              <w:rFonts w:ascii = "Segoe UI Emoji" w:fareast = "Segoe UI Emoji"
                    w:h-ansi = "Segoe UI Emoji" w:cs = "Segoe UI Emoji"/>
              <wx:font wx:val = "Segoe UI Emoji"/>
            </w:rPr>
            <w:t>??</w:t>
          </w:r>
        </w:p>
        <w:sectPr wsp:rsidR = "00CD420D" wsp:rsidSect = "002849CD"><w:pgSz w:w = "11906"
                w:h = "16838"/><w:pgMar w:top = "1417" w:right = "1417" w:bottom = "1417"
                w:left = "1417" w:header = "708" w:footer = "708" w:gutter = "0"/><w:cols
                w:space = "708"/><w:docGrid w:line-pitch = "360"/></w:sectPr>
      </wx:sect>
    </w:body>
  </w:wordDocument>

PS: компьютеры/программы, на которых я мог воспроизвести проблему:

  • Компьютер 1 (lenovo X230):
    • MS Word Office 365 1904 (16.0.11601.20174) 32-разрядная версия, Windows 10 Professional 10.0.17763 x64
    • Также после обновления до Office 365 1907 16.0.11901.20176, MSO (16.0.11901.20070) 32 бита, Windows 10 Professional 1809 17763.652 x64
  • Компьютер 2:
    • MS Word Office 365 1904 (16.0.11601.20184) 64-разрядная версия, Windows 10 Professional 1809 17763.503 x64
  • Компьютер 3 (Делл):
    • MS Word Office 365 ProPlus 1808 (16.0.10730.20334) 64-разрядная версия, Windows 10 Корпоративная 10.0.17763 x64

К вашему сведению, я не смог воспроизвести это в Office 2016 (Win10 1809).

41686d6564 19.05.2019 11:21

Обратите внимание, что то, что вставляет Windows, может не совпадать с тем, что вставляет Word/Symbol. Эмодзи может выглядеть одинаково, но не факт, что он использует тот же шрифт Unicode или комбинацию символов, что и внутренние функции Word. Если бы вы показали нам Word Open XML, который вы упомянули, это могло бы рассказать нам больше о том, как Windows вставляет эмодзи. FWIW, я думаю, что он вставляется как какое-то изображение, а не как символ шрифта, как работает Insert/Symbol.

Cindy Meister 20.05.2019 11:24

@CindyMeister Вопрос обновлен с учетом содержимого свойства ActiveDocument.Range.XML.

Sandra Rossi 20.05.2019 15:33

Для опубликованного вами XML, для какого варианта использования он предназначен? У вас должно быть 2 фрагмента: один, когда он ведет себя так, как ожидалось, и один, когда это не так. Вероятно, они не будут одинаковыми.

Chris 21.05.2019 15:30

@Chris Вопрос обновлен с помощью XML для 2 случаев

Sandra Rossi 21.05.2019 16:02

Мэйби, перестроив диапазон из XML: ActiveDocument.Content.InsertXML ActiveDocument.Content.XML ?

Florent B. 22.05.2019 10:21

@ФлорентБ. это работает, спасибо! Можете ли вы преобразовать в ответ и немного объяснить, если это возможно? Если я применю его ко всему документу с различным форматированием, есть ли риск что-то потерять (встроенные изображения, ...)?

Sandra Rossi 22.05.2019 14:13

@ Сандра Росси, я предполагаю, что Range неправильно заполнен первыми двумя символами 31 00 32 00 десятичной кодовой точки 128077 (0x1F44D) вместо суррогатного кода UTF-16 3D D8 4D DC. Я не знаю, есть ли побочные эффекты при перезагрузке XML. Может быть, у кого-то есть идея получше, как исправить Range.

Florent B. 23.05.2019 10:11

@ФлорентБ. Не могли бы вы включить это в ответ? Я думаю, что ОП примет это, если вы это сделаете.

S.S. Anne 25.05.2019 00:48

@FlorentB, пожалуйста, преобразуйте два ваших комментария в один ответ, больше ничего добавлять не нужно. Он работает на моих 3 компьютерах. Перезагрузка XML немного влияет на всю мою программу VBA (идентификаторы фигур перенумеровываются), но у меня есть обходной путь: создание фиктивного документа, инициализированного с помощью insertXML, вместо применения его к исходному документу... но это уже другая история.

Sandra Rossi 26.05.2019 14:49

На самом деле не могу воспроизвести это в Office 2016, в моих символах Word через Win +; вставлены правильно..

Van Ng 27.05.2019 11:12
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
16
11
726
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Я надеюсь, что это поможет: основываясь на комментариях @SandraRossi выше, кажется, что ввод с панели смайликов неправильно переведен в его суррогатную кодовую точку. Если вы сохраните документ, содержащий оба символа (один из панели Emoji, а другой из меню, как вы описали) в виде XML-документа, вы заметите разницу:

Ввод эмодзи:

<w:r w:rsidR = "003814F5">
  <w:rPr>
    <mc:AlternateContent>
      <mc:Choice Requires = "w16se">
        <w:rFonts w:ascii = "Segoe UI Emoji" w:hAnsi = "Segoe UI Emoji"/>
      </mc:Choice>
      <mc:Fallback>
        <w:rFonts w:hint = "eastAsia"/>
      </mc:Fallback>
    </mc:AlternateContent>
  </w:rPr>
  <mc:AlternateContent>
    <mc:Choice Requires = "w16se">
      <w16se:symEx w16se:font = "@SimHei" w16se:char = "1F44D"/>
    </mc:Choice>
    <mc:Fallback>
      <w:t>?</w:t>
    </mc:Fallback>
  </mc:AlternateContent>
</w:r>

Ввод меню (символ):

<w:r w:rsidR = "003814F5">
  <w:rPr>
    <w:rFonts w:ascii = "Segoe UI Emoji" w:hAnsi = "Segoe UI Emoji"/>
  </w:rPr>
  <w:t xml:space = "preserve"> is not ?</w:t>
</w:r>

Строка <w16se:symEx w16se:font = "@SimHei" w16se:char = "1F44D"/> здесь ключевое отличие. Обычный (Меню -> Вставить символ) смайлик используется в качестве запасного варианта.

Кажется, проблема только в Word. Я попробовал тот же ввод панели смайликов в Excel (и PowerPoint) и получил правильные значения в отладке ??, которые переводятся в кодовую точку Unicode U+1F44D как в Excel, так и при копировании обратно в Word.

Спасибо за уделенное время и за тесты по Word/Excel/Powerpoint! Итак, я предполагаю, что это ошибка в Word. Если я сохраняю документ, я получаю не тот результат (я пробовал с форматами DOCX, XML, XML 2003): ни отката, ни «w16se:char» 1F44D, должно быть что-то другое в наших двух конфигурациях.

Sandra Rossi 26.05.2019 14:39

Это странно. Мои настройки такие же, как у Computer2, по крайней мере, в отношении Office. Я попробовал еще раз. У меня было <w16se:symEx w16se:font = "Yu Mincho Light" w16se:char = "1F44D"/>. Итак, не могли бы вы ввести эмодзи в обоих направлениях в Word, а затем сохранить как XML и вставить XML-дерево, пожалуйста?

AAA 27.05.2019 12:39

Кроме того, дальнейшее тестирование показало, что у нас та же ситуация, если вы ввели 1F44D, Alt+X (прекрасно преобразуется в Unicode) по сравнению с Alt+128077 на цифровой клавиатуре. Последний является (должен быть) той же кодовой точкой, но плохо переводится.

AAA 27.05.2019 15:10

Извините, извините, если я разархивирую файл DOCX, я увижу, что document.xml содержит w16se:char = "1F44D" (думаю, меня ввел в заблуждение термин «XML-документ» и, наконец, я не посмотрел в нужном месте).

Sandra Rossi 28.05.2019 20:14

ОК, это нормально. Это подтверждает, что это небольшая ошибка

AAA 28.05.2019 20:21
Ответ принят как подходящий

Вот мои окончательные убеждения и выводы.

Вероятно, это ошибка в MS Word VBA, основанная на тестах, проведенных AAA в Excel, Powerpoint и Word. У некоторых эта ошибка отсутствует (см. комментарии).

Объекты VBA дают недопустимое значение для эмодзи, но свойство XML правильное. XML слишком сложен, чтобы его можно было легко разобрать, поэтому Florent B. в комментариях нашел простейший обходной путь, который заключается в «воссоздании документа из самого себя»:

ActiveDocument.Content.InsertXML ActiveDocument.Content.XML

К сожалению, в моем личном случае это может иметь некоторые побочные эффекты, такие как перенумерация идентификаторов фигур.

Итак, я расширил приведенный выше код, чтобы исправить только символы смайликов в исходном документе, остальное осталось нетронутым:

  • копирование XML в документ новый,
  • затем синтаксический анализ каждого символа, длина текста которого > 1 в новом документе (т. е. тех, которые находятся за пределами базовой многоязычной плоскости Unicode, а также содержат смайлики и многие другие символы),
  • также анализ исходного документа (при условии, что символы должны быть в том же порядке, что и в новом документе, и их длина текста одинакова),
  • копирование этих символов из нового документа обратно в исходный документ,
  • закрытие нового документа.

Ладно, макросы работают дольше, но лучшего решения я не нашел.

Вот мой код, упрощенный (вас может удивить бесполезная коллекция объектов Range, где каждый Range — это один объект Character, на самом деле я не привожу исходный код функции Split_Into_Ranges, которая намного больше, но быстрее, но он работает и хорошо демонстрирует решение в подпункте correct_emojis):

Sub test()

    Dim text As String
    Dim length As Integer
    Dim arrBytes() As Byte

    Dim zranges As Collection
    Set zranges = Split_Into_Ranges(ActiveDocument)

    Call correct_emojis(zranges) ' <=== here the important algorithm

    text = ActiveDocument.Range.Characters(1).text
    length = Len(ActiveDocument.Range.Characters(1).text)
    arrBytes = ActiveDocument.Range.Characters(1).text

End Sub

Function Split_Into_Ranges(ioDocument As Document) As Collection

    Dim zranges As Collection
    Set zranges = New Collection
    For i = 1 To ioDocument.Characters.Count
        zranges.Add ioDocument.Characters(i)
    Next
    Set Split_Into_Ranges = zranges

End Function

Sub correct_emojis(zranges As Collection)

    Dim current_emoji_zranges As Collection
    Dim temp_zranges As Collection
    Dim temp_emoji_zranges As Collection
    Dim doc_current As Document
    Dim doc_temp As Document
    Dim arrBytes() As Byte

    Set doc_current_zranges = get_emoji_zranges(zranges)
    If doc_current_zranges.Count = 0 Then
        Exit Sub
    End If

    Set doc_current = ActiveDocument
    Set doc_temp = Documents.Add()
    Call doc_temp.Content.InsertXML(doc_current.Content.XML)
    Set temp_zranges = Split_Into_Ranges(doc_temp)

    Set current_emoji_zranges = get_emoji_zranges(zranges)
    Set temp_emoji_zranges = get_emoji_zranges(temp_zranges)

    For i = 1 To current_emoji_zranges.Count
        If 0 = 1 Then
            arrBytes = current_emoji_zranges(i).Characters(1).text
            arrBytes = temp_emoji_zranges(i).Characters(1).text
        End If
        current_emoji_zranges(i).Characters(1).text = temp_emoji_zranges(i).Characters(1).text
    Next

    Call doc_temp.Close(False)

End Sub

Function get_emoji_zranges(zranges As Collection) As Collection

    Dim emoji_zranges As Collection

    Set emoji_zranges = New Collection
    For i = 1 To zranges.Count
        If Len(zranges(i).text) > zranges(i).Characters.Count Then
            For j = 1 To zranges(i).Characters.Count
                If Len(zranges(i).Characters(j).text) > 1 Then
                    emoji_zranges.Add (zranges(i))
                End If
            Next
        End If
    Next

    Set get_emoji_zranges = emoji_zranges

End Function

Другие вопросы по теме

Похожие вопросы

Обновите формулу суммы, чтобы включить недавно вставленную строку с помощью макроса
Как сохранить значение в текстовом поле при перезагрузке формы
Цикл доступа VBA MS: как определить оператор IF, который сравнивает текущую запись с предыдущей записью в цикле
Как очистить значения, скопированные и вставленные за пределы именованного диапазона, из выделения
Необходимо исправить эту ошибку: «Не удается найти точку входа DLL GetSystemInfo в kernel32»
Автозаполнение данных в Excel
Как получить пользовательскую форму Excel для проверки, а затем отредактировать пользовательскую форму
Разбор строки времени с использованием регулярного выражения и преобразование в целое число
Как обновить несколько сводных таблиц с несколькими источниками данных?
Как отсортировать столбцы на основе заголовков, используя список всех доступных заголовков?