md2bbcode 0.1.0__py2.py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
md2bbcode/__about__.py ADDED
@@ -0,0 +1 @@
1
+ __version__ = "0.1.0"
md2bbcode/__init__.py ADDED
File without changes
md2bbcode/eqlog.md ADDED
@@ -0,0 +1,36 @@
1
+ # EQLogParser
2
+ Everquest Log Parser for Live/TLP servers with basic support for P99.
3
+
4
+ Link to DOWNLOAD the latest Installer:</br>
5
+ https://github.com/kauffman12/EQLogParser/raw/master/Release/EQLogParser-install-2.2.48.exe
6
+
7
+ Minimum Requirements:
8
+ 1. Windows 10 x64
9
+ 2. .Net 8.0 Desktop Runtime for x64
10
+
11
+ .Net 8.0 is provided by Microsoft but is not included with Windows. It can be downloaded from here:</br>
12
+ https://dotnet.microsoft.com/en-us/download/dotnet/thank-you/runtime-desktop-8.0.7-windows-x64-installer
13
+
14
+ If Everquest is in windowed mode but the Damage Meter or other overlays are hidden when you switch to the game make sure Overlap with Taskbar is turned off:</br>
15
+
16
+ ![Parser](./examples/eqsetting.png)
17
+
18
+ Note for Developers:</br>
19
+ Syncfusion components used by this application require a license. If you apply for a community license you should be able to get one for free.
20
+
21
+ Additional Notes:</br>
22
+ The installer for EQLogParser has been signed with a certificate. It's recommended that the following steps are done ONCE so that you're sure you have an official version. After your system trusts the certificate you'll notice the install prompt will be blue in color and no longer say Unknown Publisher. Then in the future if it returns to yellow/Unknown Publisher you'll know that the installer either wasn't from me or I had to change certificates. Which I will mention here if I have change them. Note that this is not required and if you don't bother installing the certs everything should still work fine.
23
+
24
+ 1. right-click the exe file and choose properties
25
+ 2. under the digital signatures tab select the one signature and click details
26
+ 3. click View Certificate
27
+ 4. click Install Certificate
28
+
29
+ Lastly, EQLogParser-2.2.13.msi in the Releases folder can be ignored by most people. It exists to allow auto update to work from old versions of the parser. Old parsers were hard coded to look for MSI files and this one just wraps the exe installer for 2.2.13. Then the next time the parser is started it will auto update to the latest exe installer.
30
+
31
+
32
+ # Example
33
+ ![Parser](./examples/example1.png)
34
+
35
+ # Damage Meter and Timer Overlay Example
36
+ ![Damage Meter](./examples/example2.png)
@@ -0,0 +1,143 @@
1
+ # converts some HTML tags to BBCode
2
+ # pass --debug to save the output to readme.finalpass
3
+ # may be better off replacing this with html to markdown (and then to bbcode). Lepture recommeds a JS html to markdown converter: sundown
4
+ from bs4 import BeautifulSoup
5
+ import argparse
6
+
7
+ def handle_font_tag(tag, replacements):
8
+ """Handles the conversion of <font> tag with attributes like color and size."""
9
+ attributes = []
10
+ if 'color' in tag.attrs:
11
+ attributes.append(f"COLOR={tag['color']}")
12
+ if 'size' in tag.attrs:
13
+ attributes.append(f"SIZE={tag['size']}")
14
+ if 'face' in tag.attrs:
15
+ attributes.append(f"FONT={tag['face']}")
16
+
17
+ inner_content = ''.join(recursive_html_to_bbcode(child, replacements) for child in tag.children)
18
+ if attributes:
19
+ # Nest all attributes. Example: [COLOR=red][SIZE=5]content[/SIZE][/COLOR]
20
+ for attr in reversed(attributes):
21
+ inner_content = f"[{attr}]{inner_content}[/{attr.split('=')[0]}]"
22
+ return inner_content
23
+
24
+ def handle_style_tag(tag, replacements):
25
+ """Handles the conversion of tags with style attributes like color, size, and font."""
26
+ attributes = []
27
+ style = tag.attrs.get('style', '')
28
+
29
+ # Extracting CSS properties
30
+ css_properties = {item.split(':')[0].strip(): item.split(':')[1].strip() for item in style.split(';') if ':' in item}
31
+
32
+ # Mapping CSS properties to BBCode
33
+ if 'color' in css_properties:
34
+ attributes.append(f"COLOR={css_properties['color']}")
35
+ if 'font-size' in css_properties:
36
+ attributes.append(f"SIZE={css_properties['font-size']}")
37
+ if 'font-family' in css_properties:
38
+ attributes.append(f"FONT={css_properties['font-family']}")
39
+ if 'text-decoration' in css_properties and 'line-through' in css_properties['text-decoration']:
40
+ attributes.append("S") # Assume strike-through
41
+ if 'text-decoration' in css_properties and 'underline' in css_properties['text-decoration']:
42
+ attributes.append("U")
43
+ if 'font-weight' in css_properties:
44
+ if css_properties['font-weight'].lower() == 'bold' or (css_properties['font-weight'].isdigit() and int(css_properties['font-weight']) >= 700):
45
+ attributes.append("B") # Assume bold
46
+
47
+ inner_content = ''.join(recursive_html_to_bbcode(child, replacements) for child in tag.children)
48
+ if attributes:
49
+ # Nest all attributes
50
+ for attr in reversed(attributes):
51
+ if '=' in attr: # For attributes with values
52
+ inner_content = f"[{attr}]{inner_content}[/{attr.split('=')[0]}]"
53
+ else: # For simple BBCode tags like [B], [I], [U], [S]
54
+ inner_content = f"[{attr}]{inner_content}[/{attr}]"
55
+ return inner_content
56
+
57
+ def recursive_html_to_bbcode(tag, replacements):
58
+ """Recursively convert HTML content of a given tag to BBCode."""
59
+ if tag.name is None:
60
+ return str(tag)
61
+ elif tag.name == 'br':
62
+ # Directly return a newline for <br> or </br> tags
63
+ return '\n'
64
+ elif tag.name in replacements:
65
+ bb_tag = replacements[tag.name]
66
+ inner_content = ''
67
+ for child in tag.children:
68
+ inner_content += recursive_html_to_bbcode(child, replacements)
69
+
70
+ if tag.name in ['a', 'img']:
71
+ if tag.name == 'a':
72
+ href = tag.get('href', '')
73
+ return f"[URL={href}]{inner_content}[/URL]"
74
+ elif tag.name == 'img':
75
+ src = tag.get('src', '')
76
+ alt = tag.get('alt', '')
77
+ if alt:
78
+ return f"[IMG alt=\"{alt}\"]{src}[/IMG]"
79
+ else:
80
+ return f"[IMG]{src}[/IMG]"
81
+ elif tag.name in ['ul', 'ol']:
82
+ return f"[{bb_tag}]{inner_content}[/LIST]"
83
+ elif tag.name == 'font':
84
+ # Special handling for <font> tag with attributes
85
+ return handle_font_tag(tag, replacements) # Pass replacements here
86
+ elif tag.name == 'li':
87
+ return f"[*]{inner_content}"
88
+ else:
89
+ return f"[{bb_tag}]{inner_content}[/{bb_tag}]"
90
+ elif tag.name in ['span', 'div']:
91
+ return handle_style_tag(tag, replacements)
92
+ else:
93
+ # For tags not in the replacements, concatenate the content
94
+ return ''.join(recursive_html_to_bbcode(child, replacements) for child in tag.children)
95
+
96
+ def html_to_bbcode(html):
97
+ replacements = {
98
+ 'b': 'B',
99
+ 'strong': 'B',
100
+ 'i': 'I',
101
+ 'em': 'I',
102
+ 'u': 'U',
103
+ 's': 'S',
104
+ 'sub': 'SUB',
105
+ 'sup': 'SUP',
106
+ 'p': '', # Handled by default
107
+ 'ul': 'LIST',
108
+ 'ol': 'LIST=1',
109
+ 'li': '*', # Special handling in recursive function
110
+ 'font': '', # To be handled for attributes
111
+ 'blockquote': 'QUOTE',
112
+ 'pre': 'CODE',
113
+ 'code': 'ICODE',
114
+ 'a': 'URL', # Special handling for attributes
115
+ 'img': 'IMG' # Special handling for attributes
116
+ }
117
+
118
+ soup = BeautifulSoup(html, 'html.parser')
119
+ return recursive_html_to_bbcode(soup, replacements)
120
+
121
+ def process_html(input_html, debug=False, output_file=None):
122
+ converted_bbcode = html_to_bbcode(input_html)
123
+
124
+ if debug:
125
+ with open(output_file, 'w', encoding='utf-8') as file:
126
+ file.write(converted_bbcode)
127
+ else:
128
+ return converted_bbcode
129
+
130
+ if __name__ == "__main__":
131
+ parser = argparse.ArgumentParser(description="Convert HTML to BBCode with optional debugging output.")
132
+ parser.add_argument('input_file', type=str, help='Input HTML file path')
133
+ parser.add_argument('--debug', action='store_true', help='Save output to readme.finalpass for debugging')
134
+
135
+ args = parser.parse_args()
136
+ input_file = args.input_file
137
+ output_file = 'readme.finalpass' if args.debug else None
138
+
139
+ with open(input_file, 'r', encoding='utf-8') as file:
140
+ html_content = file.read()
141
+
142
+ # Call the processing function
143
+ process_html(html_content, debug=args.debug, output_file=output_file)
md2bbcode/main.py ADDED
@@ -0,0 +1,66 @@
1
+ # uses a custom mistune renderer to convert Markdown to BBCode. The custom renderer is defined in the bbcode.py file.
2
+ # pass --debug to save the output to readme.1stpass (main.py) and readme.finalpass (html2bbcode)
3
+ # for further debugging, you can convert the markdown file to AST using md2ast.py. Remember to load the plugin(s) you want to test.
4
+
5
+ #standard library
6
+ import argparse
7
+ import sys
8
+
9
+ # mistune
10
+ import mistune
11
+ from mistune.plugins.formatting import strikethrough, mark, superscript, subscript, insert
12
+ from mistune.plugins.table import table, table_in_list
13
+ from mistune.plugins.footnotes import footnotes
14
+ from mistune.plugins.task_lists import task_lists
15
+ from mistune.plugins.def_list import def_list
16
+ from mistune.plugins.abbr import abbr
17
+ from mistune.plugins.spoiler import spoiler
18
+
19
+ # local
20
+ from .renderers.bbcode import BBCodeRenderer
21
+ from .html2bbcode import process_html
22
+
23
+ def convert_markdown_to_bbcode(markdown_text, domain):
24
+ # Create a Markdown parser instance using the custom BBCode renderer
25
+ markdown_parser = mistune.create_markdown(renderer=BBCodeRenderer(domain=domain), plugins=[strikethrough, mark, superscript, subscript, insert, table, footnotes, task_lists, def_list, abbr, spoiler, table_in_list])
26
+
27
+ # Convert Markdown text to BBCode
28
+ return markdown_parser(markdown_text)
29
+
30
+ def process_readme(markdown_text, domain, debug=False):
31
+ # Convert Markdown to BBCode
32
+ bbcode_text = convert_markdown_to_bbcode(markdown_text, domain)
33
+
34
+ # If debug mode, save intermediate BBCode
35
+ if debug:
36
+ with open('readme.1stpass', 'w', encoding='utf-8') as file:
37
+ file.write(bbcode_text)
38
+
39
+ # Convert BBCode formatted as HTML to final BBCode
40
+ final_bbcode = process_html(bbcode_text, debug, 'readme.finalpass')
41
+
42
+ return final_bbcode
43
+
44
+ def main():
45
+ parser = argparse.ArgumentParser(description='Convert Markdown file to BBCode with HTML processing.')
46
+ parser.add_argument('input', help='Input Markdown file path')
47
+ parser.add_argument('--domain', help='Domain to prepend to relative URLs')
48
+ parser.add_argument('--debug', action='store_true', help='Output intermediate results to files for debugging')
49
+ args = parser.parse_args()
50
+
51
+ if args.input == '-':
52
+ # Read Markdown content from stdin
53
+ markdown_text = sys.stdin.read()
54
+ else:
55
+ with open(args.input, 'r', encoding='utf-8') as md_file:
56
+ markdown_text = md_file.read()
57
+
58
+ # Process the readme and get the final BBCode
59
+ final_bbcode = process_readme(markdown_text, args.domain, args.debug)
60
+
61
+ # Optionally, print final BBCode to console
62
+ if not args.debug:
63
+ print(final_bbcode)
64
+
65
+ if __name__ == '__main__':
66
+ main()
md2bbcode/md2ast.py ADDED
@@ -0,0 +1,44 @@
1
+ # this is for debugging the custom mistune renderer bbcode.py
2
+ import argparse
3
+ import mistune
4
+ import json # Import the json module for serialization
5
+ from mistune.plugins.formatting import strikethrough, mark, superscript, subscript, insert
6
+ from mistune.plugins.table import table, table_in_list
7
+ from mistune.plugins.footnotes import footnotes
8
+ from mistune.plugins.task_lists import task_lists
9
+ from mistune.plugins.def_list import def_list
10
+ from mistune.plugins.abbr import abbr
11
+ from mistune.plugins.spoiler import spoiler
12
+
13
+ def convert_markdown_to_ast(input_filepath, output_filepath):
14
+ # Initialize Markdown parser with no renderer to produce an AST
15
+ markdown_parser = mistune.create_markdown(renderer=None, plugins=[strikethrough, mark, superscript, subscript, insert, table, footnotes, task_lists, def_list, abbr, spoiler, table_in_list])
16
+
17
+ # Read the input Markdown file
18
+ with open(input_filepath, 'r', encoding='utf-8') as md_file:
19
+ markdown_text = md_file.read()
20
+
21
+ # Convert Markdown text to AST
22
+ ast_text = markdown_parser(markdown_text)
23
+
24
+ # Serialize the AST to a JSON string
25
+ ast_json = json.dumps(ast_text, indent=4)
26
+
27
+ # Write the output AST to a new file in JSON format
28
+ with open(output_filepath, 'w', encoding='utf-8') as ast_file:
29
+ ast_file.write(ast_json)
30
+
31
+ def main():
32
+ # Create argument parser
33
+ parser = argparse.ArgumentParser(description='Convert Markdown file to AST file (JSON format).')
34
+ # Add arguments
35
+ parser.add_argument('input', help='Input Markdown file path')
36
+ parser.add_argument('output', help='Output AST file path (JSON format)')
37
+ # Parse arguments
38
+ args = parser.parse_args()
39
+
40
+ # Convert the Markdown to AST using the provided paths
41
+ convert_markdown_to_ast(args.input, args.output)
42
+
43
+ if __name__ == '__main__':
44
+ main()
File without changes
@@ -0,0 +1,222 @@
1
+ from mistune.core import BaseRenderer
2
+ from mistune.util import escape as escape_text, striptags, safe_entity
3
+ from urllib.parse import urljoin, urlparse
4
+
5
+
6
+ class BBCodeRenderer(BaseRenderer):
7
+ """A renderer for converting Markdown to BBCode."""
8
+ _escape: bool
9
+ NAME = 'bbcode'
10
+
11
+ def __init__(self, escape=False, domain=None):
12
+ super(BBCodeRenderer, self).__init__()
13
+ self._escape = escape
14
+ self.domain = domain
15
+
16
+ def render_token(self, token, state):
17
+ func = self._get_method(token['type'])
18
+ attrs = token.get('attrs')
19
+
20
+ if 'raw' in token:
21
+ text = token['raw']
22
+ elif 'children' in token:
23
+ text = self.render_tokens(token['children'], state)
24
+ else:
25
+ if attrs:
26
+ return func(**attrs)
27
+ else:
28
+ return func()
29
+ if attrs:
30
+ return func(text, **attrs)
31
+ else:
32
+ return func(text)
33
+
34
+ def safe_url(self, url: str) -> str:
35
+ # Simple URL sanitization
36
+ if url.startswith(('javascript:', 'vbscript:', 'data:')):
37
+ return '#harmful-link'
38
+ # Check if the URL is absolute by looking for a netloc part in the URL
39
+ if not urlparse(url).netloc:
40
+ url = urljoin(self.domain, url)
41
+ return url
42
+
43
+ def text(self, text: str) -> str:
44
+ if self._escape:
45
+ return escape_text(text)
46
+ return text
47
+
48
+ def emphasis(self, text: str) -> str:
49
+ return '[i]' + text + '[/i]'
50
+
51
+ def strong(self, text: str) -> str:
52
+ return '[b]' + text + '[/b]'
53
+
54
+ def link(self, text: str, url: str, title=None) -> str:
55
+ return '[url=' + self.safe_url(url) + ']' + text + '[/url]'
56
+
57
+ def image(self, text: str, url: str, title=None) -> str:
58
+ alt_text = f' alt="{text}"' if text else ''
59
+ return f'[img{alt_text}]' + self.safe_url(url) + '[/img]'
60
+
61
+ def codespan(self, text: str) -> str:
62
+ return '[icode]' + text + '[/icode]'
63
+
64
+ def linebreak(self) -> str:
65
+ return '\n'
66
+
67
+ def softbreak(self) -> str:
68
+ return ''
69
+
70
+ def inline_html(self, html: str) -> str:
71
+ if self._escape:
72
+ return escape_text(html)
73
+ return html
74
+
75
+ def paragraph(self, text: str) -> str:
76
+ return text + '\n\n'
77
+
78
+ def heading(self, text: str, level: int, **attrs) -> str:
79
+ if 1 <= level <= 3:
80
+ return f"[HEADING={level}]{text}[/HEADING]\n"
81
+ else:
82
+ # Handle cases where level is outside 1-3
83
+ return f"[HEADING=3]{text}[/HEADING]\n"
84
+
85
+ def blank_line(self) -> str:
86
+ return ''
87
+
88
+ def thematic_break(self) -> str:
89
+ return '[hr][/hr]\n'
90
+
91
+ def block_text(self, text: str) -> str:
92
+ return text
93
+
94
+ def block_code(self, code: str, **attrs) -> str:
95
+ # Renders blocks of code using the language specified in Markdown
96
+ special_cases = {
97
+ 'plaintext': None # Default [CODE]
98
+ }
99
+
100
+ if 'info' in attrs:
101
+ lang_info = safe_entity(attrs['info'].strip())
102
+ lang = lang_info.split(None, 1)[0].lower()
103
+ # Check if the language needs special handling
104
+ bbcode_lang = special_cases.get(lang, lang) # Use the special case if it exists, otherwise use lang as is
105
+ if bbcode_lang:
106
+ return f"[CODE={bbcode_lang}]{escape_text(code)}[/CODE]\n\n"
107
+ else:
108
+ return f"[CODE]{escape_text(code)}[/CODE]\n\n"
109
+ else:
110
+ # No language specified, render with a generic [CODE] tag
111
+ return f"[CODE]{escape_text(code)}[/CODE]\n\n"
112
+
113
+ def block_quote(self, text: str) -> str:
114
+ return '[QUOTE]\n' + text + '[/QUOTE]\n'
115
+
116
+ def block_html(self, html: str) -> str:
117
+ if self._escape:
118
+ return '<p>' + escape_text(html.strip()) + '</p>\n'
119
+ return html + '\n'
120
+
121
+ def block_error(self, text: str) -> str:
122
+ return '[color=red][icode]' + text + '[/icode][/color]\n'
123
+
124
+ def list(self, text: str, ordered: bool, **attrs) -> str:
125
+ tag = 'list' if not ordered else 'list=1'
126
+ return '[{}]'.format(tag) + text + '[/list]\n'
127
+
128
+ def list_item(self, text: str) -> str:
129
+ return '[*]' + text + '\n'
130
+
131
+ def strikethrough(self, text: str) -> str:
132
+ return '[s]' + text + '[/s]'
133
+
134
+ def mark(self, text: str) -> str:
135
+ # Simulate the mark effect with a background color in BBCode
136
+ return '[mark]' + text + '[/mark]'
137
+
138
+ def insert(self, text: str) -> str:
139
+ # Use underline to represent insertion
140
+ return '[u]' + text + '[/u]'
141
+
142
+ def superscript(self, text: str) -> str:
143
+ return '[sup]' + text + '[/sup]'
144
+
145
+ def subscript(self, text: str) -> str:
146
+ return '[sub]' + text + '[/sub]'
147
+
148
+ def inline_spoiler(self, text: str) -> str:
149
+ return '[ISPOILER]' + text + '[/ISPOILER]'
150
+
151
+ def block_spoiler(self, text: str) -> str:
152
+ return '[SPOILER]\n' + text + '\n[/SPOILER]'
153
+
154
+ def footnote_ref(self, key: str, index: int):
155
+ # Use superscript for the footnote reference
156
+ return f'[sup][u][JUMPTO=fn-{index}]{index}[/JUMPTO][/u][/sup]'
157
+
158
+ def footnotes(self, text: str):
159
+ # Optionally wrap all footnotes in a specific section if needed
160
+ return '[b]Footnotes:[/b]\n' + text
161
+
162
+ def footnote_item(self, text: str, key: str, index: int):
163
+ # Define the footnote with an anchor at the end of the document
164
+ return f'[ANAME=fn-{index}]{index}[/ANAME]. {text}'
165
+
166
+ def table(self, children, **attrs):
167
+ # Starting with a full-width table by default if not specified
168
+ # width = attrs.get('width', '100%') # comment out until XF 2.3
169
+ # return f'[TABLE width="{width}"]\n' + children + '[/TABLE]\n' # comment out until XF 2.3
170
+ return '[TABLE]\n' + children + '[/TABLE]\n'
171
+
172
+ def table_head(self, children, **attrs):
173
+ return '[TR]\n' + children + '[/TR]\n'
174
+
175
+ def table_body(self, children, **attrs):
176
+ return children
177
+
178
+ def table_row(self, children, **attrs):
179
+ return '[TR]\n' + children + '[/TR]\n'
180
+
181
+ def table_cell(self, text, align=None, head=False, **attrs):
182
+ # BBCode does not support direct cell alignment,
183
+ # use [LEFT], [CENTER], or [RIGHT] tags
184
+
185
+ # Use th for header cells and td for normal cells
186
+ tag = 'TH' if head else 'TD'
187
+
188
+ # Initialize alignment tags
189
+ alignment_start = ''
190
+ alignment_end = ''
191
+
192
+ if align == 'center':
193
+ alignment_start = '[CENTER]'
194
+ alignment_end = '[/CENTER]'
195
+ elif align == 'right':
196
+ alignment_start = '[RIGHT]'
197
+ alignment_end = '[/RIGHT]'
198
+ elif align == 'left':
199
+ alignment_start = '[LEFT]'
200
+ alignment_end = '[/LEFT]'
201
+
202
+ return f'[{tag}]{alignment_start}{text}{alignment_end}[/{tag}]\n'
203
+
204
+ def task_list_item(self, text: str, checked: bool = False) -> str:
205
+ # Using emojis to represent the checkbox
206
+ checkbox_emoji = '🗹' if checked else '☐'
207
+ return checkbox_emoji + ' ' + text + '\n'
208
+
209
+ def def_list(self, text: str) -> str:
210
+ # No specific BBCode tag for <dl>, so we just use the plain text grouping
211
+ return '\n' + text + '\n'
212
+
213
+ def def_list_head(self, text: str) -> str:
214
+ return '[b]' + text + '[/b]' + ' ' + ':' + '\n'
215
+
216
+ def def_list_item(self, text: str) -> str:
217
+ return '[INDENT]' + text + '[/INDENT]\n'
218
+
219
+ def abbr(self, text: str, title: str) -> str:
220
+ if title:
221
+ return f'[abbr={title}]{text}[/abbr]'
222
+ return text
@@ -0,0 +1,126 @@
1
+ Metadata-Version: 2.3
2
+ Name: md2bbcode
3
+ Version: 0.1.0
4
+ Summary: Convert Markdown to BBCode using a custom Mistune renderer.
5
+ Project-URL: Homepage, https://github.com/RedGuides/md2bbcode
6
+ Project-URL: Repository, https://github.com/RedGuides/md2bbcode.git
7
+ Project-URL: Issues, https://github.com/RedGuides/md2bbcode/issues
8
+ Project-URL: Documentation, https://github.com/RedGuides/md2bbcode#readme
9
+ Author-email: Redbot <ask@redguides.com>
10
+ License-Expression: GPL-3.0-or-later
11
+ License-File: LICENSE.txt
12
+ Classifier: Development Status :: 5 - Production/Stable
13
+ Classifier: Intended Audience :: Developers
14
+ Classifier: License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)
15
+ Classifier: Operating System :: OS Independent
16
+ Classifier: Programming Language :: Python :: 3
17
+ Classifier: Topic :: Text Processing :: Markup :: HTML
18
+ Classifier: Topic :: Text Processing :: Markup :: Markdown
19
+ Classifier: Topic :: Utilities
20
+ Requires-Dist: beautifulsoup4
21
+ Requires-Dist: mistune>=3.0.2
22
+ Description-Content-Type: text/markdown
23
+
24
+ ![md2bbcode logo](https://www.redguides.com/images/md2bbcode-logo.png)
25
+
26
+ # md2bbcode
27
+ **A wrapper and plugin for [Mistune](https://github.com/lepture/mistune).** It converts GitHub-flavored Markdown to Xenforo-flavored BBCode. Custom BBCodes made for RedGuides are included in `bb_codes.xml`.
28
+
29
+ ## Installation
30
+
31
+ You can install md2bbcode using pip:
32
+
33
+ ```bash
34
+ pip install md2bbcode
35
+ ```
36
+
37
+ ## Usage
38
+
39
+ After installation, you can use md2bbcode from the command line:
40
+
41
+ ```bash
42
+ md2bbcode README.md
43
+ ```
44
+
45
+ If the markdown includes relative images or other assets, you can use the --domain flag to prepend a domain to the relative URLs:
46
+
47
+ ```bash
48
+ md2bbcode README.md --domain https://raw.githubusercontent.com/RedGuides/md2bbcode/main/
49
+ ```
50
+
51
+ ### Debug Mode
52
+
53
+ You can use the `--debug` flag to save intermediate results to files for debugging:
54
+
55
+ ```bash
56
+ md2bbcode README.md --debug
57
+ ```
58
+
59
+ ## Development
60
+
61
+ If you want to contribute to md2bbcode or set up a development environment, follow these steps:
62
+
63
+ 1. Clone the repository:
64
+ ```bash
65
+ git clone https://github.com/RedGuides/md2bbcode.git
66
+ cd md2bbcode
67
+ ```
68
+
69
+ 2. Install Hatch, which is used for building and managing the project:
70
+ ```bash
71
+ pip install hatch
72
+ ```
73
+
74
+ 3. Create a development environment and install dependencies:
75
+ ```bash
76
+ hatch env create
77
+ ```
78
+
79
+ 4. Activate the development environment:
80
+ ```bash
81
+ hatch shell
82
+ ```
83
+
84
+ ### renderers/bbcode.py
85
+
86
+ The custom plugin for Mistune, which converts AST to bbcode.[^1]
87
+
88
+ [^1]: Mistune does not convert Markdown HTML to AST, hence the need for `html2bbcode`.
89
+
90
+ ## Additional Tools
91
+
92
+ ### html2bbcode
93
+
94
+ Converts several HTML tags typically allowed in Markdown to BBCode.[^2]
95
+
96
+ [^2]: Currently used for post-processing mistune output, but there's a better way. See inside the file for a suggestion.
97
+
98
+ ```bash
99
+ html2bbcode input_file.html
100
+ ```
101
+
102
+ ### md2ast
103
+
104
+ For debugging Mistune's renderer, converts a Markdown file to AST (JSON format).
105
+
106
+ ```bash
107
+ md2ast input.md output.json
108
+ ```
109
+
110
+ ## Features Test
111
+
112
+ Here are a few GitHub-flavored Markdown features so you can use this README.md for testing:
113
+
114
+ - **Strikethrough:** ~~This text is struck through.~~
115
+ - **Superscript:** This text is normal and this is <sup>superscript</sup>.
116
+ - **Table:**
117
+
118
+ | Syntax | Description |
119
+ | ----------- | ----------- |
120
+ | Header | Title |
121
+ | Paragraph | Text |
122
+
123
+ ## Todo
124
+
125
+ - refactor html2bbcode
126
+ - update for new Xenforo 2.3 and 2.4 BBCode
@@ -0,0 +1,13 @@
1
+ md2bbcode/__about__.py,sha256=Pru0BlFBASFCFo7McHdohtKkUtgMPDwbGfyUZlE2_Vw,21
2
+ md2bbcode/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
3
+ md2bbcode/eqlog.md,sha256=acMtTLnE-kfmRnVq1pZeLUmeLDHgEH6QNWUK-RE7EJU,2182
4
+ md2bbcode/html2bbcode.py,sha256=yONG7DMxx_QBvSExGH0_viw6RnX6_PXDtAubkXvUf2c,6102
5
+ md2bbcode/main.py,sha256=g5Lp_HO6AsT6aYX2g7aUchhNSzyubjyGqUvAuViUR4c,2757
6
+ md2bbcode/md2ast.py,sha256=gsmDTuDyTL7Q25bX2GhDR2TkMg1y3-mte4PnUGYpKk0,1888
7
+ md2bbcode/renderers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
8
+ md2bbcode/renderers/bbcode.py,sha256=425aNzLiGvJHlKAlK78jL4HiPtin18sg5oGzzkjK4pY,7807
9
+ md2bbcode-0.1.0.dist-info/METADATA,sha256=OJHafV0w6s5W_kExIg500WGB5mFGBFB_6Bm0ETMcEho,3454
10
+ md2bbcode-0.1.0.dist-info/WHEEL,sha256=fl6v0VwpzfGBVsGtkAkhILUlJxROXbA3HvRL6Fe3140,105
11
+ md2bbcode-0.1.0.dist-info/entry_points.txt,sha256=JUMQnuUEsZ8Fy5vx5O7hYWiD7QWSmByfTFIB1aD6Y9Q,122
12
+ md2bbcode-0.1.0.dist-info/licenses/LICENSE.txt,sha256=IwGE9guuL-ryRPEKi6wFPI_zOhg7zDZbTYuHbSt_SAk,35823
13
+ md2bbcode-0.1.0.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: hatchling 1.25.0
3
+ Root-Is-Purelib: true
4
+ Tag: py2-none-any
5
+ Tag: py3-none-any
@@ -0,0 +1,4 @@
1
+ [console_scripts]
2
+ html2bbcode = md2bbcode.html2bbcode:main
3
+ md2ast = md2bbcode.md2ast:main
4
+ md2bbcode = md2bbcode.main:main