WinAPI и TrueType шрифты
От: Hydrophobia  
Дата: 27.03.18 12:32
Оценка:
Разрабатываю библиотеку по конвертации EMF файлов в PDF документ и появились вопросы как WinAPI интерпретирует данные TrueType шрифтов.
В TrueType шрифтах есть таблица hmtx, которая хранит advance width и left-side bearing (LSB) в design юнитах. Для преобразования design units -> device units пользуюсь формулой

 DeviceUnits = (DesignUnits/unitsPerEm) * (PointSize/72) * DeviceResolution



В WinAPI есть функция GetCharABCWidths(), которая возвращает в структуре ABC параметры глифов в виде набора
abcA — ширина отступа перед отрисовкой глифа, LSB в терминах TrueType;
abcB — ширина глифа, вычисляется как (xmax — xmin) из таблицы glyf;
abcC — ширина отступа после отрисовки глифа, вычисляется как advance width — LSB — (xmax — xmin);

В итоге полная ширина глифа равна сумме трех составляющих abcA + abcB + abcC и она должна равняться advance width из таблицы hmtx. Но она отличается на 1-2 логических юнита, что приводит к наложению соседних слов в PDF документе, когда в EMF все корректно. Например,




здесь черным цветом — расположение символов в EMF документе, зеленым — расположение в PDF документе.

Может кто подскажет, что упустил из расчетов? Книгу "Windows Graphics Programming Win32 GDI and DirectDraw" Feng Yuan смотрел, там про расчеты ничего не сказано.
winapi emf
Re: WinAPI и TrueType шрифты
От: VladFein США  
Дата: 27.03.18 18:10
Оценка: +1
Здравствуйте, Hydrophobia, Вы писали:

H>Может кто подскажет, что упустил из расчетов? Книгу "Windows Graphics Programming Win32 GDI and DirectDraw" Feng Yuan смотрел, там про расчеты ничего не сказано.


Rounding?
https://msdn.microsoft.com/en-us/library/windows/desktop/dd183564(v=vs.85).aspx

Device units are always rounded to the nearest pixel. The propagated round-off error can become very large, especially when an application is working with screen sizes.

Re[2]: WinAPI и TrueType шрифты
От: Hydrophobia  
Дата: 28.03.18 05:05
Оценка:
Здравствуйте, VladFein, Вы писали:


VF>Rounding?

VF>https://msdn.microsoft.com/en-us/library/windows/desktop/dd183564(v=vs.85).aspx
VF>

Device units are always rounded to the nearest pixel. The propagated round-off error can become very large, especially when an application is working with screen sizes.



округление присутствует, использую std::lround(). В wine что-то похожее используют.
Re[3]: WinAPI и TrueType шрифты
От: VladFein США  
Дата: 28.03.18 16:38
Оценка:
Здравствуйте, Hydrophobia, Вы писали:

H>округление присутствует, использую std::lround(). В wine что-то похожее используют.


Вывод в PDF идёт по словам или по буквам?
Очевидно, что начало второго слова посчитано правильно.
Я бы попробовал вычислять X для каждой буквы отдельно, что бы не накапливать ошибки округления.
Re[4]: WinAPI и TrueType шрифты
От: Hydrophobia  
Дата: 28.03.18 16:57
Оценка:
Здравствуйте, VladFein, Вы писали:


VF>Вывод в PDF идёт по словам или по буквам?


картинка из топика — вывод по словам.

VF>Очевидно, что начало второго слова посчитано правильно.

VF>Я бы попробовал вычислять X для каждой буквы отдельно, что бы не накапливать ошибки округления.

да, так сделал, получилось весьма пригодно, но все равно местами межсимвольное расстояние скачет. С другой стороны в EMF есть текстовые записи (EMR_EXTTEXTOUT[A, W]), в которых есть информация по межсимвольному расстоянию (массив EMREXTTEXTOUTW::emrtext::offDx). Если EMF базируется на устройстве с низким DPI (например, экран), то вывод текста в PDF очень низкого качества:



слово "within" разбивается на две части, причина — большая разница в ширине глифа 'w'.
Re[5]: WinAPI и TrueType шрифты
От: c-smile Канада http://terrainformatica.com
Дата: 29.03.18 03:28
Оценка: 9 (1)
Здравствуйте, Hydrophobia, Вы писали:

H>Если EMF базируется на устройстве с низким DPI (например, экран), то вывод текста в PDF очень низкого качества


Решал я похожую задачу в одном из своих WYSIWYG редакторов. Но не решил до конца ибо она решения не имеет в общем случае.
В EMF (т.е. GDI) глифы приколочены к пиксельным сеткам. PDF же оперирует векторами.

MS Word например для вывода текста на экран не использует примитивы GDI (типа TextOut например) в чистом виде.
Позиции глифов там float numbers, а не integer как в GDI.

Задача имеет квази-оптимальное решение только если ты сам осуществляешь EMF генерацию и выставляешь большое разрешение — 300 DPI как минимум.
Для произвольных же EMF сделанных в масштабе экрана — увы и ах.

Тем не менее можешь попробовать вытащить что-нибудь отсюда: https://www.codeproject.com/Articles/8410/Presenting-EMFexplorer-a-GDI-experiment
Но вот сомнительно всё же.
Re[6]: WinAPI и TrueType шрифты
От: Hydrophobia  
Дата: 29.03.18 06:43
Оценка:
Здравствуйте, c-smile, Вы писали:


CS>Задача имеет квази-оптимальное решение только если ты сам осуществляешь EMF генерацию и выставляешь большое разрешение — 300 DPI как минимум.

CS>Для произвольных же EMF сделанных в масштабе экрана — увы и ах.

Генерация EMF осуществляется самостоятельно, но не нашел способа выставить DPI так, чтобы вызовы типа GetDeviceCaps() вовзращали установленное DPI.
А "виртуальное" DPI выставляю так

    // Don't forget Windows Text Scale factor: DPI * HORZSIZE / (HORZRES * 25.4) * (HORZRES / DESKTOPHORZRES)
    float const scaleX = (float) mDeviceCaps.mLogPixelsX * GetDeviceCaps(hdc, HORZSIZE) / ((float) GetDeviceCaps(hdc, DESKTOPHORZRES) * 25.4f);
    float const scaleY = (float) mDeviceCaps.mLogPixelsY * GetDeviceCaps(hdc, VERTSIZE) / ((float) GetDeviceCaps(hdc, DESKTOPVERTRES) * 25.4f);

    mHdc = CreateWriteableEmf(hdc, to01mm(hdc, width * scaleX, height * scaleY), path, flags);



CS>Тем не менее можешь попробовать вытащить что-нибудь отсюда: https://www.codeproject.com/Articles/8410/Presenting-EMFexplorer-a-GDI-experiment


спасибо, посмотрю.
Re[7]: WinAPI и TrueType шрифты
От: c-smile Канада http://terrainformatica.com
Дата: 29.03.18 21:49
Оценка:
Здравствуйте, Hydrophobia, Вы писали:

H>Здравствуйте, c-smile, Вы писали:



CS>>Задача имеет квази-оптимальное решение только если ты сам осуществляешь EMF генерацию и выставляешь большое разрешение — 300 DPI как минимум.

CS>>Для произвольных же EMF сделанных в масштабе экрана — увы и ах.

H>Генерация EMF осуществляется самостоятельно, но не нашел способа выставить DPI так, чтобы вызовы типа GetDeviceCaps() вовзращали установленное DPI.

H>А "виртуальное" DPI выставляю так

Ну тогда я бы просто размеры и позиции всех выводимых GDI примитивов (в т.ч. font sizes) умножал на 100 и в EMF имел бы fixed float координаты —
float pdfPos = emfPos / 100.0f;


Как-то так в общем.

А что ты используешь в качестве PDF canvas? Что-то свое или библиотеку какую?
Re[8]: WinAPI и TrueType шрифты
От: Hydrophobia  
Дата: 30.03.18 09:06
Оценка:
Здравствуйте, c-smile, Вы писали:


CS>Ну тогда я бы просто размеры и позиции всех выводимых GDI примитивов (в т.ч. font sizes) умножал на 100 и в EMF имел бы fixed float координаты —

CS>
CS>float pdfPos = emfPos / 100.0f;  
CS>


CS>Как-то так в общем.


видимо обманул, генерация EMF осуществляется внешней программой и она для рассчетов использует GetDeviceCaps(). Переписать нет возможности — ей 10-15 лет отроду.

CS>А что ты используешь в качестве PDF canvas? Что-то свое или библиотеку какую?


своя pdf библиотека, сверху emf2pdf конвертер.
Re[6]: WinAPI и TrueType шрифты
От: Hydrophobia  
Дата: 31.03.18 09:02
Оценка:
Здравствуйте, c-smile, Вы писали:


CS>Задача имеет квази-оптимальное решение только если ты сам осуществляешь EMF генерацию и выставляешь большое разрешение — 300 DPI как минимум.

CS>Для произвольных же EMF сделанных в масштабе экрана — увы и ах.

идея с сеткой осенила меня — можно же 'двигать' глиф в рамках 'сетки' GDI. Т.е. для глифов у которых разница (w — w') > k, где w — ширина глифа в GDI, w' — ширина глифа из TrueType, k — число, использовать межсимвольное расстояние заданное в EMF (offDx[i] — w). Для остальных глифов — центрировать положение (offDx[i] — w' — (w — w')/2). Параметр k взял равный 3 пикселя. Ниже пример с коррекцией и без.




c-smile, cпасибо!
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.