#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Конвертер OpenType/CFF (.otf) → TrueType (.ttf) для dompdf.

Зачем: dompdf (php-font-lib) умеет внедрять в PDF только TrueType-контуры.
Файл .otf он разбирает настолько, что снимает метрики и расставляет текст,
но сам шрифт в документ не попадает — в pdffonts выходит emb=no, и вместо
букв остаются пустые места.

Установка зависимостей (один раз):
    pip3 install fonttools cu2qu

Запуск:
    python3 otf2ttf.py alethiapro-regular.otf
    python3 otf2ttf.py alethiapro-regular.otf alethiapro-italic.otf
    python3 otf2ttf.py *.otf

Рядом с каждым .otf появится .ttf. Дальше шрифт надо переустановить
в dompdf — см. инструкцию в конце вывода скрипта.
"""

import sys
import os
import re

MAX_ERR = 1.0          # допуск аппроксимации кривых, в единицах em/1000
POST_FORMAT = 3.0      # без таблицы имён глифов — файл меньше


def convert(src, dst=None):
    from fontTools.ttLib import TTFont, newTable
    from fontTools.pens.ttGlyphPen import TTGlyphPen
    from cu2qu.pens import Cu2QuPen

    if dst is None:
        dst = os.path.splitext(src)[0] + '.ttf'

    font = TTFont(src)

    if 'glyf' in font:
        print(f'  {os.path.basename(src)}: это уже TrueType, конвертация не нужна')
        return None
    if 'CFF ' not in font and 'CFF2' not in font:
        print(f'  {os.path.basename(src)}: не найдены контуры CFF — пропускаю')
        return None

    order = font.getGlyphOrder()
    glyph_set = font.getGlyphSet()
    upem = font['head'].unitsPerEm
    tolerance = MAX_ERR / 1000 * upem

    # Кубические кривые CFF → квадратичные TrueType
    glyphs = {}
    for name in order:
        pen = TTGlyphPen(glyphs)
        glyph_set[name].draw(Cu2QuPen(pen, tolerance, reverse_direction=True))
        glyphs[name] = pen.glyph()

    font['loca'] = newTable('loca')
    glyf = font['glyf'] = newTable('glyf')
    glyf.glyphOrder = order
    glyf.glyphs = glyphs

    for tag in ('CFF ', 'CFF2', 'VORG'):
        if tag in font:
            del font[tag]
    glyf.compile(font)

    maxp = font['maxp'] = newTable('maxp')
    maxp.tableVersion = 0x00010000
    maxp.maxZones = 1
    maxp.maxTwilightPoints = 0
    maxp.maxStorage = 0
    maxp.maxFunctionDefs = 0
    maxp.maxInstructionDefs = 0
    maxp.maxStackElements = 0
    maxp.maxSizeOfInstructions = 0
    maxp.numGlyphs = len(order)
    maxp.compile(font)

    post = font['post']
    post.formatType = POST_FORMAT
    post.extraNames = []
    post.mapping = {}
    post.glyphOrder = order
    try:
        post.compile(font)
    except OverflowError:
        post.formatType = 3.0

    # PostScript-имя без пробелов — иначе dompdf соберёт битый /BaseFont
    ps, fixed = fix_ps_name(font)

    # Главное: файл должен начинаться сигнатурой TrueType, а не OTTO
    font.sfntVersion = '\000\001\000\000'
    font.save(dst)

    size = os.path.getsize(dst)
    print(f'  {os.path.basename(src)} → {os.path.basename(dst)} '
          f'({size} байт, глифов: {len(order)})')
    print(f'     PostScript-имя: {ps}' + (' (исправлено)' if fixed else ''))
    return dst


def fix_ps_name(font):
    """
    PostScript-имя (nameID 6) dompdf пишет в PDF как /BaseFont. Пробел или
    кириллица в нём ломают синтаксис PDF — часть просмотрщиков такой шрифт
    просто выкидывает. Если имя отсутствует или невалидно, собираем его
    из семейства и начертания: «Alethia Pro» + «Regular» → AlethiaPro-Regular.
    """
    name = font['name']
    ps = name.getDebugName(6)
    valid = bool(ps) and len(ps) <= 63 and all(
        33 <= ord(c) <= 126 and c not in '[](){}<>/%' for c in ps)
    if valid:
        return ps, False
    fam = name.getDebugName(16) or name.getDebugName(1) or 'Font'
    sty = name.getDebugName(17) or name.getDebugName(2) or 'Regular'
    ps = (re.sub(r'[^A-Za-z0-9]', '', fam) or 'Font') + '-' + (re.sub(r'[^A-Za-z0-9]', '', sty) or 'Regular')
    ps = ps[:63]
    name.setName(ps, 6, 3, 1, 0x409)
    name.setName(ps, 6, 1, 0, 0)
    return ps, True


def check(path):
    """Короткая проверка результата: формат и наличие кириллицы."""
    from fontTools.ttLib import TTFont
    f = TTFont(path)
    cmap = f.getBestCmap()
    probe = {'А': 0x410, 'я': 0x44F, 'ё': 0x451, '«': 0xAB, '—': 0x2014, '₽': 0x20BD}
    missing = [c for c, cp in probe.items() if cp not in cmap]
    print(f'     глифов в cmap: {len(cmap)}; '
          + ('всё на месте' if not missing else 'нет: ' + ' '.join(missing)))


if __name__ == '__main__':
    files = sys.argv[1:]
    if not files:
        print(__doc__)
        sys.exit(1)

    print('Конвертация:')
    made = []
    for src in files:
        if not os.path.isfile(src):
            print(f'  {src}: файла нет')
            continue
        try:
            out = convert(src)
            if out:
                check(out)
                made.append(out)
        except ImportError as e:
            print(f'\nНе хватает библиотеки: {e}\nУстановите: pip3 install fonttools cu2qu')
            sys.exit(1)
        except Exception as e:
            print(f'  {src}: ошибка — {e}')

    if made:
        names = ' '.join(os.path.basename(m) for m in made)
        print(f"""
Готово. Дальше переустановите шрифт в dompdf:

  1. Удалите старые записи из папки шрифтов dompdf:
       alethiapro-regular.otf  alethiapro-regular.ufm
       alethiapro-italic.otf   alethiapro-italic.ufm
     и строки про alethiapro из installed-fonts.json
     (или dompdf_font_family_cache.php — смотря что там лежит).

  2. Поставьте заново уже .ttf:
       php dompdf/vendor/dompdf/dompdf/load_font.php alethiapro {names}

  3. Проверьте: php font_check.php alethiapro
     Должно быть «формат: TrueType — подходит» и «ИТОГ: со шрифтом всё в порядке».
     В pdffonts font_check.pdf в колонке emb должно стоять yes.
""")