Преобразовать ширину столбца Excel между символами и пикселями (точками) - PullRequest
0 голосов
/ 05 апреля 2020

"Одна единица ширины столбца равна ширине одного символа в стиле Normal. Для пропорциональных шрифтов используется ширина символа 0 (ноль)."

Таким образом, ColumnWidth в Excel измеряется как число символов «0», которое помещается в столбце. Как можно преобразовать это значение в пиксели и наоборот?

Image from https://bettersolutions.com/excel/rows-columns/column-widths.htm

1 Ответ

0 голосов
/ 05 апреля 2020

Как уже упоминалось, значение ColumnWidth в Excel зависит от шрифта рабочей книги по умолчанию, который можно получить с помощью Workbook.Styles("Normal").Font. Кроме того, это зависит от текущего разрешения экрана.

Проведя некоторые исследования для различных шрифтов и размеров в Excel 2013, я обнаружил, что у нас есть 2 линейные функции (Arial нельзя увидеть, поскольку он перекрывается с Tahoma.) :

enter image description here

Как видно на рисунке, функция для ColumnWidth < 1 отличается от основной части линейного графика. Он рассчитывается как количество пикселей в столбце / количество пикселей, необходимое для размещения одного символа «0» в столбце.

Теперь давайте посмотрим, из чего состоит типичная ширина ячейки.

enter image description here

  • A - ширина символа "0" в обычном стиле
  • B - отступы слева и справа
  • C - 1px правое поле

A можно рассчитать с помощью GetTextExtentPoint32 Windows API-функции, но размер шрифта должен быть немного больше. Экспериментально я выбрал + 0.3pt, который работал для разных шрифтов с базовым размером 8-48pt. B - (A + 1) / 4 округляется до целого числа, используя "округлить до половины" . Также здесь потребуется экран DPI (см. Реализацию Python 3 ниже)

Вот уравнения для преобразования символов в пиксели и их реализация в Python 3:

enter image description here

enter image description here

enter image description here

import win32print, win32gui
from math import floor

def get_screen_dpi():
    dc = win32gui.GetDC(0)
    LOGPIXELSX, LOGPIXELSY = 88, 90
    dpi = [win32print.GetDeviceCaps(dc, i) for i in (LOGPIXELSX,
                                                        LOGPIXELSY)]
    win32gui.ReleaseDC(0, dc)
    return dpi

def get_text_metrics(fontname, fontsize):
    "Measures '0' char size for the specified font name and size in pt"
    dc = win32gui.GetDC(0)
    font = win32gui.LOGFONT()
    font.lfFaceName = fontname
    font.lfHeight = -fontsize * dpi[1] / 72
    hfont = win32gui.CreateFontIndirect(font)
    win32gui.SelectObject(dc, hfont)
    metrics = win32gui.GetTextExtentPoint32(dc, "0")
    win32gui.ReleaseDC(0, dc)
    return metrics

def ch_px(v, unit="ch"):
    """
    Convert between Excel character width and pixel width.
    `unit` - unit to convert from: 'ch' (default) or 'px'
    """
    rd = lambda x: floor(x + 0.5)  # round half up
    # pad = left cell padding + right cell padding + cell border(1)
    pad = rd((z + 1) / 4) * 2 + 1
    z_p = z + pad  # space (px) for "0" character with padding
    if unit == "ch":
        return v * z_p if v < 1 else v * z + pad
    else:
        return v / z_p if v < z_p else (v - pad) / z

font = "Calibri", 11
dpi = get_screen_dpi()
z = get_text_metrics(font[0], font[1] + 0.3)[0]  # "0" char width in px
px = ch_px(30, "ch")
ch = ch_px(px, "px")
print("Characters:", ch, "Pixels:", px, "for", font)
...