Другое

Полное руководство по размеру шрифтов в Excel PDF

Узнайте, как исправить проблемы с размером шрифтов при конвертации файлов Excel в PDF с использованием ClosedXML и iTextSharp. Найдите решения для расчета ширины столбцов, внедрения шрифтов и правильного размещения текста в ячейках PDF.

Проблема с преобразованием Excel в PDF: изменение размера текста с использованием ClosedXML и iTextSharp

Я столкнулся с проблемой изменения размера текста при преобразовании файла Excel в PDF с использованием ClosedXML и iTextSharp. Моя цель - обеспечить идеальное соответствие текста внутри каждой ячейки, но иногда текст изменяется некорректно. Интересно, что когда я отключаю изменение размера, текст фактически правильно помещается внутри ячейки.

Текущая реализация

Вот мой текущий код для обработки ширины столбцов и размера текста:

vb
Dim colWidthsExcel As New List(Of Double)
For c As Integer = firstColNum To lastColNum
    Dim widthCol As Double = worksheet.Column(c).Width
    If widthCol <= 0 Then widthCol = 1
    widthCol = widthCol
    colWidthsExcel.Add(widthCol)
Next

Dim sumWidths As Double = colWidthsExcel.Sum()
Dim availablePageWidth As Single = document.PageSize.Width - document.LeftMargin - document.RightMargin

Dim colWidthsPoints(colCount - 1) As Single
For i As Integer = 0 To colCount - 1
    colWidthsPoints(i) = CSng((colWidthsExcel(i) / sumWidths) * availablePageWidth)
Next

For row As Integer = firstRowNum To lastRowNum
    For col As Integer = firstColNum To lastColNum
        Dim cell = worksheet.Cell(row, col)
        If cell Is Nothing Then Continue For

        Dim idx = col - firstColNum
        Dim cellWidth As Single = 40.0F

        If idx >= 0 AndAlso idx < colWidthsPoints.Length Then
            If cell.IsMerged() Then
                Dim mRange As IXLRange = cell.MergedRange()
                Dim spanCols As Integer = Math.Max(1, mRange.ColumnCount())
                cellWidth = 0
                For k As Integer = idx To Math.Min(idx + spanCols - 1, colWidthsPoints.Length - 1)
                    cellWidth += colWidthsPoints(k)
                Next
            Else
                cellWidth = colWidthsPoints(idx)
            End If
        End If

        Dim cellPadding As Single = 4.0F ' Внутренний отступ ячейки PDF (2 точки с каждой стороны)
        Dim availableWidth As Single = cellWidth - cellPadding

        Dim text As String = cell.GetFormattedString().Trim()

        Dim fx = cell.Style.Font
        Dim cellsize As Single = cell.Style.Font.FontSize * scaleFactor
        Dim pdfFont As iTextSharp.text.Font = getFont(cell, cellsize)
        Dim baseFont As iTextSharp.text.pdf.BaseFont = pdfFont.BaseFont
        If baseFont Is Nothing Then
            baseFont = iTextSharp.text.pdf.BaseFont.CreateFont(iTextSharp.text.pdf.BaseFont.HELVETICA, iTextSharp.text.pdf.BaseFont.CP1252, iTextSharp.text.pdf.BaseFont.NOT_EMBEDDED)
        End If

        Dim size As Single = GetFittingFontSize_SingleLine(baseFont:=baseFont, text:=text, cellSize:=cellsize, availableWidth:=availableWidth, isWrapCell:=cell.Style.Alignment.WrapText)
    Next
Next

Private Function GetFittingFontSize_SingleLine(baseFont As iTextSharp.text.pdf.BaseFont, text As String, cellSize As Single, availableWidth As Single, isWrapCell As Boolean, Optional minSize As Single = 2.0F, Optional stepSize As Single = 0.5F) As Single
    If String.IsNullOrEmpty(text) OrElse isWrapCell Then
        Return cellSize
    End If
    Dim size As Single = cellSize
    While size > minSize
        getMinValuelStep05(size)
        Dim textWidth As Single = baseFont.GetWidthPoint(text, size)
        If textWidth <= availableWidth Then Exit While
        size -= stepSize
    End While
    If size < minSize Then size = minSize
    Return size
End Function

Проблема

Несмотря на реализацию этой логики, текст иногда изменяется некорректно при преобразовании Excel в PDF. Когда я отключаю функцию изменения размера, текст правильно помещается внутри ячейки. Это указывает на возможную проблему с моими вычислениями размера шрифта или определения ширины.

Вопрос

Что может вызывать некорректное изменение размера текста в моем преобразовании Excel в PDF с использованием ClosedXML и iTextSharp? Есть ли какие-либо проблемы в моем вычислении ширины столбцов или логике изменения размера шрифта, которые мне следует исправить?

Проблемы с изменением размера текста при преобразовании Excel в PDF с использованием ClosedXML и iTextSharp

Проблемы с изменением размера текста при преобразовании Excel в PDF с использованием ClosedXML и iTextSharp обычно связаны с несколькими распространенными проблемами в расчетах шрифтов, определении ширины и обработке шрифтов. Ваша реализация имеет несколько областей, которые могут вызывать некорректное поведение изменения размера, которое вы наблюдаете.

Основные проблемы, скорее всего, связаны с неправильными расчетами ширины шрифта, некорректным масштабированием ширины столбцов из единиц Excel в единицы PDF, несоответствиями встраивания шрифтов и недостаточной обработкой отступов ячеек и полей.


Содержание


Основные причины проблем с размером текста

Несколько факторов способствуют некорректному изменению размера текста при преобразовании Excel в PDF:

Неточности в расчетах ширины шрифта - Метод GetWidthPoint в iTextSharp вычисляет ширину строки, но он может не учитывать все характеристики шрифта, которые учитывает Excel. Согласно исследованиям на Stack Overflow, хотя BaseFont.getWidthPoint(String text, float fontSize) предоставляет ширину в пунктах, он может не идеально соответствовать рендеринг-движку Excel.

Несоответствия отступов ячеек и полей - Ваш код использует фиксированные отступы в 4 пункта, но отступы ячеек в Excel измеряются иначе и могут варьироваться в зависимости от границ и форматирования. Расчет доступной ширины может быть неверным, что приводит к чрезмерно агрессивному уменьшению шрифта.

Несогласованность масштабирования шрифта - Используемый вами scaleFactor может корректно не переводить между системой изменения размера шрифта Excel и пунктами PDF. Excel использует разные единицы и масштабирование, чем PDF, что требует тщательного преобразования.

Обработка объединенных ячеек - Ваш расчет ширины объединенных ячеек складывает ширины столбцов, но не учитывает, как Excel распределяет пространство в объединенных диапазонах, что может привести к неверным расчетам доступной ширины.


Проблемы с расчетом ширины столбцов

Ваш текущий расчет ширины столбцов имеет несколько проблем, которые могут вызывать затруднения:

vb
Dim widthCol As Double = worksheet.Column(c).Width
If widthCol <= 0 Then widthCol = 1

Единицы ширины столбцов Excel - Ширины столбцов в Excel измеряются в символах, а не в пунктах. Ширина 8.43 в Excel представляет среднее количество символов, помещающихся в ширину столбца по умолчанию. Ваше преобразование в пункты должно корректно учитывать это.

Отсутствие учета шрифта - Расчеты ширины столбцов в Excel зависят от шрифта по умолчанию (обычно Calibri 11pt). При расчете доступной ширины следует использовать это как отправную точку, а не предполагать прямое преобразование.

Проблемы пропорционального распределения - Ваш текущий подход делит доступную ширину страницы пропорционально на основе ширин столбцов Excel, но это не учитывает ограничения страницы PDF, такие как поля, верхние и нижние колонтитулы, которые могут уменьшить фактическое доступное пространство.

Резервная фиксированная ширина ячейки - Резервное значение cellWidth = 40.0F для индексов вне границ может вызывать несогласованное изменение размера при добавлении или удалении столбцов из файла Excel.


Ошибки в расчетах размера шрифта

Функция GetFittingFontSize_SingleLine имеет несколько проблемных областей:

vb
Private Function GetFittingFontSize_SingleLine(baseFont As iTextSharp.text.pdf.BaseFont, text As String, cellSize As Single, availableWidth As Single, isWrapCell As Boolean, Optional minSize As Single = 2.0F, Optional stepSize As Single = 0.5F) As Single
    If String.IsNullOrEmpty(text) OrElse isWrapCell Then
        Return cellSize
    End If
    Dim size As Single = cellSize
    While size > minSize
        getMinValuelStep05(size)  ' Этот вызов метода вызывает подозрения
        Dim textWidth As Single = baseFont.GetWidthPoint(text, size)
        If textWidth <= availableWidth Then Exit While
        size -= stepSize
    End While
    If size < minSize Then size = minSize
    Return size
End Function

Вызов неопределенного метода - Вызов getMinValuelStep05(size) кажется неопределенным или неправильно названным, что может вызывать непредвиденное поведение или ошибки.

Неточный расчет ширины - Согласно документации iTextSharp, GetWidthPoint вычисляет ширину, но не учитывает метрики шрифта, такие как подъем (ascent) и спуск (descent), которые влияют на использование вертикального пространства.

Фиксированный размер шага - Использование фиксированного размера шага 0.5F может быть слишком агрессивным для некоторых шрифтов и слишком консервативным для других, что приводит к оптимальному изменению размера.

Отсутствие учета метрик шрифта - Расчет не учитывает ограничивающие рамки шрифта (bounding boxes) или кернинг, которые могут влиять на горизонтальное размещение текста.


Проблемы с встраиванием и сопоставлением шрифтов

Обработка шрифтов является распространенным источником проблем при преобразовании Excel в PDF:

Несоответство резервного шрифта - Ваш переход на Helvetica может не соответствовать исходному шрифту Excel, вызывая разные расчеты ширины:

vb
If baseFont Is Nothing Then
    baseFont = iTextSharp.text.pdf.BaseFont.CreateFont(iTextSharp.text.pdf.BaseFont.HELVETICA, iTextSharp.text.pdf.BaseFont.CP1252, iTextSharp.text.pdf.BaseFont.NOT_EMBEDDED)
End If

Проблемы с встраиванием шрифтов - Согласно исследованиям Document Solutions, шрифты должны быть правильно встроены, и путь к шрифту должен быть указан, особенно на системах, отличных от Windows.

Несоответства стилей шрифта - Стили шрифта Excel (жирный, курсив, подчеркивание) могут быть неправильно переведены в шрифты iTextSharp, что приводит к разным расчетам ширины.

Проблемы кодировки символов - Кодировка CP1252 может не поддерживать все символы в исходном файле Excel, вызывая расхождения в расчетах ширины.


Рекомендуемые исправления и улучшения

Вот конкретные исправления для решения проблем в вашей реализации:

Улучшенный расчет ширины столбцов

vb
' Более точный расчет ширины столбца Excel в пунктах
Dim widthCol As Double = worksheet.Column(c).Width
If widthCol <= 0 Then widthCol = 1 ' Ширина по умолчанию

' Преобразование единиц символов Excel в пункты (используя шрифт по умолчанию как эталон)
' В Excel по умолчанию используется Calibri 11pt, средняя ширина символа составляет примерно 7 пунктов
Dim colWidthPoints As Single = CSng(widthCol * 7.0F) ' Более точное преобразование
colWidthsExcel.Add(colWidthPoints)

Улучшенная функция изменения размера шрифта

vb
Private Function GetFittingFontSize_SingleLine(baseFont As iTextSharp.text.pdf.BaseFont, text As String, cellSize As Single, availableWidth As Single, isWrapCell As Boolean, Optional minSize As Single = 2.0F, Optional maxIterations As Integer = 20) As Single
    If String.IsNullOrEmpty(text) OrElse isWrapCell Then
        Return cellSize
    End If
    
    ' Удаляем подозрительный вызов метода
    Dim size As Single = cellSize
    Dim iteration As Integer = 0
    
    ' Используем двоичный поиск для более эффективного изменения размера
    Dim low As Single = minSize
    Dim high As Single = cellSize
    Dim bestFit As Single = minSize
    
    While iteration < maxIterations AndAlso (high - low) > 0.1F
        size = (low + high) / 2
        Dim textWidth As Single = baseFont.GetWidthPoint(text, size)
        
        If textWidth <= availableWidth Then
            bestFit = size
            low = size + 0.1F
        Else
            high = size - 0.1F
        End If
        
        iteration += 1
    End While
    
    Return bestFit
End Function

Лучшая обработка шрифтов

vb
Private Function getFont(cell As IXLCell, targetSize As Single) As iTextSharp.text.Font
    Try
        Dim fx = cell.Style.Font
        Dim fontName As String = fx.FontName
        Dim fontStyle As iTextSharp.text.Font.FontStyle = iTextSharp.text.Font.NORMAL
        
        If fx.Bold Then fontStyle = fontStyle Or iTextSharp.text.Font.BOLD
        If fx.Italic Then fontStyle = fontStyle Or iTextSharp.text.Font.ITALIC
        If fx.Underline Then fontStyle = fontStyle Or iTextSharp.text.Font.UNDERLINE
        
        ' Пытаемся более точно соответствовать шрифту Excel
        Dim baseFont As iTextSharp.text.pdf.BaseFont
        Try
            ' Сопоставляем распространенные шрифты Excel с эквивалентами iTextSharp
            Select Case fontName.ToLower()
                Case "arial", "arial narrow"
                    baseFont = iTextSharp.text.pdf.BaseFont.CreateFont(iTextSharp.text.pdf.BaseFont.HELVETICA, iTextSharp.text.pdf.BaseFont.WINANSI, iTextSharp.text.pdf.BaseFont.EMBEDDED)
                Case "times new roman", "times"
                    baseFont = iTextSharp.text.pdf.BaseFont.CreateFont(iTextSharp.text.pdf.BaseFont.TIMES_ROMAN, iTextSharp.text.pdf.BaseFont.WINANSI, iTextSharp.text.pdf.BaseFont.EMBEDDED)
                Case "courier new", "courier"
                    baseFont = iTextSharp.text.pdf.BaseFont.CreateFont(iTextSharp.text.pdf.BaseFont.COURIER, iTextSharp.text.pdf.BaseFont.WINANSI, iTextSharp.text.pdf.BaseFont.EMBEDDED)
                Case Else
                    baseFont = iTextSharp.text.pdf.BaseFont.CreateFont(fontName, iTextSharp.text.pdf.BaseFont.WINANSI, iTextSharp.text.pdf.BaseFont.EMBEDDED)
            End Select
        Catch ex As Exception
            ' Переходим на Arial, если шрифт не найден
            baseFont = iTextSharp.text.pdf.BaseFont.CreateFont(iTextSharp.text.pdf.BaseFont.HELVETICA, iTextSharp.text.pdf.BaseFont.WINANSI, iTextSharp.text.pdf.BaseFont.EMBEDDED)
        End Try
        
        Return New iTextSharp.text.Font(baseFont, targetSize, fontStyle)
    Catch ex As Exception
        Return New iTextSharp.text.Font(iTextSharp.text.pdf.BaseFont.HELVETICA, targetSize)
    End Try
End Function

Альтернативные подходы

Если вы продолжаете испытывать проблемы с текущим подходом, рассмотрите эти альтернативы:

Использование коммерческих библиотек

Document Solutions for Excel - Согласно документации Document Solutions, эта библиотека предоставляет встроенное преобразование Excel в PDF с правильной обработкой шрифтов:

vb
' Использование Document Solutions for Excel
Dim workbook = new Workbook()
workbook.Load("input.xlsx")
workbook.SaveAsPdf("output.pdf")

Корректировка расчета отступов ячеек

Вместо фиксированных отступов используйте динамические отступы, основанные на размере шрифта:

vb
' Динамический расчет отступов
Dim paddingFactor As Single = 0.15F ' 15% от размера шрифта
Dim cellPadding As Single = CSng(cellsize * paddingFactor * 2) ' С обеих сторон

Рассмотрите возможность переноса текста

Для длинного текста рассмотрите возможность включения переноса текста вместо уменьшения шрифта:

vb
If textWidth > availableWidth AndAlso text.Length > 10 Then
    ' Включаем перенос и используем исходный размер шрифта
    Return cellSize
End If

Лучшие практики для преобразования Excel в PDF

Для обеспечения надежного преобразования Excel в PDF с правильным изменением размера текста:

Тестируйте с различными шрифтами - Разные шрифты имеют разные характеристики. Тестируйте ваше преобразование с Arial, Times New Roman и другими часто используемыми шрифтами.

Проверяйте встраивание шрифтов - Убедитесь, что все шрифты правильно встроены с помощью BaseFont.EMBEDDED для поддержания согласованности на разных системах.

Обрабатывайте крайние случаи - Учитывайте объединенные ячейки, скрытые столбцы и специальные символы, которые могут влиять на расчеты ширины.

Используйте подходящие единицы измерения - Будьте последовательны в использовании пунктов во всем процессе преобразования.

Учитывайте производительность - Подход с двоичным поиском для изменения размера шрифта более эффективен, чем линейный поиск, особенно для больших документов.

Тестируйте на разных системах - Рендеринг шрифтов может различаться в средах Windows, macOS и Linux.

Устраняя эти проблемы в расчетах изменения размера шрифта, определении ширины столбцов и обработке шрифтов, вы должны решить проблемы некорректного изменения размера текста в вашем преобразовании Excel в PDF с использованием ClosedXML и iTextSharp.


Источники

  1. Stack Overflow - Расчет ширины строки с использованием BaseFont.GetWidthPoint
  2. Документация iTextSharp - Расчет ширины шрифта
  3. Document Solutions for Excel - Преобразование Excel в PDF
  4. Stack Overflow - Проблемы с преобразованием ширины из XLSX (Closed XML) в PDF (iTextSharp)
  5. Документация iTextSharp - Автоматическая корректировка размера шрифта
  6. Примеры кода C# - Использование BaseFont.GetWidthPoint

Заключение

Основные проблемы, вызывающие некорректное изменение размера текста в вашем преобразовании Excel в PDF, включают:

Неточности в расчетах ширины шрифта - Метод GetWidthPoint должен использоваться с правильными метриками шрифта и учетом характеристик шрифта по умолчанию в Excel.

Проблемы преобразования ширины столбцов - Ширины столбцов Excel, измеряемые в символах, требуют точного преобразования в пункты PDF с использованием шрифта по умолчанию как эталона.

Проблемы с встраиванием и сопоставлением шрифтов - Правильное сопоставление и встраивание шрифтов критически важны для поддержания согласованности изменения размера на разных системах.

Неэффективность алгоритма - Подход с линейным поиском в вашей функции изменения размера шрифта можно улучшить с помощью двоичного поиска для лучшей производительности и точности.

Реализуя рекомендуемые исправления, особенно улучшая расчет ширины столбцов, совершенствуя алгоритм изменения размера шрифта и обеспечивая правильное встраивание и сопоставление шрифтов, вы должны добиться согласованного и точного изменения размера текста в ваших преобразованиях Excel в PDF. Всегда тестируйте с различными шрифтами и крайними случаями для обеспечения надежной работы на разных файлах Excel и в системных средах.

Авторы
Проверено модерацией
Модерация
Полное руководство по размеру шрифтов в Excel PDF