Files
pdf/scripts/update_timeline.py
2026-06-22 15:18:47 +05:30

219 lines
14 KiB
Python

import openpyxl
import copy
import shutil
import os
def update_timeline():
excel_path = "PDF Editor Timeline.xlsx"
backup_path = "PDF Editor Timeline.xlsx.bak"
if os.path.exists(excel_path):
shutil.copyfile(excel_path, backup_path)
print(f"Created backup at {backup_path}")
else:
print(f"Error: {excel_path} not found.")
return
wb = openpyxl.load_workbook(excel_path)
tasks_data = {
"FreeType + HarfBuzz wrappers + LRU glyph cache": [
("FreeType Integration", "1 Day", "22-05-26", "22-05-26", "FreeType library initialized; FT_Face successfully loaded from font buffer", "", "Base integration for the entire typography engine.", "Wk 2", "Wk 2", "Critical"),
("HarfBuzz Integration", "1 Day", "22-05-26", "22-05-26", "HarfBuzz library linked; hb_font and hb_buffer created from FT_Face", "", "Required for shaping Unicode and layout engine.", "Wk 2", "Wk 2", "Critical"),
("FontFace Architecture", "1-2 Days", "22-05-26", "22-05-26", "RAII wrapper for FT_Face and hb_font_t; clean memory management", "", "Encapsulates font face lifecycle in C++.", "Wk 2", "Wk 2", "High"),
("FontLoader System", "1 Day", "22-05-26", "22-05-26", "Loads raw font binary data (TTF/OTF) from memory buffer into FontFace", "", "Supports loading from system or custom paths.", "Wk 2", "Wk 2", "High"),
("FontResolver System", "1-2 Days", "22-05-26", "22-05-26", "Resolves font requests (family, weight, italic) to best matching local font", "", "Determines which font to load dynamically.", "Wk 2", "Wk 2", "High"),
("Font Metrics Engine", "1 Day", "22-05-26", "23-05-26", "Calculates ascender, descender, line gap, and scale factors", "", "Ensures vertical text alignment is correct.", "Wk 2", "Wk 3", "High"),
("Advance Width Calculation", "1 Day", "22-05-26", "23-05-26", "Retrieves horizontal advances for individual glyphs via FreeType / HarfBuzz", "", "Needed to correctly advance cursor during text rendering.", "Wk 2", "Wk 3", "High"),
("Glyph Lookup System", "1 Day", "22-05-26", "23-05-26", "Maps unicode character points to glyph indices accurately", "", "Core of character representation.", "Wk 2", "Wk 3", "High"),
("Missing Glyph Detection", "1 Day", "23-05-26", "23-05-26", "Identifies when a font lacks a glyph for a codepoint; triggers fallback", "", "Prevents rendering blank spaces / boxes (tofu).", "Wk 3", "Wk 3", "Med"),
("LRU Glyph Cache", "1-2 Days", "22-05-26", "23-05-26", "Caches rendered glyph bitmaps; cache hits >90% on text corpus", "", "Crucial for text rendering performance.", "Wk 2", "Wk 3", "Critical"),
("Runtime Font Cache", "1 Day", "22-05-26", "23-05-26", "Stores loaded FontFace instances globally to prevent reloading same font resource", "", "Speeds up multi-page renders.", "Wk 2", "Wk 3", "High"),
("Thread-Safe Font Engine", "1 Day", "22-05-26", "23-05-26", "All font engine components are thread-safe; concurrent access protected by mutexes", "", "Supports background parsing and parallel rasterization.", "Wk 2", "Wk 3", "High"),
("Memory Management & RAII", "1 Day", "22-05-26", "22-05-26", "Zero memory leaks under ASan when loading and unloading fonts", "", "Strict enforcement of ownership semantics.", "Wk 2", "Wk 2", "Critical"),
("HarfBuzz Ligature & Kerning Support", "1 Day", "22-05-26", "23-05-26", "Applies advanced open type features (liga, kern) correctly during text shaping", "", "Ensures professional typesetting look.", "Wk 2", "Wk 3", "High"),
("Unicode Text Shaping Pipeline", "2 Days", "22-05-26", "23-05-26", "Shapes incoming UTF-8 string into shaped glyph run with correct advance and offsets", "", "Translates abstract string to concrete spatial coordinates.", "Wk 2", "Wk 3", "Critical")
],
"PDF font resource loader (Type1/TrueType/CID)": [
("PDF Font Resource Loader", "2 Days", "23-05-26", "25-05-26", "Loads font resource streams directly from PDF dictionaries", "", "Essential for rendering documents with embedded fonts.", "Wk 3", "Wk 3", "Critical"),
("TrueType Font Support", "1 Day", "23-05-26", "24-05-26", "Parses and renders standard TrueType (.ttf) fonts", "", "Handles glyph outline rendering for TrueType format.", "Wk 3", "Wk 3", "High"),
("OpenType Font Support", "1 Day", "24-05-26", "24-05-26", "Parses and renders standard OpenType (.otf) fonts", "", "Handles CFF outlines and advanced OpenType tables.", "Wk 3", "Wk 3", "High"),
("Type1 Font Support", "1 Day", "24-05-26", "25-05-26", "Parses and renders legacy Type 1 postscript fonts", "", "Needed for legacy PDF documents.", "Wk 3", "Wk 3", "High"),
("CIDFont Support", "2 Days", "25-05-26", "26-05-26", "Correctly maps character IDs to glyph indices for CIDFonts", "", "Crucial for large character sets (CJK).", "Wk 3", "Wk 4", "Critical"),
("Embedded Font Loading", "1 Day", "23-05-26", "24-05-26", "Extracts and loads embedded font file streams (FontFile, FontFile2, FontFile3)", "", "Prevents font missing errors.", "Wk 3", "Wk 3", "Critical"),
("Subset Font Loading", "1-2 Days", "24-05-26", "26-05-26", "Successfully loads fonts containing only a subset of glyphs", "", "Common optimization in PDFs; requires robust glyph-index mapping.", "Wk 3", "Wk 4", "High"),
("Font Metadata Extraction", "1 Day", "23-05-26", "24-05-26", "Parses font names, flags, subtypes, and panose data", "", "Useful for debugging and font substitution matching.", "Wk 3", "Wk 3", "Med"),
("CJK (Chinese/Japanese/Korean) Support", "2 Days", "24-05-26", "26-05-26", "Successfully renders double-byte character sets and vertical writing parameters", "", "Crucial for international document rendering.", "Wk 3", "Wk 4", "High"),
("PDFium ↔ Font Engine Integration", "2 Days", "24-05-26", "26-05-26", "Synchronizes PDFium's font extraction with our FreeType/HarfBuzz pipeline", "", "Binds the layout interpreter to our rendering path.", "Wk 3", "Wk 4", "Critical"),
("Lazy Font Loading", "1 Day", "24-05-26", "26-05-26", "Loads font resource streams only when a glyph from that font needs to be rendered", "", "Optimizes document open times and memory consumption.", "Wk 3", "Wk 4", "Med")
],
"Font substitution system (Liberation fonts)": [
("Font Fallback System", "1-2 Days", "25-05-26", "27-05-26", "Dynamically selects a fallback font when primary font lacks glyph", "", "Ensures all characters render.", "Wk 4", "Wk 4", "High"),
("Font Substitution System (Liberation Fonts)", "2 Days", "25-05-26", "01-06-26", "Substitutes missing system fonts (e.g. Arial) with metric-compatible Liberation fonts", "", "Keeps layout intact.", "Wk 4", "Wk 4", "High")
]
}
def copy_cell_style(src_cell, dest_cell):
if src_cell.has_style:
dest_cell.font = copy.copy(src_cell.font)
dest_cell.fill = copy.copy(src_cell.fill)
dest_cell.border = copy.copy(src_cell.border)
dest_cell.alignment = copy.copy(src_cell.alignment)
dest_cell.number_format = copy.copy(src_cell.number_format)
dest_cell.protection = copy.copy(src_cell.protection)
ws = wb['📋 Master Timeline']
headers = [ws.cell(row=6, column=col).value for col in range(1, ws.max_column + 1)]
has_dates = "Start Date" in headers
if not has_dates:
print("Inserting Start Date and End Date columns in Master Timeline...")
ws.insert_cols(11, 2)
ws.cell(row=6, column=11).value = "Start Date"
ws.cell(row=6, column=12).value = "End Date"
headers = [ws.cell(row=6, column=col).value for col in range(1, ws.max_column + 1)]
copy_cell_style(ws.cell(row=6, column=10), ws.cell(row=6, column=11))
copy_cell_style(ws.cell(row=6, column=10), ws.cell(row=6, column=12))
parent_rows = {}
for r in range(7, ws.max_row + 1):
task_name = ws.cell(row=r, column=3).value
if task_name in tasks_data:
parent_rows[task_name] = r
sorted_parents = sorted(parent_rows.items(), key=lambda x: x[1], reverse=True)
total_inserted = 0
for parent_name, row_idx in sorted_parents:
subtasks = tasks_data[parent_name]
num_sub = len(subtasks)
print(f"Found parent '{parent_name}' at row {row_idx}. Inserting {num_sub} subtasks below it.")
ws.insert_rows(row_idx + 1, num_sub)
for i, sub in enumerate(subtasks):
curr_row = row_idx + 1 + i
task_t, dur, sd, ed, sc, dep, notes, wk_s, wk_e, pri = sub
ws.cell(row=curr_row, column=1).value = "P1"
ws.cell(row=curr_row, column=2).value = "Phase 1"
ws.cell(row=curr_row, column=3).value = f" - {task_t}"
ws.cell(row=curr_row, column=4).value = "Dev 3"
ws.cell(row=curr_row, column=5).value = wk_s
ws.cell(row=curr_row, column=6).value = wk_e
ws.cell(row=curr_row, column=7).value = dur
ws.cell(row=curr_row, column=8).value = "Done"
ws.cell(row=curr_row, column=9).value = pri
ws.cell(row=curr_row, column=10).value = 1.0
ws.cell(row=curr_row, column=11).value = sd
ws.cell(row=curr_row, column=12).value = ed
ws.cell(row=curr_row, column=13).value = sc
ws.cell(row=curr_row, column=14).value = dep if dep else None
ws.cell(row=curr_row, column=15).value = notes
for col in range(1, 16):
copy_cell_style(ws.cell(row=row_idx, column=col), ws.cell(row=curr_row, column=col))
total_inserted += num_sub
summary_row_idx = None
for r in range(7, ws.max_row + 1):
val = ws.cell(row=r, column=1).value
if val and isinstance(val, str) and "=COUNTIF" in val:
summary_row_idx = r
break
if summary_row_idx:
print(f"Summary row found at {summary_row_idx}. Updating formulas to include new rows.")
old_end_row = 69
new_end_row = 69 + total_inserted
f_a = ws.cell(row=summary_row_idx, column=1).value
ws.cell(row=summary_row_idx, column=1).value = f_a.replace(f"D{old_end_row}", f"D{new_end_row}")
f_d = ws.cell(row=summary_row_idx, column=4).value
ws.cell(row=summary_row_idx, column=4).value = f_d.replace(f"I{old_end_row}", f"I{new_end_row}")
f_h = ws.cell(row=summary_row_idx, column=8).value
ws.cell(row=summary_row_idx, column=8).value = f_h.replace(f"H{old_end_row}", f"H{new_end_row}")
f_k = ws.cell(row=summary_row_idx, column=13).value
if f_k and "J7:J69" in f_k:
ws.cell(row=summary_row_idx, column=13).value = f_k.replace(f"J{old_end_row}", f"J{new_end_row}")
print("Master Timeline formulas updated successfully.")
if '🔍 Phase Detail' in wb.sheetnames:
ws_det = wb['🔍 Phase Detail']
parent_rows_det = {}
for r in range(7, ws_det.max_row + 1):
task_name = ws_det.cell(row=r, column=3).value
if task_name in tasks_data:
parent_rows_det[task_name] = r
sorted_parents_det = sorted(parent_rows_det.items(), key=lambda x: x[1], reverse=True)
for parent_name, row_idx in sorted_parents_det:
subtasks = tasks_data[parent_name]
num_sub = len(subtasks)
ws_det.insert_rows(row_idx + 1, num_sub)
for i, sub in enumerate(subtasks):
curr_row = row_idx + 1 + i
task_t, dur, sd, ed, sc, dep, notes, wk_s, wk_e, pri = sub
ws_det.cell(row=curr_row, column=1).value = "P1"
ws_det.cell(row=curr_row, column=2).value = "Phase 1"
ws_det.cell(row=curr_row, column=3).value = f" - {task_t}"
ws_det.cell(row=curr_row, column=4).value = "Dev 3"
ws_det.cell(row=curr_row, column=5).value = wk_s
ws_det.cell(row=curr_row, column=6).value = wk_e
ws_det.cell(row=curr_row, column=7).value = dur
ws_det.cell(row=curr_row, column=8).value = "Done"
ws_det.cell(row=curr_row, column=9).value = pri
ws_det.cell(row=curr_row, column=10).value = 1.0
ws_det.cell(row=curr_row, column=11).value = sc
for col in range(1, 12):
copy_cell_style(ws_det.cell(row=row_idx, column=col), ws_det.cell(row=curr_row, column=col))
print("Phase Detail sheet updated successfully.")
if '📅 Gantt' in wb.sheetnames:
ws_gantt = wb['📅 Gantt']
parent_rows_g = {}
for r in range(5, ws_gantt.max_row + 1):
task_name = ws_gantt.cell(row=r, column=2).value
if task_name in tasks_data:
parent_rows_g[task_name] = r
sorted_parents_g = sorted(parent_rows_g.items(), key=lambda x: x[1], reverse=True)
for parent_name, row_idx in sorted_parents_g:
subtasks = tasks_data[parent_name]
num_sub = len(subtasks)
ws_gantt.insert_rows(row_idx + 1, num_sub)
for i, sub in enumerate(subtasks):
curr_row = row_idx + 1 + i
task_t, dur, sd, ed, sc, dep, notes, wk_s, wk_e, pri = sub
ws_gantt.cell(row=curr_row, column=1).value = "P1"
ws_gantt.cell(row=curr_row, column=2).value = f" - {task_t}"
ws_gantt.cell(row=curr_row, column=3).value = "Dev 3"
ws_gantt.cell(row=curr_row, column=4).value = dur
wk_s_num = int(wk_s.split()[1])
wk_e_num = int(wk_e.split()[1])
for wk in range(1, 21):
col_idx = 4 + wk
if wk_s_num <= wk <= wk_e_num:
ws_gantt.cell(row=curr_row, column=col_idx).value = "▶"
else:
ws_gantt.cell(row=curr_row, column=col_idx).value = None
for col in range(1, 25):
copy_cell_style(ws_gantt.cell(row=row_idx, column=col), ws_gantt.cell(row=curr_row, column=col))
print("Gantt sheet updated successfully.")
wb.save(excel_path)
print(f"Successfully saved updated timeline to {excel_path}!")
if __name__ == "__main__":
update_timeline()