"""Rebuild the Call for Papers PDF with a real text layer.

The two pages stay exactly what they are — full-page renders of an approved design — but the
PDF gains an invisible text layer placed over the visible words. The consequences are the point
of the exercise: the CFP becomes searchable, indexable by Google, readable by a screen reader,
and an author can finally select the deadline or the contact address and paste them somewhere.

Nothing here is transcribed from memory. Every string below is read off the rendered pages, and
`verify()` extracts the text back out of the finished PDF and checks the facts that matter.

    python3 fix_cfp_2026_08.py     # page 2 repairs → cfp_p2_fixed.png
    python3 build_cfp_pdf.py       # → META2027_Call_for_Papers.pdf
"""

import fitz
from PIL import Image

SCALE = 3                    # the renders are 3× a 595 × 841 pt page
QUALITY = 82                 # visually indistinguishable here, and a third of the weight
OUT = 'META2027_Call_for_Papers.pdf'
LAYER_FONT = 'Sora.ttf'       # Unicode-capable; only ever used invisibly

# ---------------------------------------------------------------------------- the text layer
# (text, x0, y0, x1, y1) in render pixels; y bands measured off the pages.
PAGE1 = [
    ("17TH INTERNATIONAL CONFERENCE ON METAMATERIALS, PHOTONIC CRYSTALS & PLASMONICS",
     90, 340, 1740, 415),
    ("META 2027", 90, 480, 1200, 612),
    ("Niagara Falls Convention Centre", 90, 665, 1200, 710),
    ("Niagara Falls, Ontario, Canada", 90, 730, 1200, 775),
    ("JULY 13–16, 2027", 90, 806, 1200, 848),
    ("CALL FOR PAPERS", 130, 1258, 700, 1332),
    ("META invites original contributions addressing fundamental advances, emerging concepts, "
     "devices and applications across metamaterials, photonics, plasmonics and structured "
     "light–matter interactions.", 130, 1376, 1300, 1584),
    ("TRACK 1  Metamaterials, Metasurfaces & Structured Waves", 130, 1632, 1500, 1670),
    ("TRACK 2  Plasmonics, Nano-Optics & Light–Matter Interactions", 130, 1710, 1500, 1752),
    ("TRACK 3  Photonic Crystals, Topological & Quantum Photonics", 130, 1788, 1500, 1832),
    ("TRACK 4  Emerging Platforms, Design, Fabrication & Applications", 130, 1865, 1500, 1908),
    ("Call opens  September 1, 2026", 130, 2024, 700, 2110),
    ("Submission deadline  February 12, 2027", 700, 2024, 1150, 2110),
    ("Contributed papers  2 – 4 pages", 1150, 2024, 1620, 2110),
    ("ISSN 2429-1390", 130, 2225, 600, 2265),
    ("metaconferences.org", 1330, 2270, 1740, 2310),
]

PAGE2 = [
    ("META 2027 — CALL FOR PAPERS", 130, 62, 1200, 105),
    ("Niagara Falls, Canada · July 13–16, 2027", 130, 108, 1200, 145),
    ("What to submit", 130, 243, 800, 282),
    ("A contribution of two to four pages, prepared with the official META 2027 template: title, "
     "authors and affiliations; a short abstract of 50 words at most; motivation and context; "
     "principal methods and results; figures and references. Submit as PDF. Authors choose the "
     "length that fits their work. The paper submitted for review is the paper published in the "
     "proceedings — there is no second, final-paper deadline. Invited contributions: one page "
     "minimum.", 130, 330, 1740, 565),
    ("Review criteria", 130, 593, 800, 633),
    ("Scientific quality — sound methods, analysis and conclusions", 130, 681, 1740, 722),
    ("Originality — a new result, concept, method or significant advance", 130, 730, 1740, 770),
    ("Significance — relevance to META’s scope and interest to the community",
     130, 779, 1740, 819),
    ("Clarity — motivation and results presented clearly for technical review",
     130, 828, 1740, 868),
    ("Key dates", 130, 908, 800, 947),
    ("Call for Papers opens  September 1, 2026", 130, 996, 1740, 1041),
    ("Submission deadline  February 12, 2027", 130, 1044, 1740, 1090),
    ("Acceptance notification  February 19, 2027", 130, 1093, 1740, 1138),
    ("Early registration until  April 23, 2027", 130, 1142, 1740, 1189),
    ("Advanced registration until  June 5, 2027", 130, 1191, 1740, 1238),
    ("Conference  July 13–16, 2027", 130, 1240, 1740, 1290),
    ("General Co-Chairs", 130, 1319, 800, 1360),
    ("Khaled Mnaymneh  National Research Council Canada", 130, 1407, 1740, 1450),
    ("Sofiane Haffouz  National Research Council Canada", 130, 1454, 1740, 1497),
    ("Said Zouhdi  Université Paris-Saclay, France", 130, 1501, 1740, 1545),
    ("Special sessions", 130, 1580, 800, 1625),
    ("Researchers are invited to propose Special Sessions on focused or rapidly developing areas "
     "within the scope of META 2027. Proposals should identify the scientific theme, the "
     "organizers and prospective contributors.", 130, 1663, 1740, 1804),
    ("Proceedings", 130, 1827, 800, 1874),
    ("Accepted contributions presented at META 2027 are published in the META Conference "
     "Proceedings (ISSN 2429-1390), indexed in Scopus. The paper submitted for review is the "
     "proceedings contribution: nothing further is uploaded after acceptance.",
     130, 1912, 1740, 2050),
    ("CALL FOR PAPERS   SUBMIT YOUR PAPER   PAPER TEMPLATE", 130, 2350, 1740, 2400),
    ("metaconferences.org · contact@metaconferences.org · Proceedings ISSN 2429-1390 "
     "(Scopus-indexed)", 130, 2440, 1740, 2490),
]

# facts the finished file must contain, checked by extracting its text back out
MUST_CONTAIN = [
    'September 1, 2026', 'February 12, 2027', 'February 19, 2027', 'April 23, 2027',
    'June 5, 2027', 'July 13–16, 2027', '50 words at most', '2 – 4 pages',
    'ISSN 2429-1390', 'contact@metaconferences.org', 'Niagara Falls',
]
MUST_NOT_CONTAIN = ['about 50 words', 'Autumn 2026', 'Dublin']


def add_page(doc, image_path, blocks):
    im = Image.open(image_path).convert('RGB')
    w, h = im.size[0] / SCALE, im.size[1] / SCALE
    page = doc.new_page(width=w, height=h)

    jpg = image_path.rsplit('.', 1)[0] + '_q.jpg'
    im.save(jpg, 'JPEG', quality=QUALITY, optimize=True, progressive=True)
    page.insert_image(fitz.Rect(0, 0, w, h), filename=jpg)

    # The layer is invisible, so the face is irrelevant to the eye — but not to the clipboard:
    # the Base-14 Helvetica turns every en dash into "?", which would corrupt "13 – 16 July 2027"
    # and "2 – 4 pages" for anyone who copies them. A real Unicode face fixes that.
    page.insert_font(fontname='cfp', fontfile=LAYER_FONT)

    for text, x0, y0, x1, y1 in blocks:
        rect = fitz.Rect(x0 / SCALE, y0 / SCALE, x1 / SCALE, y1 / SCALE)
        size = 11.0
        while size > 3.0:
            # render_mode 3 = draw nothing; the glyphs are there, only invisible
            if page.insert_textbox(rect, text, fontname='cfp', fontsize=size,
                                   render_mode=3, align=0) >= 0:
                break
            size -= 0.5
        else:
            raise SystemExit(f'texte trop long pour son cadre : {text[:60]}…')
    return page


def verify(path):
    doc = fitz.open(path)
    text = ' '.join(' '.join(p.get_text().split()) for p in doc)
    missing = [s for s in MUST_CONTAIN if s not in text]
    present = [s for s in MUST_NOT_CONTAIN if s in text]
    print(f'  {len(doc)} pages · {len(text)} caractères extractibles')
    print('  manquant :', missing or 'rien')
    print('  interdit présent :', present or 'rien')
    return not missing and not present


def main():
    doc = fitz.open()
    add_page(doc, 'cfp_p1.jpg', PAGE1)
    add_page(doc, 'cfp_p2_fixed.png', PAGE2)
    doc.set_metadata({
        'title': 'META 2027 — Call for Papers',
        'subject': ('17th International Conference on Metamaterials, Photonic Crystals and '
                    'Plasmonics. Niagara Falls, Ontario, Canada, July 13–16, 2027.'),
        'author': 'META Conferences',
        'keywords': ('metamaterials, photonic crystals, plasmonics, metasurfaces, nanophotonics, '
                     'quantum photonics, call for papers, META 2027, Niagara Falls'),
    })
    doc.subset_fonts()          # the layer font ships as a subset, not the whole face
    doc.save(OUT, garbage=4, deflate=True, clean=True)
    doc.close()
    import os
    print(f'{OUT} écrit — {os.path.getsize(OUT) / 1024:.0f} Ko')
    if not verify(OUT):
        raise SystemExit('vérification du texte échouée')
    print('  vérification OK')


if __name__ == '__main__':
    main()
