quizcomp.parser.renderer.html
1import os 2import re 3import typing 4 5import markdown_it.renderer 6import markdown_it.token 7import markdown_it.utils 8 9import quizcomp.model.constants 10import quizcomp.parser.common 11import quizcomp.parser.math 12import quizcomp.parser.renderer.base 13import quizcomp.parser.style 14import quizcomp.util.image 15 16HTML_BORDER_SPEC = '1px solid black' 17 18# pylint: disable=abstract-method 19class QuizComposerRendererHTML(markdown_it.renderer.RendererHTML, quizcomp.parser.renderer.base.QuizComposerRendererBase): 20 """ 21 A renderer for HTML. 22 This builds of of the existing markdown_it.renderer.RendererHTML renderer. 23 """ 24 25 __output__ = quizcomp.model.constants.Format.HTML.value 26 27 def image(self, # type: ignore[override] # pylint: disable=arguments-renamed 28 tokens: typing.List[markdown_it.token.Token], 29 token_index: int, 30 options: markdown_it.utils.OptionsDict, 31 env: typing.Dict[str, typing.Any], 32 force_raw_image_src: bool = False, 33 **kwargs: typing.Any) -> str: 34 """ Render an image. """ 35 36 # Do custom rendering and then pass onto super. 37 38 context = typing.cast(quizcomp.parser.common.RenderContext, env[quizcomp.parser.common.ENV_KEY_CONTEXT]) 39 40 # Set width. 41 width_float = quizcomp.parser.style.get_image_width(context.style) 42 tokens[token_index].attrSet('width', f"{(width_float * 100.0):0.2f}%") 43 44 original_src = str(tokens[token_index].attrGet('src')) 45 46 # Check the env to see if we need to force raw images. 47 force_raw_image_src = (force_raw_image_src or context.force_raw_image_src) 48 49 if (force_raw_image_src or re.match(r'^http(s)?://', original_src) or original_src.startswith('data:image')): 50 # Do not further modify the src if we are explicitly directed not to 51 # or if it is an http or data URL. 52 pass 53 else: 54 # Otherwise, do a base64 encoding of the image and embed it. 55 path = original_src 56 if (not os.path.isabs(path)): 57 path = os.path.join(context.base_dir, path) 58 59 path = os.path.abspath(path) 60 61 mime, content = quizcomp.util.image.b64_encode(path) 62 tokens[token_index].attrSet('src', f"data:{mime};base64,{content}") 63 64 result = super().image(tokens, token_index, options, env) 65 66 # Reset the src so that future callback hits have the proper cache key. 67 tokens[token_index].attrSet('src', original_src) 68 69 return result 70 71 def container_block_open(self, 72 tokens: typing.List[markdown_it.token.Token], 73 token_index: int, 74 options: markdown_it.utils.OptionsDict, 75 env: typing.Dict[str, typing.Any], 76 ) -> str: 77 """ Render the opening of a block. """ 78 79 context = typing.cast(quizcomp.parser.common.RenderContext, env[quizcomp.parser.common.ENV_KEY_CONTEXT]) 80 81 # Add on a specific class. 82 tokens[token_index].attrJoin('class', 'qg-block') 83 84 # Pull any style attached to this block and put it in a copy of the context. 85 context, block_style = quizcomp.parser.common.handle_block_style(tokens[token_index].meta, context) 86 env[quizcomp.parser.common.ENV_KEY_CONTEXT] = context 87 88 # Attach style based on if we are the root block. 89 # If root use all style, otherwise just use the style for this block. 90 active_style = block_style 91 if (tokens[token_index].meta.get(quizcomp.parser.common.TOKEN_META_KEY_ROOT, False)): 92 active_style = context.style 93 94 style_string = quizcomp.parser.style.compute_html_style_string(active_style) 95 if (style_string != ''): 96 tokens[token_index].attrSet('style', style_string) 97 98 # Send to super for further rendering. 99 return super().renderToken(tokens, token_index, options, env) 100 101 def math_inline(self, 102 tokens: typing.List[markdown_it.token.Token], 103 token_index: int, 104 options: markdown_it.utils.OptionsDict, 105 env: typing.Dict[str, typing.Any], 106 ) -> str: 107 """ Render inline math. """ 108 109 return quizcomp.parser.math.render(quizcomp.model.constants.Format.HTML, True, tokens, token_index, options, env) 110 111 def math_block(self, 112 tokens: typing.List[markdown_it.token.Token], 113 token_index: int, 114 options: markdown_it.utils.OptionsDict, 115 env: typing.Dict[str, typing.Any], 116 ) -> str: 117 """ Render a math block. """ 118 119 return quizcomp.parser.math.render(quizcomp.model.constants.Format.HTML, False, tokens, token_index, options, env) 120 121 def placeholder(self, 122 tokens: typing.List[markdown_it.token.Token], 123 token_index: int, 124 options: markdown_it.utils.OptionsDict, 125 env: typing.Dict[str, typing.Any], 126 ) -> str: 127 """ Render an answer placeholder. """ 128 129 return f"<placeholder>{tokens[token_index].content}</placeholder>" 130 131 def table_open(self, 132 tokens: typing.List[markdown_it.token.Token], 133 token_index: int, 134 options: markdown_it.utils.OptionsDict, 135 env: typing.Dict[str, typing.Any], 136 ) -> str: 137 """ Render the opening of a table. """ 138 139 token = tokens[token_index] 140 context = typing.cast(quizcomp.parser.common.RenderContext, env[quizcomp.parser.common.ENV_KEY_CONTEXT]) 141 142 table_style = [ 143 'border-collapse: collapse', 144 ] 145 146 has_border = quizcomp.parser.style.get_boolean_style_key( 147 context.style, 148 quizcomp.parser.style.KEY_TABLE_BORDER_TABLE, 149 quizcomp.parser.style.DEFAULT_TABLE_BORDER_TABLE, 150 ) 151 152 if (has_border): 153 table_style.append(f"border: {HTML_BORDER_SPEC}") 154 else: 155 table_style.append('border-style: hidden') 156 157 # HTML tables require extra encouragement to align. 158 text_align = quizcomp.parser.style.get_alignment(context.style, quizcomp.parser.style.KEY_TEXT_ALIGN) 159 if (text_align is not None): 160 table_style.append(f"text-align: {text_align}") 161 162 _join_html_style(token, table_style) 163 164 return super().renderToken(tokens, token_index, options, env) 165 166 def thead_open(self, 167 tokens: typing.List[markdown_it.token.Token], 168 token_index: int, 169 options: markdown_it.utils.OptionsDict, 170 env: typing.Dict[str, typing.Any], 171 ) -> str: 172 """ Render the opening of a table header. """ 173 174 token = tokens[token_index] 175 176 context = typing.cast(quizcomp.parser.common.RenderContext, env[quizcomp.parser.common.ENV_KEY_CONTEXT]) 177 178 has_head = quizcomp.parser.style.get_boolean_style_key( 179 context.style, 180 quizcomp.parser.style.KEY_TABLE_HEAD_RULE, 181 quizcomp.parser.style.DEFAULT_TABLE_HEAD_RULE, 182 ) 183 184 if (has_head): 185 _join_html_style(token, [f"border-bottom: {HTML_BORDER_SPEC}"]) 186 187 return super().renderToken(tokens, token_index, options, env) 188 189 def th_open(self, 190 tokens: typing.List[markdown_it.token.Token], 191 token_index: int, 192 options: markdown_it.utils.OptionsDict, 193 env: typing.Dict[str, typing.Any], 194 ) -> str: 195 """ Render the opening of a th. """ 196 197 token = tokens[token_index] 198 199 context = typing.cast(quizcomp.parser.common.RenderContext, env[quizcomp.parser.common.ENV_KEY_CONTEXT]) 200 201 self._cell_html(token, context.style) 202 203 weight = 'normal' 204 use_bold = quizcomp.parser.style.get_boolean_style_key( 205 context.style, 206 quizcomp.parser.style.KEY_TABLE_HEAD_BOLD, 207 quizcomp.parser.style.DEFAULT_TABLE_HEAD_BOLD, 208 ) 209 210 if (use_bold): 211 weight = 'bold' 212 213 _join_html_style(token, [f"font-weight: {weight}"]) 214 215 return super().renderToken(tokens, token_index, options, env) 216 217 def td_open(self, 218 tokens: typing.List[markdown_it.token.Token], 219 token_index: int, 220 options: markdown_it.utils.OptionsDict, 221 env: typing.Dict[str, typing.Any], 222 ) -> str: 223 """ Render the opening of a td. """ 224 225 token = tokens[token_index] 226 227 context = typing.cast(quizcomp.parser.common.RenderContext, env[quizcomp.parser.common.ENV_KEY_CONTEXT]) 228 229 self._cell_html(token, context.style) 230 231 return super().renderToken(tokens, token_index, options, env) 232 233 def _cell_html(self, token: markdown_it.token.Token, style: typing.Dict[str, typing.Any]) -> None: 234 """ 235 Common cell rendering. 236 """ 237 238 height = max(1.0, float(style.get(quizcomp.parser.style.KEY_TABLE_CELL_HEIGHT, quizcomp.parser.style.DEFAULT_TABLE_CELL_HEIGHT))) 239 vertical_padding = height - 1.0 240 241 width = max(1.0, float(style.get(quizcomp.parser.style.KEY_TABLE_CELL_WIDTH, quizcomp.parser.style.DEFAULT_TABLE_CELL_WIDTH))) 242 horizontal_padding = width - 1.0 243 244 cell_style = { 245 'padding-top': f"{(vertical_padding / 2):0.2f}em", 246 'padding-bottom': f"{(vertical_padding / 2):0.2f}em", 247 'padding-left': f"{(horizontal_padding / 2):0.2f}em", 248 'padding-right': f"{(horizontal_padding / 2):0.2f}em", 249 } 250 251 if (quizcomp.parser.style.get_boolean_style_key( 252 style, quizcomp.parser.style.KEY_TABLE_BORDER_CELLS, quizcomp.parser.style.DEFAULT_TABLE_BORDER_CELLS)): 253 cell_style['border'] = HTML_BORDER_SPEC 254 255 _join_html_style(token, [': '.join(item) for item in cell_style.items()]) 256 257def _join_html_style(token: markdown_it.token.Token, rules: typing.List[str]) -> None: 258 """ 259 Take all style rules to apply, add in any existing style, and set the style attribute. 260 """ 261 262 if (len(rules) == 0): 263 return 264 265 existing_style = token.attrGet('style') 266 if (existing_style is not None): 267 rules = [str(existing_style)] + rules 268 269 style_string = '; '.join(rules) 270 token.attrSet('style', style_string) 271 272def get_renderer(options: markdown_it.utils.OptionsDict) -> QuizComposerRendererHTML: 273 """ Get this renderer and options. """ 274 275 return QuizComposerRendererHTML()
HTML_BORDER_SPEC =
'1px solid black'
class
QuizComposerRendererHTML(markdown_it.renderer.RendererHTML, quizcomp.parser.renderer.base.QuizComposerRendererBase):
20class QuizComposerRendererHTML(markdown_it.renderer.RendererHTML, quizcomp.parser.renderer.base.QuizComposerRendererBase): 21 """ 22 A renderer for HTML. 23 This builds of of the existing markdown_it.renderer.RendererHTML renderer. 24 """ 25 26 __output__ = quizcomp.model.constants.Format.HTML.value 27 28 def image(self, # type: ignore[override] # pylint: disable=arguments-renamed 29 tokens: typing.List[markdown_it.token.Token], 30 token_index: int, 31 options: markdown_it.utils.OptionsDict, 32 env: typing.Dict[str, typing.Any], 33 force_raw_image_src: bool = False, 34 **kwargs: typing.Any) -> str: 35 """ Render an image. """ 36 37 # Do custom rendering and then pass onto super. 38 39 context = typing.cast(quizcomp.parser.common.RenderContext, env[quizcomp.parser.common.ENV_KEY_CONTEXT]) 40 41 # Set width. 42 width_float = quizcomp.parser.style.get_image_width(context.style) 43 tokens[token_index].attrSet('width', f"{(width_float * 100.0):0.2f}%") 44 45 original_src = str(tokens[token_index].attrGet('src')) 46 47 # Check the env to see if we need to force raw images. 48 force_raw_image_src = (force_raw_image_src or context.force_raw_image_src) 49 50 if (force_raw_image_src or re.match(r'^http(s)?://', original_src) or original_src.startswith('data:image')): 51 # Do not further modify the src if we are explicitly directed not to 52 # or if it is an http or data URL. 53 pass 54 else: 55 # Otherwise, do a base64 encoding of the image and embed it. 56 path = original_src 57 if (not os.path.isabs(path)): 58 path = os.path.join(context.base_dir, path) 59 60 path = os.path.abspath(path) 61 62 mime, content = quizcomp.util.image.b64_encode(path) 63 tokens[token_index].attrSet('src', f"data:{mime};base64,{content}") 64 65 result = super().image(tokens, token_index, options, env) 66 67 # Reset the src so that future callback hits have the proper cache key. 68 tokens[token_index].attrSet('src', original_src) 69 70 return result 71 72 def container_block_open(self, 73 tokens: typing.List[markdown_it.token.Token], 74 token_index: int, 75 options: markdown_it.utils.OptionsDict, 76 env: typing.Dict[str, typing.Any], 77 ) -> str: 78 """ Render the opening of a block. """ 79 80 context = typing.cast(quizcomp.parser.common.RenderContext, env[quizcomp.parser.common.ENV_KEY_CONTEXT]) 81 82 # Add on a specific class. 83 tokens[token_index].attrJoin('class', 'qg-block') 84 85 # Pull any style attached to this block and put it in a copy of the context. 86 context, block_style = quizcomp.parser.common.handle_block_style(tokens[token_index].meta, context) 87 env[quizcomp.parser.common.ENV_KEY_CONTEXT] = context 88 89 # Attach style based on if we are the root block. 90 # If root use all style, otherwise just use the style for this block. 91 active_style = block_style 92 if (tokens[token_index].meta.get(quizcomp.parser.common.TOKEN_META_KEY_ROOT, False)): 93 active_style = context.style 94 95 style_string = quizcomp.parser.style.compute_html_style_string(active_style) 96 if (style_string != ''): 97 tokens[token_index].attrSet('style', style_string) 98 99 # Send to super for further rendering. 100 return super().renderToken(tokens, token_index, options, env) 101 102 def math_inline(self, 103 tokens: typing.List[markdown_it.token.Token], 104 token_index: int, 105 options: markdown_it.utils.OptionsDict, 106 env: typing.Dict[str, typing.Any], 107 ) -> str: 108 """ Render inline math. """ 109 110 return quizcomp.parser.math.render(quizcomp.model.constants.Format.HTML, True, tokens, token_index, options, env) 111 112 def math_block(self, 113 tokens: typing.List[markdown_it.token.Token], 114 token_index: int, 115 options: markdown_it.utils.OptionsDict, 116 env: typing.Dict[str, typing.Any], 117 ) -> str: 118 """ Render a math block. """ 119 120 return quizcomp.parser.math.render(quizcomp.model.constants.Format.HTML, False, tokens, token_index, options, env) 121 122 def placeholder(self, 123 tokens: typing.List[markdown_it.token.Token], 124 token_index: int, 125 options: markdown_it.utils.OptionsDict, 126 env: typing.Dict[str, typing.Any], 127 ) -> str: 128 """ Render an answer placeholder. """ 129 130 return f"<placeholder>{tokens[token_index].content}</placeholder>" 131 132 def table_open(self, 133 tokens: typing.List[markdown_it.token.Token], 134 token_index: int, 135 options: markdown_it.utils.OptionsDict, 136 env: typing.Dict[str, typing.Any], 137 ) -> str: 138 """ Render the opening of a table. """ 139 140 token = tokens[token_index] 141 context = typing.cast(quizcomp.parser.common.RenderContext, env[quizcomp.parser.common.ENV_KEY_CONTEXT]) 142 143 table_style = [ 144 'border-collapse: collapse', 145 ] 146 147 has_border = quizcomp.parser.style.get_boolean_style_key( 148 context.style, 149 quizcomp.parser.style.KEY_TABLE_BORDER_TABLE, 150 quizcomp.parser.style.DEFAULT_TABLE_BORDER_TABLE, 151 ) 152 153 if (has_border): 154 table_style.append(f"border: {HTML_BORDER_SPEC}") 155 else: 156 table_style.append('border-style: hidden') 157 158 # HTML tables require extra encouragement to align. 159 text_align = quizcomp.parser.style.get_alignment(context.style, quizcomp.parser.style.KEY_TEXT_ALIGN) 160 if (text_align is not None): 161 table_style.append(f"text-align: {text_align}") 162 163 _join_html_style(token, table_style) 164 165 return super().renderToken(tokens, token_index, options, env) 166 167 def thead_open(self, 168 tokens: typing.List[markdown_it.token.Token], 169 token_index: int, 170 options: markdown_it.utils.OptionsDict, 171 env: typing.Dict[str, typing.Any], 172 ) -> str: 173 """ Render the opening of a table header. """ 174 175 token = tokens[token_index] 176 177 context = typing.cast(quizcomp.parser.common.RenderContext, env[quizcomp.parser.common.ENV_KEY_CONTEXT]) 178 179 has_head = quizcomp.parser.style.get_boolean_style_key( 180 context.style, 181 quizcomp.parser.style.KEY_TABLE_HEAD_RULE, 182 quizcomp.parser.style.DEFAULT_TABLE_HEAD_RULE, 183 ) 184 185 if (has_head): 186 _join_html_style(token, [f"border-bottom: {HTML_BORDER_SPEC}"]) 187 188 return super().renderToken(tokens, token_index, options, env) 189 190 def th_open(self, 191 tokens: typing.List[markdown_it.token.Token], 192 token_index: int, 193 options: markdown_it.utils.OptionsDict, 194 env: typing.Dict[str, typing.Any], 195 ) -> str: 196 """ Render the opening of a th. """ 197 198 token = tokens[token_index] 199 200 context = typing.cast(quizcomp.parser.common.RenderContext, env[quizcomp.parser.common.ENV_KEY_CONTEXT]) 201 202 self._cell_html(token, context.style) 203 204 weight = 'normal' 205 use_bold = quizcomp.parser.style.get_boolean_style_key( 206 context.style, 207 quizcomp.parser.style.KEY_TABLE_HEAD_BOLD, 208 quizcomp.parser.style.DEFAULT_TABLE_HEAD_BOLD, 209 ) 210 211 if (use_bold): 212 weight = 'bold' 213 214 _join_html_style(token, [f"font-weight: {weight}"]) 215 216 return super().renderToken(tokens, token_index, options, env) 217 218 def td_open(self, 219 tokens: typing.List[markdown_it.token.Token], 220 token_index: int, 221 options: markdown_it.utils.OptionsDict, 222 env: typing.Dict[str, typing.Any], 223 ) -> str: 224 """ Render the opening of a td. """ 225 226 token = tokens[token_index] 227 228 context = typing.cast(quizcomp.parser.common.RenderContext, env[quizcomp.parser.common.ENV_KEY_CONTEXT]) 229 230 self._cell_html(token, context.style) 231 232 return super().renderToken(tokens, token_index, options, env) 233 234 def _cell_html(self, token: markdown_it.token.Token, style: typing.Dict[str, typing.Any]) -> None: 235 """ 236 Common cell rendering. 237 """ 238 239 height = max(1.0, float(style.get(quizcomp.parser.style.KEY_TABLE_CELL_HEIGHT, quizcomp.parser.style.DEFAULT_TABLE_CELL_HEIGHT))) 240 vertical_padding = height - 1.0 241 242 width = max(1.0, float(style.get(quizcomp.parser.style.KEY_TABLE_CELL_WIDTH, quizcomp.parser.style.DEFAULT_TABLE_CELL_WIDTH))) 243 horizontal_padding = width - 1.0 244 245 cell_style = { 246 'padding-top': f"{(vertical_padding / 2):0.2f}em", 247 'padding-bottom': f"{(vertical_padding / 2):0.2f}em", 248 'padding-left': f"{(horizontal_padding / 2):0.2f}em", 249 'padding-right': f"{(horizontal_padding / 2):0.2f}em", 250 } 251 252 if (quizcomp.parser.style.get_boolean_style_key( 253 style, quizcomp.parser.style.KEY_TABLE_BORDER_CELLS, quizcomp.parser.style.DEFAULT_TABLE_BORDER_CELLS)): 254 cell_style['border'] = HTML_BORDER_SPEC 255 256 _join_html_style(token, [': '.join(item) for item in cell_style.items()])
A renderer for HTML. This builds of of the existing markdown_it.renderer.RendererHTML renderer.
def
image( self, tokens: List[markdown_it.token.Token], token_index: int, options: markdown_it.utils.OptionsDict, env: Dict[str, Any], force_raw_image_src: bool = False, **kwargs: Any) -> str:
28 def image(self, # type: ignore[override] # pylint: disable=arguments-renamed 29 tokens: typing.List[markdown_it.token.Token], 30 token_index: int, 31 options: markdown_it.utils.OptionsDict, 32 env: typing.Dict[str, typing.Any], 33 force_raw_image_src: bool = False, 34 **kwargs: typing.Any) -> str: 35 """ Render an image. """ 36 37 # Do custom rendering and then pass onto super. 38 39 context = typing.cast(quizcomp.parser.common.RenderContext, env[quizcomp.parser.common.ENV_KEY_CONTEXT]) 40 41 # Set width. 42 width_float = quizcomp.parser.style.get_image_width(context.style) 43 tokens[token_index].attrSet('width', f"{(width_float * 100.0):0.2f}%") 44 45 original_src = str(tokens[token_index].attrGet('src')) 46 47 # Check the env to see if we need to force raw images. 48 force_raw_image_src = (force_raw_image_src or context.force_raw_image_src) 49 50 if (force_raw_image_src or re.match(r'^http(s)?://', original_src) or original_src.startswith('data:image')): 51 # Do not further modify the src if we are explicitly directed not to 52 # or if it is an http or data URL. 53 pass 54 else: 55 # Otherwise, do a base64 encoding of the image and embed it. 56 path = original_src 57 if (not os.path.isabs(path)): 58 path = os.path.join(context.base_dir, path) 59 60 path = os.path.abspath(path) 61 62 mime, content = quizcomp.util.image.b64_encode(path) 63 tokens[token_index].attrSet('src', f"data:{mime};base64,{content}") 64 65 result = super().image(tokens, token_index, options, env) 66 67 # Reset the src so that future callback hits have the proper cache key. 68 tokens[token_index].attrSet('src', original_src) 69 70 return result
Render an image.
def
container_block_open( self, tokens: List[markdown_it.token.Token], token_index: int, options: markdown_it.utils.OptionsDict, env: Dict[str, Any]) -> str:
72 def container_block_open(self, 73 tokens: typing.List[markdown_it.token.Token], 74 token_index: int, 75 options: markdown_it.utils.OptionsDict, 76 env: typing.Dict[str, typing.Any], 77 ) -> str: 78 """ Render the opening of a block. """ 79 80 context = typing.cast(quizcomp.parser.common.RenderContext, env[quizcomp.parser.common.ENV_KEY_CONTEXT]) 81 82 # Add on a specific class. 83 tokens[token_index].attrJoin('class', 'qg-block') 84 85 # Pull any style attached to this block and put it in a copy of the context. 86 context, block_style = quizcomp.parser.common.handle_block_style(tokens[token_index].meta, context) 87 env[quizcomp.parser.common.ENV_KEY_CONTEXT] = context 88 89 # Attach style based on if we are the root block. 90 # If root use all style, otherwise just use the style for this block. 91 active_style = block_style 92 if (tokens[token_index].meta.get(quizcomp.parser.common.TOKEN_META_KEY_ROOT, False)): 93 active_style = context.style 94 95 style_string = quizcomp.parser.style.compute_html_style_string(active_style) 96 if (style_string != ''): 97 tokens[token_index].attrSet('style', style_string) 98 99 # Send to super for further rendering. 100 return super().renderToken(tokens, token_index, options, env)
Render the opening of a block.
def
math_inline( self, tokens: List[markdown_it.token.Token], token_index: int, options: markdown_it.utils.OptionsDict, env: Dict[str, Any]) -> str:
102 def math_inline(self, 103 tokens: typing.List[markdown_it.token.Token], 104 token_index: int, 105 options: markdown_it.utils.OptionsDict, 106 env: typing.Dict[str, typing.Any], 107 ) -> str: 108 """ Render inline math. """ 109 110 return quizcomp.parser.math.render(quizcomp.model.constants.Format.HTML, True, tokens, token_index, options, env)
Render inline math.
def
math_block( self, tokens: List[markdown_it.token.Token], token_index: int, options: markdown_it.utils.OptionsDict, env: Dict[str, Any]) -> str:
112 def math_block(self, 113 tokens: typing.List[markdown_it.token.Token], 114 token_index: int, 115 options: markdown_it.utils.OptionsDict, 116 env: typing.Dict[str, typing.Any], 117 ) -> str: 118 """ Render a math block. """ 119 120 return quizcomp.parser.math.render(quizcomp.model.constants.Format.HTML, False, tokens, token_index, options, env)
Render a math block.
def
placeholder( self, tokens: List[markdown_it.token.Token], token_index: int, options: markdown_it.utils.OptionsDict, env: Dict[str, Any]) -> str:
122 def placeholder(self, 123 tokens: typing.List[markdown_it.token.Token], 124 token_index: int, 125 options: markdown_it.utils.OptionsDict, 126 env: typing.Dict[str, typing.Any], 127 ) -> str: 128 """ Render an answer placeholder. """ 129 130 return f"<placeholder>{tokens[token_index].content}</placeholder>"
Render an answer placeholder.
def
table_open( self, tokens: List[markdown_it.token.Token], token_index: int, options: markdown_it.utils.OptionsDict, env: Dict[str, Any]) -> str:
132 def table_open(self, 133 tokens: typing.List[markdown_it.token.Token], 134 token_index: int, 135 options: markdown_it.utils.OptionsDict, 136 env: typing.Dict[str, typing.Any], 137 ) -> str: 138 """ Render the opening of a table. """ 139 140 token = tokens[token_index] 141 context = typing.cast(quizcomp.parser.common.RenderContext, env[quizcomp.parser.common.ENV_KEY_CONTEXT]) 142 143 table_style = [ 144 'border-collapse: collapse', 145 ] 146 147 has_border = quizcomp.parser.style.get_boolean_style_key( 148 context.style, 149 quizcomp.parser.style.KEY_TABLE_BORDER_TABLE, 150 quizcomp.parser.style.DEFAULT_TABLE_BORDER_TABLE, 151 ) 152 153 if (has_border): 154 table_style.append(f"border: {HTML_BORDER_SPEC}") 155 else: 156 table_style.append('border-style: hidden') 157 158 # HTML tables require extra encouragement to align. 159 text_align = quizcomp.parser.style.get_alignment(context.style, quizcomp.parser.style.KEY_TEXT_ALIGN) 160 if (text_align is not None): 161 table_style.append(f"text-align: {text_align}") 162 163 _join_html_style(token, table_style) 164 165 return super().renderToken(tokens, token_index, options, env)
Render the opening of a table.
def
thead_open( self, tokens: List[markdown_it.token.Token], token_index: int, options: markdown_it.utils.OptionsDict, env: Dict[str, Any]) -> str:
167 def thead_open(self, 168 tokens: typing.List[markdown_it.token.Token], 169 token_index: int, 170 options: markdown_it.utils.OptionsDict, 171 env: typing.Dict[str, typing.Any], 172 ) -> str: 173 """ Render the opening of a table header. """ 174 175 token = tokens[token_index] 176 177 context = typing.cast(quizcomp.parser.common.RenderContext, env[quizcomp.parser.common.ENV_KEY_CONTEXT]) 178 179 has_head = quizcomp.parser.style.get_boolean_style_key( 180 context.style, 181 quizcomp.parser.style.KEY_TABLE_HEAD_RULE, 182 quizcomp.parser.style.DEFAULT_TABLE_HEAD_RULE, 183 ) 184 185 if (has_head): 186 _join_html_style(token, [f"border-bottom: {HTML_BORDER_SPEC}"]) 187 188 return super().renderToken(tokens, token_index, options, env)
Render the opening of a table header.
def
th_open( self, tokens: List[markdown_it.token.Token], token_index: int, options: markdown_it.utils.OptionsDict, env: Dict[str, Any]) -> str:
190 def th_open(self, 191 tokens: typing.List[markdown_it.token.Token], 192 token_index: int, 193 options: markdown_it.utils.OptionsDict, 194 env: typing.Dict[str, typing.Any], 195 ) -> str: 196 """ Render the opening of a th. """ 197 198 token = tokens[token_index] 199 200 context = typing.cast(quizcomp.parser.common.RenderContext, env[quizcomp.parser.common.ENV_KEY_CONTEXT]) 201 202 self._cell_html(token, context.style) 203 204 weight = 'normal' 205 use_bold = quizcomp.parser.style.get_boolean_style_key( 206 context.style, 207 quizcomp.parser.style.KEY_TABLE_HEAD_BOLD, 208 quizcomp.parser.style.DEFAULT_TABLE_HEAD_BOLD, 209 ) 210 211 if (use_bold): 212 weight = 'bold' 213 214 _join_html_style(token, [f"font-weight: {weight}"]) 215 216 return super().renderToken(tokens, token_index, options, env)
Render the opening of a th.
def
td_open( self, tokens: List[markdown_it.token.Token], token_index: int, options: markdown_it.utils.OptionsDict, env: Dict[str, Any]) -> str:
218 def td_open(self, 219 tokens: typing.List[markdown_it.token.Token], 220 token_index: int, 221 options: markdown_it.utils.OptionsDict, 222 env: typing.Dict[str, typing.Any], 223 ) -> str: 224 """ Render the opening of a td. """ 225 226 token = tokens[token_index] 227 228 context = typing.cast(quizcomp.parser.common.RenderContext, env[quizcomp.parser.common.ENV_KEY_CONTEXT]) 229 230 self._cell_html(token, context.style) 231 232 return super().renderToken(tokens, token_index, options, env)
Render the opening of a td.
273def get_renderer(options: markdown_it.utils.OptionsDict) -> QuizComposerRendererHTML: 274 """ Get this renderer and options. """ 275 276 return QuizComposerRendererHTML()
Get this renderer and options.