Вот функция barebone, которую я придумал, которая имеет только AWS Javascript SDK
в качестве зависимости. Вначале я ошибся в том, что Base 64 кодировал RawMessage.Data
, но AWS SDK
уже позаботился об этом.
Пустые строки \n
также важны.
const sendRawEmail = async () => {
// Set up from_name, from_email, to, subject, message_id, plain_text, html and configuration_set variables from database or manually
var date = new Date();
var boundary = `----=_Part${ Math.random().toString().substr( 2 ) }`;
var rawMessage = [
`From: "${ from_name }" <${ from_email }>`, // Can be just the email as well without <>
`To: ${ to }`,
`Subject: ${ subject }`,
`MIME-Version: 1.0`,
`Message-ID: <${ message_id }@eu-west-1.amazonses.com>`, // Will be replaced by SES
`Date: ${ formatDate( date ) }`, // Will be replaced by SES
`Return-Path: <${ message_id }@eu-west-1.amazonses.com>`, // Will be replaced by SES
`Content-Type: multipart/alternative; boundary="${ boundary }"`, // For sending both plaintext & html content
// ... you can add more headers here as decribed in https://docs.aws.amazon.com/ses/latest/DeveloperGuide/header-fields.html
`\n`,
`--${ boundary }`,
`Content-Type: text/plain; charset=UTF-8`,
`Content-Transfer-Encoding: 7bit`,
`\n`,
plain_text,
`--${ boundary }`,
`Content-Type: text/html; charset=UTF-8`,
`Content-Transfer-Encoding: 7bit`,
`\n`,
html,
`\n`,
`--${ boundary }--`
]
// Send the actual email
await ses.sendRawEmail( {
Destinations: [
to
],
RawMessage: {
Data: rawMessage.join( '\n' )
},
Source: from_email, // Must be verified within AWS SES
ConfigurationSetName: configuration_set, // optional AWS SES configuration set for open & click tracking
Tags: [
// ... optional email tags
]
} ).promise();
}
Это служебные методы для форматирования заголовка даты. Вы также можете использовать библиотеку moment
, например, moment().format('ddd, DD MMM YYYY HH:MM:SS ZZ');
, но это не имеет значения, поскольку заголовок даты все равно перезаписывается AWS SES.
// Outputs timezone offset in format ZZ
const getOffset = ( date ) => {
var offset = - date.getTimezoneOffset();
var offsetHours = Math.abs( Math.floor( offset / 60 ) );
var offsetMinutes = Math.abs( offset ) - offsetHours * 60;
var offsetSign = ( offset > 0 ) ? '+' : '-';
return offsetSign + ( '0' + offsetHours ).slice( -2 ) + ( '0' + offsetMinutes ).slice( -2 );
}
// Outputs two digit inputs with leading zero
const leadingZero = ( input ) => ( '0' + input ).slice( -2 );
// Formats date in ddd, DD MMM YYYY HH:MM:SS ZZ
const formatDate = ( date ) => {
var weekdays = [ 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun' ];
var months = [ 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec' ];
var weekday = weekdays[ date.getDay() ];
var day = leadingZero( date.getDate() );
var month = months[ date.getMonth() ];
var year = date.getFullYear();
var hour = leadingZero( date.getHours() );
var minute = leadingZero( date.getMinutes() );
var second = leadingZero( date.getSeconds() );
var offset = getOffset( date );
return `${ weekday }, ${ day } ${ month } ${ year } ${ hour }:${ minute }:${ second } ${ offset }`;
}
Я наконец разрешил копировать пословно. FormattedText, казалось, работал довольно хорошо до последнего слова (некоторое специальное предложение (очевидно) символы), куда внезапно ячейка, которую я просто заполнил скопированным содержанием, пойдет пробел. Когда я увеличил число ячеек, другие ошибки времени выполнения откроются, как Ваша таблица был поврежден, и другие неоднозначные. Так или иначе исходная ячейка, с которой я копировал всегда, казалось, имела эти специфические символы в конце с кодами ASCII 13 и 7. Я знаю что 13 средств, но 7? Так или иначе я решил скопировать все кроме этого последнего знака с кодом 7. Это, кажется, работает хорошо. И форматирование и поля копируется также. В любом случае целая история доказала мне для еще раз, что программирование в VBA является главным образом эмпирическим размещением. Вы никогда не уверены, когда что-то могло бы повредиться.. если я не пропускаю обновление на некоторых решающих понятиях..
Вот блоки кода, который я использовал. Идея состоит в том, что сначала у нас есть документ с синглом 1x1 таблица ячейки с некоторым содержанием обогащенного текста. В первой части кода (в макросе) я умножаю ячейки:
Dim cur_width As Integer, i As Integer, max_cells As Integer, cur_row As Integer
Dim origin_width As Integer
If ActiveDocument.Tables.Count = 1 _
And ActiveDocument.Tables(1).Rows.Count = 1 _
And ActiveDocument.Tables(1).Columns.Count = 1 _
Then
max_cells = 7 ' how many times we are going to "clone" the original content
i = 2 ' current cell count - starting from 2 since the cell with the original content is cell number 1
cur_width = -1 ' current width
cur_row = 1 ' current row count
origin_width = ActiveDocument.Tables(1).Rows(1).Cells(1).Width
' loop for each row
While i <= max_cells
' adjust current width
If cur_row = 1 Then
cur_width = origin_width
Else
cur_width = 0
End If
' loop for each cell - as long as we have space, add cells horizontally
While i <= max_cells And cur_width + origin_width < ActiveDocument.PageSetup.PageWidth
Dim col As Integer
' \ returns floor() of the result
col = i \ ActiveDocument.Tables(1).Rows.Count
// 'add cell, if it is not already created (which happens when we add rows)
If ActiveDocument.Tables(1).Rows(cur_row).Cells.Count < col Then
ActiveDocument.Tables(1).Rows(cur_row).Cells.Add
End If
// 'adjust new cell width (probably unnecessary
With ActiveDocument.Tables(1).Rows(cur_row).Cells(col)
.Width = origin_width
End With
// 'keep track of the current width
cur_width = cur_width + origin_width
i = i + 1
Wend
' when we don't have any horizontal space left, add row
If i <= max_cells Then
ActiveDocument.Tables(1).Rows.Add
cur_row = cur_row + 1
End If
Wend
End If
Во второй части макроса я заполняю каждую пустую ячейку с содержанием первой ячейки:
' duplicate the contents of the first cell to other cells
Dim r As Row
Dim c As Cell
Dim b As Boolean
Dim w As Range
Dim rn As Range
b = False
i = 1
For Each r In ActiveDocument.Tables(1).Rows
For Each c In r.Cells
If i <= max_cells Then
// ' don't copy first cell to itself
If b = True Then
' copy everything word by word
For Each w In ActiveDocument.Tables(1).Rows(1).Cells(1).Range.Words
' get the last bit of formatted text in the destination cell, as range
' do it first by getting the whole range of the cell, then collapsing it
' so that it is now the very end of the cell, and moving it one character
' before (because collapsing moves the range actually beyond the last character of the range)
Set rn = c.Range
rn.Collapse Direction:=wdCollapseEnd
rn.MoveEnd Unit:=wdCharacter, Count:=-1
' somehow the last word of the contents of the cell is always Chr(13) & Chr(7)
' and especially Chr(7) causes some very strange and murky problems
' I end up avoiding them by not copying the last character, and by setting as a rule
' that the contents of the first cell should always contain an empty line in the end
If c.Range.Words.Count <> ActiveDocument.Tables(1).Rows(1).Cells(1).Range.Words.Count Then
rn.FormattedText = w
Else
'MsgBox "The strange text is: " & w.Text
'the two byte values of this text (which obviously contains special characters with special
'meaning to Word can be found (and watched) with
'AscB(Mid(w.Text, 1, 1)) and AscB(Mid(w.Text, 2, 1))
w.MoveEnd Unit:=WdUnits.wdCharacter, Count:=-1
rn.FormattedText = w
End If
Next w
End If
b = True
End If
i = i + 1
Next c
Next r
Вот изображения рассматриваемого документа Word. Первое изображение прежде выполняет макрос, второй между первым блоком кода и последним, в то время как третье изображение является получающимся документом.
Изображение 1 Изображение 2 Изображение 3
Именно.
Используйте текстовое свойство объекта Выбора поместить данные в строковую переменную, а не на буфер обмена:
Тусклый strTemp как Строка
strTemp = Выбор. Текст
Можно затем вставить текст, сохраненный в переменную в другом месте по мере необходимости.