The investor business plan (docx + pdf) rebuilt for the launch plant decided in October 2026: one Duplex system and one Classic, Pheenix galvanics with zero discharge, the Sibert line, a Sillitoe cutting room, modular steam and cooling (rooms for four), cassette and CD-R; no bridge stampers, no outsourced cutting; second Duplex as a growth option. - Part A: executive summary (what changed since 0.1, the numbers, the 180M / 216M answer, first release marked open), reading guide, two price points (ResurRec and Record Industry), anatomy rewritten for Sillitoe, Pheenix, Sibert, Duplex + Classic, lead times without bridge stampers, new 4.8 artist platform, cassette 4.9, CD 4.10. - Part B: traceability with ERPNext as system of record, metrology launch tier as the investor's choice, QC, efficiency (PEA tariff, utility modules, fuel open, Chinese boilers and DIW rules), site (Mueang Chi, Payap zoning result), building blocks, safety, BOI and licences with galvanics in the first factory licence. - Part C: launch-plant investment table, the investor's question and levers, tranches, currency, operating picture of the launch plant, timeline with the metal path on the critical path, commissioning sequence and why the whole chain at launch, team, 22 risks, 17 phase 0 actions, the ask. - plan_lib: grid column widths so LibreOffice keeps table widths. - export_pdf.py: LibreOffice UNO export that fills the table of contents (47 pages). - README: total investment, revenue model and business plan bullets.
48 lines
1.8 KiB
Python
48 lines
1.8 KiB
Python
|
|
"""Export CNX-Vinyl-Business-Plan.docx to PDF with LibreOffice, filling in the table of contents.
|
|
|
|
Run with LibreOffice's own Python (it ships the uno module):
|
|
"C:/Program Files/LibreOffice/program/python.exe" export_pdf.py
|
|
A plain `soffice --convert-to pdf` leaves the Word TOC field unfilled.
|
|
"""
|
|
import uno, sys, time, subprocess, os
|
|
from pathlib import Path
|
|
HERE = Path(__file__).resolve().parent
|
|
DOCX = HERE.parent / "CNX-Vinyl-Business-Plan.docx"
|
|
PDF = HERE.parent / "CNX-Vinyl-Business-Plan.pdf"
|
|
from com.sun.star.beans import PropertyValue
|
|
def pv(n, v):
|
|
p = PropertyValue(); p.Name = n; p.Value = v; return p
|
|
soffice = r"C:\Program Files\LibreOffice\program\soffice.exe"
|
|
proc = subprocess.Popen([soffice, "--headless", "--invisible", "--norestore", "--accept=socket,host=localhost,port=2083;urp;"])
|
|
local = uno.getComponentContext()
|
|
resolver = local.ServiceManager.createInstanceWithContext("com.sun.star.bridge.UnoUrlResolver", local)
|
|
for i in range(60):
|
|
try:
|
|
ctx = resolver.resolve("uno:socket,host=localhost,port=2083;urp;StarOffice.ComponentContext"); break
|
|
except Exception:
|
|
time.sleep(0.5)
|
|
smgr = ctx.ServiceManager
|
|
desktop = smgr.createInstanceWithContext("com.sun.star.frame.Desktop", ctx)
|
|
src = uno.systemPathToFileUrl(str(DOCX))
|
|
doc = desktop.loadComponentFromURL(src, "_blank", 0, (pv("Hidden", True),))
|
|
idx = doc.getDocumentIndexes()
|
|
print("indexes", idx.getCount())
|
|
for i in range(idx.getCount()):
|
|
idx.getByIndex(i).update()
|
|
doc.refresh()
|
|
# update fields (page numbers)
|
|
doc.getTextFields().refresh()
|
|
for i in range(idx.getCount()):
|
|
idx.getByIndex(i).update()
|
|
out = uno.systemPathToFileUrl(str(PDF))
|
|
doc.storeToURL(out, (pv("FilterName", "writer_pdf_Export"),))
|
|
doc.close(True)
|
|
try:
|
|
desktop.terminate()
|
|
except Exception:
|
|
pass
|
|
time.sleep(1)
|
|
proc.kill() if proc.poll() is None else None
|
|
print("done")
|