Я создал документ всего с одним «большим пальцем вверх» эмодзи (кодовая точка Unicode U+1F44D), который я вставил с помощью стандартного сочетания клавиш Windows+;:
Но я не могу получить его фактическая кодовая точка с 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 }
(для информации, этот код декодирует два символа в 👍
)
Как определить реальный символ, если эмодзи вставлен с помощью 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: компьютеры/программы, на которых я мог воспроизвести проблему:
Обратите внимание, что то, что вставляет Windows, может не совпадать с тем, что вставляет Word/Symbol. Эмодзи может выглядеть одинаково, но не факт, что он использует тот же шрифт Unicode или комбинацию символов, что и внутренние функции Word. Если бы вы показали нам Word Open XML, который вы упомянули, это могло бы рассказать нам больше о том, как Windows вставляет эмодзи. FWIW, я думаю, что он вставляется как какое-то изображение, а не как символ шрифта, как работает Insert/Symbol.
@CindyMeister Вопрос обновлен с учетом содержимого свойства ActiveDocument.Range.XML.
Для опубликованного вами XML, для какого варианта использования он предназначен? У вас должно быть 2 фрагмента: один, когда он ведет себя так, как ожидалось, и один, когда это не так. Вероятно, они не будут одинаковыми.
@Chris Вопрос обновлен с помощью XML для 2 случаев
Мэйби, перестроив диапазон из XML: ActiveDocument.Content.InsertXML ActiveDocument.Content.XML
?
@ФлорентБ. это работает, спасибо! Можете ли вы преобразовать в ответ и немного объяснить, если это возможно? Если я применю его ко всему документу с различным форматированием, есть ли риск что-то потерять (встроенные изображения, ...)?
@ Сандра Росси, я предполагаю, что Range
неправильно заполнен первыми двумя символами 31 00 32 00
десятичной кодовой точки 128077 (0x1F44D) вместо суррогатного кода UTF-16 3D D8 4D DC
. Я не знаю, есть ли побочные эффекты при перезагрузке XML. Может быть, у кого-то есть идея получше, как исправить Range
.
@ФлорентБ. Не могли бы вы включить это в ответ? Я думаю, что ОП примет это, если вы это сделаете.
@FlorentB, пожалуйста, преобразуйте два ваших комментария в один ответ, больше ничего добавлять не нужно. Он работает на моих 3 компьютерах. Перезагрузка XML немного влияет на всю мою программу VBA (идентификаторы фигур перенумеровываются), но у меня есть обходной путь: создание фиктивного документа, инициализированного с помощью insertXML
, вместо применения его к исходному документу... но это уже другая история.
На самом деле не могу воспроизвести это в Office 2016, в моих символах Word через Win +; вставлены правильно..
Я надеюсь, что это поможет: основываясь на комментариях @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, должно быть что-то другое в наших двух конфигурациях.
Это странно. Мои настройки такие же, как у Computer2, по крайней мере, в отношении Office. Я попробовал еще раз. У меня было <w16se:symEx w16se:font = "Yu Mincho Light" w16se:char = "1F44D"/>
. Итак, не могли бы вы ввести эмодзи в обоих направлениях в Word, а затем сохранить как XML и вставить XML-дерево, пожалуйста?
Кроме того, дальнейшее тестирование показало, что у нас та же ситуация, если вы ввели 1F44D, Alt+X
(прекрасно преобразуется в Unicode) по сравнению с Alt+128077
на цифровой клавиатуре. Последний является (должен быть) той же кодовой точкой, но плохо переводится.
Извините, извините, если я разархивирую файл DOCX, я увижу, что document.xml
содержит w16se:char = "1F44D"
(думаю, меня ввел в заблуждение термин «XML-документ» и, наконец, я не посмотрел в нужном месте).
ОК, это нормально. Это подтверждает, что это небольшая ошибка
Вот мои окончательные убеждения и выводы.
Вероятно, это ошибка в MS Word VBA, основанная на тестах, проведенных AAA в Excel, Powerpoint и Word. У некоторых эта ошибка отсутствует (см. комментарии).
Объекты VBA дают недопустимое значение для эмодзи, но свойство XML правильное. XML слишком сложен, чтобы его можно было легко разобрать, поэтому Florent B. в комментариях нашел простейший обходной путь, который заключается в «воссоздании документа из самого себя»:
ActiveDocument.Content.InsertXML ActiveDocument.Content.XML
К сожалению, в моем личном случае это может иметь некоторые побочные эффекты, такие как перенумерация идентификаторов фигур.
Итак, я расширил приведенный выше код, чтобы исправить только символы смайликов в исходном документе, остальное осталось нетронутым:
Ладно, макросы работают дольше, но лучшего решения я не нашел.
Вот мой код, упрощенный (вас может удивить бесполезная коллекция объектов 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
К вашему сведению, я не смог воспроизвести это в Office 2016 (Win10 1809).