Files
cnx_vinyl/docs/business-plan/build/export_pdf.py
T
grabowski d42e99389b Business plan draft 0.2: re-based on the launch plant
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.
2026-10-16 16:01:25 +07:00

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")