Release PPT workflow 0.7.3 with fixed spacing, paragraphs and native math
This commit is contained in:
+16
-17
@@ -12,6 +12,7 @@ import unicodedata
|
||||
import xml.etree.ElementTree as ET
|
||||
|
||||
import template_ref as ref
|
||||
from component_typography import resolve_role
|
||||
|
||||
SVG = 'http://www.w3.org/2000/svg'
|
||||
|
||||
@@ -48,9 +49,9 @@ def _node(parent, tag, **attributes):
|
||||
return ET.SubElement(parent, '{'+SVG+'}'+tag, {key.replace('_', '-'):str(value) for key,value in attributes.items()})
|
||||
|
||||
|
||||
def _label(parent, value, x, y, font, size, color, align):
|
||||
def _label(parent, value, x, y, font, size, color, align, weight='normal'):
|
||||
result = _node(parent, 'text', x=x, y=y, font_family=font, font_size=size,
|
||||
font_weight='normal', fill=color, text_anchor='end' if align == 'r' else 'middle' if align == 'ctr' else 'start')
|
||||
font_weight=weight, fill=color, text_anchor='end' if align == 'r' else 'middle' if align == 'ctr' else 'start')
|
||||
result.text = value
|
||||
|
||||
|
||||
@@ -143,12 +144,10 @@ def render_grouped_table(raw: dict, style: dict, theme: dict, bounds: tuple,
|
||||
ref.require(all(row[status['column']] in status['values'] for row in rows), 'unknown status value')
|
||||
cfg = style.get('components',{}).get('table_grouped',{})
|
||||
_fields(cfg, {'group_height_px','header_height_px','row_height_px','cell_padding_px'}, set(), 'style.components.table_grouped')
|
||||
typography = style['typography']; roles = typography['roles']; families = typography['families']
|
||||
font = families[roles['label']['family_role']]['family']
|
||||
caption_font = families[roles['caption']['family_role']]['family']
|
||||
size = _number(roles['label']['size_pt'],'label size')*_number(canvas['pt_to_px'],'pt_to_px')
|
||||
small = _number(roles['caption']['size_pt'],'caption size')*canvas['pt_to_px']
|
||||
ref.require(size > 0 and small > 0, 'font sizes must be positive')
|
||||
label_role = resolve_role(style, style.get('components',{}).get('table',{}).get('text_role','label'))
|
||||
caption_role = resolve_role(style, 'caption')
|
||||
font, caption_font = label_role.family, caption_role.family
|
||||
size, small = label_role.size, caption_role.size
|
||||
padding = cfg.get('cell_padding_px',[8,16])
|
||||
ref.require(isinstance(padding,list) and len(padding) == 2 and all(_number(v,'cell padding') >= 0 and v % 8 == 0 for v in padding), 'cell_padding_px requires two nonnegative 8px multiples')
|
||||
top, side = padding
|
||||
@@ -179,11 +178,11 @@ def render_grouped_table(raw: dict, style: dict, theme: dict, bounds: tuple,
|
||||
start,span = group['start'],group['span']; gx=x+sum(widths[:start]); gw=sum(widths[start:start+span])
|
||||
fill = palette['accent1' if i%2 == 0 else 'accent2']
|
||||
_fit(group['label'],gw-2*side,small,'group header')
|
||||
cell = {'paragraphs':[{'align':'ctr','runs':[{'text':group['label'],'font_size':small,'font_family':caption_font,'bold':False,'color':palette['lt1']}]}],'fill':fill,'color':palette['lt1'],'font_size':small,'bold':False,'align':'ctr','valign':'middle','padding':{'top':0,'bottom':0,'left':side,'right':side},'borders':none()}
|
||||
cell = {'paragraphs':[{'align':'ctr','runs':[{'text':group['label'],'font_size':small,'font_family':caption_font,'bold':caption_role.bold,'color':palette['lt1']}]}],'fill':fill,'color':palette['lt1'],'font_size':small,'bold':caption_role.bold,'align':'ctr','valign':'middle','padding':{'top':0,'bottom':0,'left':side,'right':side},'borders':none()}
|
||||
if span > 1: cell['col_span']=span
|
||||
table_rows[start]=cell
|
||||
_node(fallback,'rect',x=gx,y=y,width=gw,height=gh,fill=fill)
|
||||
_label(fallback,group['label'],gx+gw/2,y+(gh+small*.72)/2,caption_font,small,palette['lt1'],'ctr')
|
||||
_label(fallback,group['label'],gx+gw/2,y+(gh+small*.72)/2,caption_font,small,palette['lt1'],'ctr',caption_role.svg_weight)
|
||||
table_rows = [table_rows]
|
||||
group_ends = {g['start']+g['span']-1 for g in groups[:-1]}
|
||||
cy = y+gh
|
||||
@@ -203,24 +202,24 @@ def render_grouped_table(raw: dict, style: dict, theme: dict, bounds: tuple,
|
||||
if focused:
|
||||
borders['top']={'style':'solid','color':palette['accent1'],'width':1}
|
||||
if ci == 0: borders['left']={'style':'solid','color':palette['accent1'],'width':4}
|
||||
cell={'fill':fill,'color':color,'align':align,'font_size':size,'bold':False,'padding':pad,'valign':'middle','borders':borders}
|
||||
cell={'fill':fill,'color':color,'align':align,'font_family':font,'font_size':size,'bold':label_role.bold,'padding':pad,'valign':'middle','borders':borders}
|
||||
_node(fallback,'rect',x=cx,y=cy,width=widths[ci],height=cell_height,fill=fill)
|
||||
tx=cx+widths[ci]-side if align == 'r' else cx+pad['left']
|
||||
if header:
|
||||
title,unit=value['label'],value.get('unit','')
|
||||
_fit(title,widths[ci]-2*side,size,'column header')
|
||||
cell['paragraphs']=[{'align':align,'runs':[{'text':title,'font_size':size,'font_family':font,'bold':False,'color':color}]}]
|
||||
cell['paragraphs']=[{'align':align,'runs':[{'text':title,'font_size':size,'font_family':font,'bold':label_role.bold,'color':color}]}]
|
||||
if unit:
|
||||
_fit(unit,widths[ci]-2*side,small,'column unit')
|
||||
cell['paragraphs'].append({'align':align,'runs':[{'text':unit,'font_size':small,'font_family':caption_font,'bold':False,'color':palette['dk2']}]})
|
||||
cell['paragraphs'].append({'align':align,'runs':[{'text':unit,'font_size':small,'font_family':caption_font,'bold':caption_role.bold,'color':palette['dk2']}]})
|
||||
baseline=cy+(cell_height-size-small-8)/2+size*.8
|
||||
_label(fallback,title,tx,baseline,font,size,color,align)
|
||||
_label(fallback,unit,tx,baseline+small+8,caption_font,small,palette['dk2'],align)
|
||||
else: _label(fallback,title,tx,cy+(cell_height+size*.72)/2,font,size,color,align)
|
||||
_label(fallback,title,tx,baseline,font,size,color,align,label_role.svg_weight)
|
||||
_label(fallback,unit,tx,baseline+small+8,caption_font,small,palette['dk2'],align,caption_role.svg_weight)
|
||||
else: _label(fallback,title,tx,cy+(cell_height+size*.72)/2,font,size,color,align,label_role.svg_weight)
|
||||
else:
|
||||
displayed=_formatted(value,columns[ci]); cell['text']=displayed
|
||||
_fit(displayed,widths[ci]-pad['left']-side,size,'table cell')
|
||||
_label(fallback,displayed,tx,cy+(cell_height+size*.72-(8 if ci in bar_columns else 0))/2,font,size,color,align)
|
||||
_label(fallback,displayed,tx,cy+(cell_height+size*.72-(8 if ci in bar_columns else 0))/2,font,size,color,align,label_role.svg_weight)
|
||||
if ci in bar_columns:
|
||||
maximum=max(r[ci] for r in rows) or 1; track=widths[ci]-2*side
|
||||
marks.append({'id':f'{component_id}-bar-r{ri}-c{ci}','kind':'bar','x':cx+side,'y':cy+cell_height-12,'width':track,'length':track*(value/maximum),'value':value,'domain':[0,maximum],'column':ci,'row':ri-1,'focused':focused})
|
||||
|
||||
Reference in New Issue
Block a user