Добрый день! Прошу от Вас помощи в выполнении определённой задачи, а именно необходимо текст разбить по строкам: 1. Формируется определенная ширина текста с использованием команды "Перенести текст"; 2. Сформированный текст преобразуется "как-то" с простановкой [ALT]+[ENTER] (т.е. визуальное преобразуется в физическое); 3. И сформированный текст дробится по строкам с помощью макроса (код в файле) . Вопрос, можно ли как-то системно выполнить пункт 2 и привязать к существующему макросу?
Добрый день! Прошу от Вас помощи в выполнении определённой задачи, а именно необходимо текст разбить по строкам: 1. Формируется определенная ширина текста с использованием команды "Перенести текст"; 2. Сформированный текст преобразуется "как-то" с простановкой [ALT]+[ENTER] (т.е. визуальное преобразуется в физическое); 3. И сформированный текст дробится по строкам с помощью макроса (код в файле) . Вопрос, можно ли как-то системно выполнить пункт 2 и привязать к существующему макросу?4step
4step, Добрый день. Нашел где-то на просторах интернета... Выделить таблицу "Вспомогательная" и применить следующий макрос [vba]
Код
Sub Split_By_Rows() Dim cell As Range, n As Integer
Set cell = ActiveCell
For i = 1 To Selection.Rows.Count ar = Split(cell, Chr(10)) 'делим текст по переносам в массив n = UBound(ar) 'определяем кол-во фрагментов cell.Offset(1, 0).Resize(n, 1).EntireRow.Insert 'вставляем пустые строки ниже cell.Resize(n + 1, 1) = WorksheetFunction.Transpose(ar) 'вводим в них данные из массива Set cell = cell.Offset(n + 1, 0) 'сдвигаемся на следующую ячейку Next i End Sub
[/vba]
Добавлено: Хммм... Данный код уже есть в листе "Должно быть" Сразу не понял задачу...
4step, Добрый день. Нашел где-то на просторах интернета... Выделить таблицу "Вспомогательная" и применить следующий макрос [vba]
Код
Sub Split_By_Rows() Dim cell As Range, n As Integer
Set cell = ActiveCell
For i = 1 To Selection.Rows.Count ar = Split(cell, Chr(10)) 'делим текст по переносам в массив n = UBound(ar) 'определяем кол-во фрагментов cell.Offset(1, 0).Resize(n, 1).EntireRow.Insert 'вставляем пустые строки ниже cell.Resize(n + 1, 1) = WorksheetFunction.Transpose(ar) 'вводим в них данные из массива Set cell = cell.Offset(n + 1, 0) 'сдвигаемся на следующую ячейку Next i End Sub
[/vba]
Добавлено: Хммм... Данный код уже есть в листе "Должно быть" Сразу не понял задачу...and_evg
Сообщение отредактировал and_evg - Вторник, 07.12.2021, 13:02
Слепил первое приближение процедуры, конвертирующей визуальные Alt+Enter в ячейке - в физические. Строчки "бьёт" по пробелам и по "минусам" (тире, переносам).
Вроде, работает. На оптимальность не претендую. Буду благодарен за придирчивое тестирование. Здоровая критика и конструктивные модификации также приветствуются.
Для тестовых запусков использовать процедуру test(). Весь код лучше поместить в обычный модуль (не модуль листа).
Сам VBA-код в это сообщение не влез, с предупреждением "Текст сообщения превышает допустимый лимит" (хотя, вроде, символов в запасе еще было предостаточно). Поэтому пытаюсь завернуть его в текстовый файл, а также попробую опубликовать код здесь в следующем сообщении.
Слепил первое приближение процедуры, конвертирующей визуальные Alt+Enter в ячейке - в физические. Строчки "бьёт" по пробелам и по "минусам" (тире, переносам).
Вроде, работает. На оптимальность не претендую. Буду благодарен за придирчивое тестирование. Здоровая критика и конструктивные модификации также приветствуются.
Для тестовых запусков использовать процедуру test(). Весь код лучше поместить в обычный модуль (не модуль листа).
Сам VBA-код в это сообщение не влез, с предупреждением "Текст сообщения превышает допустимый лимит" (хотя, вроде, символов в запасе еще было предостаточно). Поэтому пытаюсь завернуть его в текстовый файл, а также попробую опубликовать код здесь в следующем сообщении.Gustav
Sub test() Call convertVisualAltEnterToPhysical(Range("A4")) End Sub
Sub convertVisualAltEnterToPhysical(rng As Range)
Dim hgh0 As Double 'высота ячейки для пустой текстовой строки - при подстановке "" в Range.Value (единица высоты, "одна высота")
Dim str0 As String 'исходная строка - исходное значение Range.Value Dim str As String 'исходная строка после замены только очередного пробела/минуса на Alt+Enter Dim strNxt As String 'исходная строка после замены только следующего (очередного+1) пробела/минуса на Alt+Enter
Dim lines As Integer 'кол-во линий (строк) - для размещения исходной строки (текста) в ячейке (без Alt+Enter)
Dim start As Integer 'номер символа начала поиска очередного пробела/минуса Dim startNxt As Integer 'номер символа начала поиска следующего (очередного+1) пробела/минуса Dim posSpace As Integer 'позиция первого найденного ПРОБЕЛА при начале поиска от start Dim posMinus As Integer 'позиция первого найденного МИНУСА при начале поиска от start
Dim delimiters As Integer 'кол-во разделителей (пробелов/минусов) в исходной строке (максимальное потенциально возможное - для определения верхней границы цикла) Dim words As Integer 'кол-во слов = delimiters + 1 Dim strWords As String 'строка после замены минусов/пробелов на двойные тильды - заведомо длиннее исходной Dim lenWords As Integer 'заведомо большая длина этой строки (длина+1) Dim i As Integer 'счётчик цикла по delimiters
Dim arrLines() As Integer 'массив кол-в линий в ячейке, соответствующий "левым" текстам из arrLeft() Dim arrLeft() As String 'массив "левых" текстов = значений ячеек слева до очередного i-го пробела/минуса Dim arrRows() As String 'формируемый массив текстовых строк в ячейке
Dim prevRow As Integer Dim currRow As Integer Dim prevLeft As String Dim currLeft As String
str0 = rng.Value
strWords = Replace(Replace(str0, " ", "~~"), "-", "~~") lenWords = Len(strWords) + 1 'заведомо большая длина всей строки delimiters = UBound(Split(strWords, "~~")) words = delimiters + 1
ReDim arrLines(1 To words) ReDim arrLeft(1 To words)
'наполнение prevRow = lines + 1 For i = words To 1 Step -1 currRow = arrLines(i) If currRow <> prevRow Then arrRows(currRow) = arrLeft(i) End If prevRow = currRow Next i 'получение визуальных линий (строк) внутри ячейки For i = lines To 2 Step -1 arrRows(i) = LTrim(Replace(arrRows(i), arrRows(i - 1), "", 1, 1)) Next i
rng.Value = Join(arrRows, Chr(10)) End Sub
[/vba]
[vba]
Код
Sub test() Call convertVisualAltEnterToPhysical(Range("A4")) End Sub
Sub convertVisualAltEnterToPhysical(rng As Range)
Dim hgh0 As Double 'высота ячейки для пустой текстовой строки - при подстановке "" в Range.Value (единица высоты, "одна высота")
Dim str0 As String 'исходная строка - исходное значение Range.Value Dim str As String 'исходная строка после замены только очередного пробела/минуса на Alt+Enter Dim strNxt As String 'исходная строка после замены только следующего (очередного+1) пробела/минуса на Alt+Enter
Dim lines As Integer 'кол-во линий (строк) - для размещения исходной строки (текста) в ячейке (без Alt+Enter)
Dim start As Integer 'номер символа начала поиска очередного пробела/минуса Dim startNxt As Integer 'номер символа начала поиска следующего (очередного+1) пробела/минуса Dim posSpace As Integer 'позиция первого найденного ПРОБЕЛА при начале поиска от start Dim posMinus As Integer 'позиция первого найденного МИНУСА при начале поиска от start
Dim delimiters As Integer 'кол-во разделителей (пробелов/минусов) в исходной строке (максимальное потенциально возможное - для определения верхней границы цикла) Dim words As Integer 'кол-во слов = delimiters + 1 Dim strWords As String 'строка после замены минусов/пробелов на двойные тильды - заведомо длиннее исходной Dim lenWords As Integer 'заведомо большая длина этой строки (длина+1) Dim i As Integer 'счётчик цикла по delimiters
Dim arrLines() As Integer 'массив кол-в линий в ячейке, соответствующий "левым" текстам из arrLeft() Dim arrLeft() As String 'массив "левых" текстов = значений ячеек слева до очередного i-го пробела/минуса Dim arrRows() As String 'формируемый массив текстовых строк в ячейке
Dim prevRow As Integer Dim currRow As Integer Dim prevLeft As String Dim currLeft As String
str0 = rng.Value
strWords = Replace(Replace(str0, " ", "~~"), "-", "~~") lenWords = Len(strWords) + 1 'заведомо большая длина всей строки delimiters = UBound(Split(strWords, "~~")) words = delimiters + 1
ReDim arrLines(1 To words) ReDim arrLeft(1 To words)
'наполнение prevRow = lines + 1 For i = words To 1 Step -1 currRow = arrLines(i) If currRow <> prevRow Then arrRows(currRow) = arrLeft(i) End If prevRow = currRow Next i 'получение визуальных линий (строк) внутри ячейки For i = lines To 2 Step -1 arrRows(i) = LTrim(Replace(arrRows(i), arrRows(i - 1), "", 1, 1)) Next i
Добрый день! Много воды утекло с прошлого поста) Использовал функцию convertVisualAltEnterToPhysical в своем проекте. Что выявилось в процессе: 1. Некорректно работает, если многострочный текст находится в нескольких ячейках в одной строке, потому что высота ячейки теперь зависит от соседних ячеек. 2. Не корректно обрабатывает строки, где сочетаются перевод строки "физический" и визуальный экселевский. Обе эти проблемы решаемы. 3. Результат не всегда точно совпадает с тем как оно выглядит в общей ячейке. Не совсем понял из-за чего, и решаемо ли. Может это принципиальное ограничение такого подхода.
Пробовал строить таблицу символов, чтобы перенос строки осуществлять складывая ширину строки из ширины ее символов, но системы в значении ширины ячеек для размещения "А" - 1,73, "АА" - 3,27, "ААА" - 4,82 не смог уловить, кажется для больших строк накопление погрешности будет недопустимым.
На известном забугорном форуме встретил процедуру, определяющую ширину в пикселях для строки с заданным шрифтом.
[vba]
Код
'Option Explicit
'API Declares
Private Declare PtrSafe Function CreateDC Lib "gdi32.dll" Alias "CreateDCA" (ByVal lpDriverName As String, ByVal lpDeviceName As String, ByVal lpOutput As String, lpInitData As Long) As Long Private Declare PtrSafe Function CreateCompatibleBitmap Lib "gdi32.dll" (ByVal hdc As Long, ByVal nWidth As Long, ByVal nHeight As Long) As Long Private Declare PtrSafe Function CreateFontIndirect Lib "gdi32.dll" Alias "CreateFontIndirectA" (lpLogFont As LOGFONT) As Long Private Declare PtrSafe Function SelectObject Lib "gdi32.dll" (ByVal hdc As Long, ByVal hObject As Long) As Long Private Declare PtrSafe Function DeleteObject Lib "gdi32.dll" (ByVal hObject As Long) As Long Private Declare PtrSafe Function GetTextExtentPoint32 Lib "gdi32.dll" Alias "GetTextExtentPoint32A" (ByVal hdc As Long, ByVal lpsz As String, ByVal cbString As Long, lpSize As FNTSIZE) As Long Private Declare PtrSafe Function MulDiv Lib "kernel32.dll" (ByVal nNumber As Long, ByVal nNumerator As Long, ByVal nDenominator As Long) As Long Private Declare PtrSafe Function GetDC Lib "user32.dll" (ByVal hwnd As Long) As Long Private Declare PtrSafe Function GetDeviceCaps Lib "gdi32.dll" (ByVal hdc As Long, ByVal nIndex As Long) As Long Private Declare PtrSafe Function DeleteDC Lib "gdi32.dll" (ByVal hdc As Long) As Long
Private Const LOGPIXELSY As Long = 90
Private Type LOGFONT lfHeight As Long lfWidth As Long lfEscapement As Long lfOrientation As Long lfWeight As Long lfItalic As Byte lfUnderline As Byte lfStrikeOut As Byte lfCharSet As Byte lfOutPrecision As Byte lfClipPrecision As Byte lfQuality As Byte lfPitchAndFamily As Byte lfFaceName As String * 32 End Type
Private Type FNTSIZE cx As Long cy As Long End Type
Public Function GetLabelPixelWidth(label As String) As Integer
Dim font As New StdFont Dim sz As FNTSIZE font.Name = "Arial Narrow" font.Size = 9.5
Public Function GetStringPixelHeight(text As String, fontName As String, fontSize As Single, Optional isBold As Boolean = False, Optional isItalics As Boolean = False) As Integer
Dim font As New StdFont Dim sz As FNTSIZE font.Name = fontName font.Size = fontSize font.Bold = isBold font.Italic = isItalics
Public Function GetStringPixelWidth(text As String, fontName As String, fontSize As Single, Optional isBold As Boolean = False, Optional isItalics As Boolean = False) As Integer
Dim font As New StdFont Dim sz As FNTSIZE font.Name = fontName font.Size = fontSize font.Bold = isBold font.Italic = isItalics
Private Function GetLabelSize(text As String, font As StdFont) As FNTSIZE Dim tempDC As Long Dim tempBMP As Long Dim f As Long Dim lf As LOGFONT Dim textSize As FNTSIZE
' Create a device context and a bitmap that can be used to store a ' temporary font object tempDC = CreateDC("DISPLAY", vbNullString, vbNullString, ByVal 0) tempBMP = CreateCompatibleBitmap(tempDC, 1, 1)
' Assign the bitmap to the device context DeleteObject SelectObject(tempDC, tempBMP)
' Set up the LOGFONT structure and create the font lf.lfFaceName = font.Name & Chr$(0) lf.lfHeight = -MulDiv(font.Size, GetDeviceCaps(GetDC(0), 90), 72) 'LOGPIXELSY lf.lfItalic = font.Italic lf.lfStrikeOut = font.Strikethrough lf.lfUnderline = font.Underline If font.Bold Then lf.lfWeight = 800 Else lf.lfWeight = 400 f = CreateFontIndirect(lf)
' Assign the font to the device context DeleteObject SelectObject(tempDC, f)
' Measure the text, and return it into the textSize SIZE structure GetTextExtentPoint32 tempDC, text, Len(text), textSize
' Clean up (very important to avoid memory leaks!) DeleteObject f DeleteObject tempBMP DeleteDC tempDC ' Return the measurements GetLabelSize = textSize
End Function
[/vba]
Однако [vba]
Код
MsgBox (GetStringPixelWidth("AAA", "Times New Roman", 12))
[/vba] возвращаемое ей значение 51px. Может быть кто подскажет, каким образом можно перейти от этих пикселей в экселевские? Известное для Эксель соотношение 1pt=1,33px тут явно не годится. Да и вообще не понятно, почему гуглится постоянное соотношение с пикселями, которых у каждого на мониторе разное количество, да еще и масштаб пользовательского интерфейса в винде может быть 100-350%.
Решение через сторонние библиотеки нравится тем, что можно приспособить много к чему, не только для переноса строк. Но и в формах, кнопках, и т.д. применять.
Добрый день! Много воды утекло с прошлого поста) Использовал функцию convertVisualAltEnterToPhysical в своем проекте. Что выявилось в процессе: 1. Некорректно работает, если многострочный текст находится в нескольких ячейках в одной строке, потому что высота ячейки теперь зависит от соседних ячеек. 2. Не корректно обрабатывает строки, где сочетаются перевод строки "физический" и визуальный экселевский. Обе эти проблемы решаемы. 3. Результат не всегда точно совпадает с тем как оно выглядит в общей ячейке. Не совсем понял из-за чего, и решаемо ли. Может это принципиальное ограничение такого подхода.
Пробовал строить таблицу символов, чтобы перенос строки осуществлять складывая ширину строки из ширины ее символов, но системы в значении ширины ячеек для размещения "А" - 1,73, "АА" - 3,27, "ААА" - 4,82 не смог уловить, кажется для больших строк накопление погрешности будет недопустимым.
На известном забугорном форуме встретил процедуру, определяющую ширину в пикселях для строки с заданным шрифтом.
[vba]
Код
'Option Explicit
'API Declares
Private Declare PtrSafe Function CreateDC Lib "gdi32.dll" Alias "CreateDCA" (ByVal lpDriverName As String, ByVal lpDeviceName As String, ByVal lpOutput As String, lpInitData As Long) As Long Private Declare PtrSafe Function CreateCompatibleBitmap Lib "gdi32.dll" (ByVal hdc As Long, ByVal nWidth As Long, ByVal nHeight As Long) As Long Private Declare PtrSafe Function CreateFontIndirect Lib "gdi32.dll" Alias "CreateFontIndirectA" (lpLogFont As LOGFONT) As Long Private Declare PtrSafe Function SelectObject Lib "gdi32.dll" (ByVal hdc As Long, ByVal hObject As Long) As Long Private Declare PtrSafe Function DeleteObject Lib "gdi32.dll" (ByVal hObject As Long) As Long Private Declare PtrSafe Function GetTextExtentPoint32 Lib "gdi32.dll" Alias "GetTextExtentPoint32A" (ByVal hdc As Long, ByVal lpsz As String, ByVal cbString As Long, lpSize As FNTSIZE) As Long Private Declare PtrSafe Function MulDiv Lib "kernel32.dll" (ByVal nNumber As Long, ByVal nNumerator As Long, ByVal nDenominator As Long) As Long Private Declare PtrSafe Function GetDC Lib "user32.dll" (ByVal hwnd As Long) As Long Private Declare PtrSafe Function GetDeviceCaps Lib "gdi32.dll" (ByVal hdc As Long, ByVal nIndex As Long) As Long Private Declare PtrSafe Function DeleteDC Lib "gdi32.dll" (ByVal hdc As Long) As Long
Private Const LOGPIXELSY As Long = 90
Private Type LOGFONT lfHeight As Long lfWidth As Long lfEscapement As Long lfOrientation As Long lfWeight As Long lfItalic As Byte lfUnderline As Byte lfStrikeOut As Byte lfCharSet As Byte lfOutPrecision As Byte lfClipPrecision As Byte lfQuality As Byte lfPitchAndFamily As Byte lfFaceName As String * 32 End Type
Private Type FNTSIZE cx As Long cy As Long End Type
Public Function GetLabelPixelWidth(label As String) As Integer
Dim font As New StdFont Dim sz As FNTSIZE font.Name = "Arial Narrow" font.Size = 9.5
Public Function GetStringPixelHeight(text As String, fontName As String, fontSize As Single, Optional isBold As Boolean = False, Optional isItalics As Boolean = False) As Integer
Dim font As New StdFont Dim sz As FNTSIZE font.Name = fontName font.Size = fontSize font.Bold = isBold font.Italic = isItalics
Public Function GetStringPixelWidth(text As String, fontName As String, fontSize As Single, Optional isBold As Boolean = False, Optional isItalics As Boolean = False) As Integer
Dim font As New StdFont Dim sz As FNTSIZE font.Name = fontName font.Size = fontSize font.Bold = isBold font.Italic = isItalics
Private Function GetLabelSize(text As String, font As StdFont) As FNTSIZE Dim tempDC As Long Dim tempBMP As Long Dim f As Long Dim lf As LOGFONT Dim textSize As FNTSIZE
' Create a device context and a bitmap that can be used to store a ' temporary font object tempDC = CreateDC("DISPLAY", vbNullString, vbNullString, ByVal 0) tempBMP = CreateCompatibleBitmap(tempDC, 1, 1)
' Assign the bitmap to the device context DeleteObject SelectObject(tempDC, tempBMP)
' Set up the LOGFONT structure and create the font lf.lfFaceName = font.Name & Chr$(0) lf.lfHeight = -MulDiv(font.Size, GetDeviceCaps(GetDC(0), 90), 72) 'LOGPIXELSY lf.lfItalic = font.Italic lf.lfStrikeOut = font.Strikethrough lf.lfUnderline = font.Underline If font.Bold Then lf.lfWeight = 800 Else lf.lfWeight = 400 f = CreateFontIndirect(lf)
' Assign the font to the device context DeleteObject SelectObject(tempDC, f)
' Measure the text, and return it into the textSize SIZE structure GetTextExtentPoint32 tempDC, text, Len(text), textSize
' Clean up (very important to avoid memory leaks!) DeleteObject f DeleteObject tempBMP DeleteDC tempDC ' Return the measurements GetLabelSize = textSize
End Function
[/vba]
Однако [vba]
Код
MsgBox (GetStringPixelWidth("AAA", "Times New Roman", 12))
[/vba] возвращаемое ей значение 51px. Может быть кто подскажет, каким образом можно перейти от этих пикселей в экселевские? Известное для Эксель соотношение 1pt=1,33px тут явно не годится. Да и вообще не понятно, почему гуглится постоянное соотношение с пикселями, которых у каждого на мониторе разное количество, да еще и масштаб пользовательского интерфейса в винде может быть 100-350%.
Решение через сторонние библиотеки нравится тем, что можно приспособить много к чему, не только для переноса строк. Но и в формах, кнопках, и т.д. применять.Gendel
Сообщение отредактировал Gendel - Четверг, 30.05.2024, 19:37