quizcomp.parser.style
Style constants and base functionality.
1""" 2Style constants and base functionality. 3""" 4 5import typing 6 7KEY_CONTENT_ALIGN: str = 'content-align' 8KEY_FONT_SIZE: str = 'font-size' 9KEY_IMAGE_WIDTH: str = 'image-width' 10KEY_TABLE_BORDER_CELLS: str = 'table-border-cells' 11KEY_TABLE_BORDER_TABLE: str = 'table-border-table' 12KEY_TABLE_CELL_HEIGHT: str = 'table-cell-height' 13KEY_TABLE_CELL_WIDTH: str = 'table-cell-width' 14KEY_TABLE_HEAD_BOLD: str = 'table-head-bold' 15KEY_TABLE_HEAD_RULE: str = 'table-head-rule' 16KEY_TEXT_ALIGN: str = 'text-align' 17 18DEFAULT_IMAGE_WIDTH: float = 1.0 19DEFAULT_TABLE_BORDER_CELLS: bool = False 20DEFAULT_TABLE_BORDER_TABLE: bool = False 21DEFAULT_TABLE_CELL_HEIGHT: float = 1.5 22DEFAULT_TABLE_CELL_WIDTH: float = 1.5 23DEFAULT_TABLE_HEAD_BOLD: bool = True 24DEFAULT_TABLE_HEAD_RULE: bool = True 25 26ALLOWED_VALUES_ALIGNMENT_LEFT: str = 'left' 27ALLOWED_VALUES_ALIGNMENT_CENTER: str = 'center' 28ALLOWED_VALUES_ALIGNMENT_RIGHT: str = 'right' 29ALLOWED_VALUES_ALIGNMENT: typing.List[str] = [ 30 ALLOWED_VALUES_ALIGNMENT_LEFT, 31 ALLOWED_VALUES_ALIGNMENT_CENTER, 32 ALLOWED_VALUES_ALIGNMENT_RIGHT 33] 34 35FLEXBOX_ALIGNMENT: typing.Dict[str, str] = { 36 ALLOWED_VALUES_ALIGNMENT_LEFT: 'flex-start', 37 ALLOWED_VALUES_ALIGNMENT_CENTER: 'center', 38 ALLOWED_VALUES_ALIGNMENT_RIGHT: 'flex-end', 39} 40 41TEX_BLOCK_ALIGNMENT: typing.Dict[str, str] = { 42 ALLOWED_VALUES_ALIGNMENT_LEFT: 'flushleft', 43 ALLOWED_VALUES_ALIGNMENT_CENTER: 'center', 44 ALLOWED_VALUES_ALIGNMENT_RIGHT: 'flushright', 45} 46 47def get_alignment(style: typing.Dict[str, typing.Any], key: str, default_value: typing.Union[str, None] = None) -> typing.Union[str, None]: 48 """ Get the specified alignment, or None if there is no specified alignment (or default). """ 49 50 alignment = style.get(key, None) 51 if (alignment is None): 52 return default_value 53 54 alignment = str(alignment).lower() 55 if (alignment not in ALLOWED_VALUES_ALIGNMENT): 56 raise ValueError(f"Unknown value for '{key}' style key '{alignment}'. Allowed values: {ALLOWED_VALUES_ALIGNMENT}.") 57 58 return alignment 59 60def get_boolean_style_key(style: typing.Dict[str, typing.Any], key: str, default_value: bool = False) -> bool: 61 """ Get a boolean style key. """ 62 63 value = style.get(key, default_value) 64 if (value is None): 65 return default_value 66 67 return (value is True) 68 69def get_image_width(style: typing.Dict[str, typing.Any]) -> float: 70 """ Get the image width. """ 71 72 width = style.get(KEY_IMAGE_WIDTH, None) 73 if (width is None): 74 width = DEFAULT_IMAGE_WIDTH 75 76 return float(width) 77 78def compute_html_style_string(style: typing.Dict[str, typing.Any]) -> str: 79 """ 80 Compute the attribute style string for an HTML tag. 81 """ 82 83 attributes = [] 84 85 content_align = get_alignment(style, KEY_CONTENT_ALIGN) 86 if (content_align is not None): 87 attributes.append("display: flex") 88 attributes.append("flex-direction: column") 89 attributes.append("justify-content: flex-start") 90 attributes.append(f"align-items: {FLEXBOX_ALIGNMENT[content_align]}") 91 92 text_align = get_alignment(style, KEY_TEXT_ALIGN) 93 if (text_align is not None): 94 attributes.append(f"text-align: {text_align}") 95 96 font_size = style.get(KEY_FONT_SIZE, None) 97 if (font_size is not None): 98 attributes.append(f"font-size: {float(font_size):0.2f}pt") 99 100 if (len(attributes) == 0): 101 return '' 102 103 return '; '.join(attributes) 104 105def compute_tex_fixes(style: typing.Dict[str, typing.Any]) -> typing.Tuple[typing.List[str], typing.List[str]]: 106 """ 107 Compute the fixes (prefixes, suffixes) for a portion of TeX. 108 These are things like `\\begin{center}`/`\\end{center}`. 109 The returned lists have matching indexes. 110 """ 111 112 # The beginning and ends of groups. 113 # These will match 1-1. 114 prefixes = [] 115 suffixes = [] 116 117 content_align = get_alignment(style, KEY_CONTENT_ALIGN) 118 if (content_align is not None): 119 env_name = TEX_BLOCK_ALIGNMENT[content_align] 120 prefixes.append("\\begin{%s}" % (env_name)) 121 suffixes.append("\\end{%s}" % (env_name)) 122 123 font_size = style.get(KEY_FONT_SIZE, None) 124 if (font_size is not None): 125 font_size = float(font_size) 126 # 1.2 is the default size for baseline skip relative to font size. 127 # See: https://ctan.math.illinois.edu/macros/latex/contrib/fontsize/fontsize.pdf 128 baseline_skip = 1.2 * font_size 129 130 prefixes.append('\\begingroup\\fontsize{%.2fpt}{%.2fpt}\\selectfont' % (font_size, baseline_skip)) 131 suffixes.append('\\endgroup') 132 133 return prefixes, suffixes
KEY_CONTENT_ALIGN: str =
'content-align'
KEY_FONT_SIZE: str =
'font-size'
KEY_IMAGE_WIDTH: str =
'image-width'
KEY_TABLE_BORDER_CELLS: str =
'table-border-cells'
KEY_TABLE_BORDER_TABLE: str =
'table-border-table'
KEY_TABLE_CELL_HEIGHT: str =
'table-cell-height'
KEY_TABLE_CELL_WIDTH: str =
'table-cell-width'
KEY_TABLE_HEAD_BOLD: str =
'table-head-bold'
KEY_TABLE_HEAD_RULE: str =
'table-head-rule'
KEY_TEXT_ALIGN: str =
'text-align'
DEFAULT_IMAGE_WIDTH: float =
1.0
DEFAULT_TABLE_BORDER_CELLS: bool =
False
DEFAULT_TABLE_BORDER_TABLE: bool =
False
DEFAULT_TABLE_CELL_HEIGHT: float =
1.5
DEFAULT_TABLE_CELL_WIDTH: float =
1.5
DEFAULT_TABLE_HEAD_BOLD: bool =
True
DEFAULT_TABLE_HEAD_RULE: bool =
True
ALLOWED_VALUES_ALIGNMENT_LEFT: str =
'left'
ALLOWED_VALUES_ALIGNMENT_CENTER: str =
'center'
ALLOWED_VALUES_ALIGNMENT_RIGHT: str =
'right'
ALLOWED_VALUES_ALIGNMENT: List[str] =
['left', 'center', 'right']
FLEXBOX_ALIGNMENT: Dict[str, str] =
{'left': 'flex-start', 'center': 'center', 'right': 'flex-end'}
TEX_BLOCK_ALIGNMENT: Dict[str, str] =
{'left': 'flushleft', 'center': 'center', 'right': 'flushright'}
def
get_alignment( style: Dict[str, Any], key: str, default_value: Optional[str] = None) -> Optional[str]:
48def get_alignment(style: typing.Dict[str, typing.Any], key: str, default_value: typing.Union[str, None] = None) -> typing.Union[str, None]: 49 """ Get the specified alignment, or None if there is no specified alignment (or default). """ 50 51 alignment = style.get(key, None) 52 if (alignment is None): 53 return default_value 54 55 alignment = str(alignment).lower() 56 if (alignment not in ALLOWED_VALUES_ALIGNMENT): 57 raise ValueError(f"Unknown value for '{key}' style key '{alignment}'. Allowed values: {ALLOWED_VALUES_ALIGNMENT}.") 58 59 return alignment
Get the specified alignment, or None if there is no specified alignment (or default).
def
get_boolean_style_key(style: Dict[str, Any], key: str, default_value: bool = False) -> bool:
61def get_boolean_style_key(style: typing.Dict[str, typing.Any], key: str, default_value: bool = False) -> bool: 62 """ Get a boolean style key. """ 63 64 value = style.get(key, default_value) 65 if (value is None): 66 return default_value 67 68 return (value is True)
Get a boolean style key.
def
get_image_width(style: Dict[str, Any]) -> float:
70def get_image_width(style: typing.Dict[str, typing.Any]) -> float: 71 """ Get the image width. """ 72 73 width = style.get(KEY_IMAGE_WIDTH, None) 74 if (width is None): 75 width = DEFAULT_IMAGE_WIDTH 76 77 return float(width)
Get the image width.
def
compute_html_style_string(style: Dict[str, Any]) -> str:
79def compute_html_style_string(style: typing.Dict[str, typing.Any]) -> str: 80 """ 81 Compute the attribute style string for an HTML tag. 82 """ 83 84 attributes = [] 85 86 content_align = get_alignment(style, KEY_CONTENT_ALIGN) 87 if (content_align is not None): 88 attributes.append("display: flex") 89 attributes.append("flex-direction: column") 90 attributes.append("justify-content: flex-start") 91 attributes.append(f"align-items: {FLEXBOX_ALIGNMENT[content_align]}") 92 93 text_align = get_alignment(style, KEY_TEXT_ALIGN) 94 if (text_align is not None): 95 attributes.append(f"text-align: {text_align}") 96 97 font_size = style.get(KEY_FONT_SIZE, None) 98 if (font_size is not None): 99 attributes.append(f"font-size: {float(font_size):0.2f}pt") 100 101 if (len(attributes) == 0): 102 return '' 103 104 return '; '.join(attributes)
Compute the attribute style string for an HTML tag.
def
compute_tex_fixes(style: Dict[str, Any]) -> Tuple[List[str], List[str]]:
106def compute_tex_fixes(style: typing.Dict[str, typing.Any]) -> typing.Tuple[typing.List[str], typing.List[str]]: 107 """ 108 Compute the fixes (prefixes, suffixes) for a portion of TeX. 109 These are things like `\\begin{center}`/`\\end{center}`. 110 The returned lists have matching indexes. 111 """ 112 113 # The beginning and ends of groups. 114 # These will match 1-1. 115 prefixes = [] 116 suffixes = [] 117 118 content_align = get_alignment(style, KEY_CONTENT_ALIGN) 119 if (content_align is not None): 120 env_name = TEX_BLOCK_ALIGNMENT[content_align] 121 prefixes.append("\\begin{%s}" % (env_name)) 122 suffixes.append("\\end{%s}" % (env_name)) 123 124 font_size = style.get(KEY_FONT_SIZE, None) 125 if (font_size is not None): 126 font_size = float(font_size) 127 # 1.2 is the default size for baseline skip relative to font size. 128 # See: https://ctan.math.illinois.edu/macros/latex/contrib/fontsize/fontsize.pdf 129 baseline_skip = 1.2 * font_size 130 131 prefixes.append('\\begingroup\\fontsize{%.2fpt}{%.2fpt}\\selectfont' % (font_size, baseline_skip)) 132 suffixes.append('\\endgroup') 133 134 return prefixes, suffixes
Compute the fixes (prefixes, suffixes) for a portion of TeX.
These are things like \begin{center}/\end{center}.
The returned lists have matching indexes.