Подтвердить что ты не робот

Как отправить текст Unicode из MATLAB в документ Word через интерфейс ActiveX?

Я использую MATLAB для программного создания документа Microsoft Word в Windows. В общем, это решение работает отлично, но у него возникают проблемы с текстом, отличным от ASCII. Например, возьмите этот код:

wordApplication = actxserver('Word.Application');
wordApplication.Visible = 1;
wordApplication.Documents.Add;
selection = wordApplication.Selection;
umbrella = char(9730);
disp(umbrella)
selection.TypeText(umbrella)

Окно Command отображает символ зонтика правильно, но символ в документе Word является символом "вопросительный знак в поле", отсутствующим символом символа. Я могу вырезать и вставить символ из окна команд в Word, так что символ действительно доступен в этом шрифте.

Метод TypeText должен принимать ASCII. Есть ресурсы о том, как устанавливать флаги Unicode для аналогичных операций с других языков, но я не знаю, как их перевести в синтаксис, который я имею в MATLAB.

Уточнение:. Мой случай использования отправляет неизвестную строку Unicode (массив char), а не только один символ. Было бы идеально, если бы вы могли отправить все сразу. Вот лучший пример кода:

% Define a string to send with a non-ASCII character.
umbrella = char(9730);
toSend = ['Have you seen my ' umbrella '?'];
disp(toSend)

% Open a new Word document.
wordApplication = actxserver('Word.Application');
wordApplication.Visible = 1;
wordApplication.Documents.Add;

% Send the text.
selection = wordApplication.Selection;
selection.TypeText(toSend)

Я надеялся, что могу просто установить кодировку самого документа, но это, похоже, не помогает:

wordApplication = actxserver('Word.Application');
wordApplication.Visible = 1;
wordApplication.Documents.Add;
disp(wordApplication.ActiveDocument.TextEncoding)
wordApplication.ActiveDocument.TextEncoding = 65001;
disp(wordApplication.ActiveDocument.TextEncoding)
selection = wordApplication.Selection;
toSend = sprintf('Have you seen my \23002?');
selection.TypeText(toSend)
4b9b3361

Ответ 1

Способ 1. Действителен для одного символа (исходный вопрос)

Взято из здесь:

umbrella = 9730; %// Unicode number of the desired character
selection.InsertSymbol(umbrella, '', true); %// true means use Unicode

Второй аргумент определяет шрифт (так что вы можете использовать 'Arial' и т.д.), а '', очевидно, означает использование текущего шрифта. Третий аргумент 'true' означает использование Unicode.

Метод 2. Действителен для одного символа (исходный вопрос)

Менее прямой путь, взятый из здесь:

umbrella = 9730; %// Unicode number of the desired character
selection.TypeText(dec2hex(umbrella));
selection.ToggleCharacterCode;

Метод 3. Действителен для строки (отредактированный вопрос)

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

umbrella = char(9730);
toSend = ['Have you seen my ' umbrella '?'];
clipboard('copy', toSend); %// copy the Unicode string contained in variable `toSend`
selection.Paste %// paste it onto the Word document

Ответ 2

Я тоже пробовал это, и получил ту же проблему, о которой вы сообщали (я тестировал MATLAB R2015a и Office 2013)...

Я думаю, что что-то в слое COM между MATLAB и Word испортило текстовое кодирование.

Чтобы убедиться, что это действительно ошибка в MATLAB, я пробовал то же самое в Python, и он работал нормально:

#!/usr/bin/env python

import os
import win32com.client

word = win32com.client.Dispatch("Word.Application")
word.Visible = True

doc = word.Documents.Add()

str = u"Have you seen my " + unichr(9730) + u"?"
word.Selection.TypeText(str)

fname = os.path.join(os.getcwd(), "out.docx")
doc.SaveAs2(fname)
doc.Close()

word.Quit()

Я придумал два обходных пути для MATLAB:

Способ 1 (предпочтительный):

Идея заключается в создании сборки .NET, использующей Office Interop. Он получит любую строку Unicode и напишет ее в какой-то определенный документ Word. Затем эту сборку можно загрузить в MATLAB и использовать в качестве оболочки для MS Office.

Пример в С#:

MSWord.cs

using System;
using Microsoft.Office.Interop.Word;

namespace MyOfficeInterop
{
    public class MSWord
    {
        // this is very basic, but you can expose anything you want!
        public void AppendTextToDocument(string filename, string str)
        {
            Application app = null;
            Document doc = null;
            try
            {
                app = new Application();
                doc = app.Documents.Open(filename);

                app.Selection.TypeText(str);
                app.Selection.TypeParagraph();

                doc.Save();
            }
            catch (Exception)
            {
                throw;
            }
            finally
            {
                doc.Close();
                app.Quit();
            }
        }
    }
}

Сначала мы скомпилируем его:

csc.exe /nologo /target:library /out:MyOfficeInterop.dll /reference:"C:\Program Files (x86)\Microsoft Visual Studio 12.0\Visual Studio Tools for Office\PIA\Office15\Microsoft.Office.Interop.Word.dll" MSWord.cs

Затем мы проверяем его с MATLAB:

%// load assembly
NET.addAssembly('C:\path\to\MyOfficeInterop.dll')

%// I am assuming the document file already exists
fname = fullfile(pwd,'test.docx');
fclose(fopen(fname,'w'));

%// some text
str = ['Have you seen my ' char(9730) '?'];

%// add text to Word document
word = MyOfficeInterop.MSWord();
word.AppendTextToDocument(fname, str);

Способ 2:

Это скорее хак! Мы просто пишем текст в MATLAB непосредственно в текстовый файл (закодированный правильно). Затем мы используем интерфейс COM/ActiveX, чтобы открыть его в MS Word, и заново сохраните его как правильный документ Word.docx.

Пример:

%// params
fnameTXT = fullfile(pwd,'test.txt');
fnameDOCX = fullfile(pwd,'test.docx');
str = ['Have you seen my ' char(9730) '?'];

%// create UTF-8 encoded text file
bytes = unicode2native(str, 'UTF-8');
fid = fopen(fnameTXT, 'wb');
fwrite(fid, bytes);
fclose(fid);

%// some office interop constants (extracted using IL DASM)
msoEncodingUTF8 = int32(hex2dec('0000FDE9'));         % MsoEncoding
wdOpenFormatUnicodeText = int32(hex2dec('00000005')); % WdOpenFormat
wdFormatDocumentDefault = int32(hex2dec('00000010')); % WdSaveFormat
wdDoNotSaveChanges = int32(hex2dec('00000000'));      % WdSaveOptions

%// start MS Word 
Word = actxserver('Word.Application');
%Word.Visible = true;

%// open text file in MS Word
doc = Word.Documents.Open(...
    fnameTXT, ...                % FileName
    [], ...                      % ConfirmConversions
    [], ...                      % ReadOnly
    [], ...                      % AddToRecentFiles
    [], ...                      % PasswordDocument
    [], ...                      % PasswordTemplate
    [], ...                      % Revert
    [], ...                      % WritePasswordDocument
    [], ...                      % WritePasswordTemplate
    wdOpenFormatUnicodeText, ... % Format
    msoEncodingUTF8, ...         % Encoding
    [], ...                      % Visible
    [], ...                      % OpenAndRepair
    [], ...                      % DocumentDirection
    [], ...                      % NoEncodingDialog
    []);                         % XMLTransform

%// save it as docx
doc.SaveAs2(...
    fnameDOCX, ...               % FileName
    wdFormatDocumentDefault, ... % FileFormat
    [], ...                      % LockComments
    [], ...                      % Password
    [], ...                      % AddToRecentFiles
    [], ...                      % WritePassword
    [], ...                      % ReadOnlyRecommended
    [], ...                      % EmbedTrueTypeFonts
    [], ...                      % SaveNativePictureFormat
    [], ...                      % SaveFormsData
    [], ...                      % SaveAsAOCELetter
    msoEncodingUTF8, ...         % Encoding
    [], ...                      % InsertLineBreaks
    [], ...                      % AllowSubstitutions
    [], ...                      % LineEnding
    [], ...                      % AddBiDiMarks
    []),                         % CompatibilityMode

%// close doc, quit, and cleanup
doc.Close(wdDoNotSaveChanges, [], [])
Word.Quit()
clear doc Word