markdocx 0.1.0__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.
markdocx/styles.py ADDED
@@ -0,0 +1,248 @@
1
+ """
2
+ Document styles configuration.
3
+ Định nghĩa font, kích thước, màu sắc cho tài liệu giáo trình.
4
+ """
5
+
6
+ from docx.shared import Pt, Inches, RGBColor, Cm, Emu
7
+ from docx.enum.text import WD_ALIGN_PARAGRAPH, WD_LINE_SPACING
8
+ from docx.enum.table import WD_TABLE_ALIGNMENT
9
+ from docx.oxml import OxmlElement
10
+ from docx.oxml.ns import qn
11
+
12
+
13
+ # ── Font Configuration ──────────────────────────────────────────────
14
+ FONT_BODY = "Times New Roman"
15
+ FONT_HEADING = "Arial"
16
+ FONT_CODE = "Consolas"
17
+
18
+ FONT_SIZE_BODY = Pt(12)
19
+ FONT_SIZE_CODE = Pt(9.5)
20
+ FONT_SIZE_CODE_BLOCK = Pt(9)
21
+ FONT_SIZE_SMALL = Pt(10)
22
+
23
+ # ── Heading Sizes ───────────────────────────────────────────────────
24
+ HEADING_SIZES = {
25
+ 1: Pt(22),
26
+ 2: Pt(18),
27
+ 3: Pt(15),
28
+ 4: Pt(13),
29
+ 5: Pt(12),
30
+ 6: Pt(11),
31
+ }
32
+
33
+ HEADING_COLORS = {
34
+ 1: RGBColor(0x1A, 0x23, 0x7E), # Deep Blue
35
+ 2: RGBColor(0x28, 0x3E, 0x9A), # Medium Blue
36
+ 3: RGBColor(0x37, 0x47, 0x4F), # Dark Gray Blue
37
+ 4: RGBColor(0x45, 0x55, 0x60), # Gray
38
+ 5: RGBColor(0x54, 0x64, 0x6E), # Medium Gray
39
+ 6: RGBColor(0x60, 0x70, 0x7A), # Light Gray
40
+ }
41
+
42
+ # ── Colors ──────────────────────────────────────────────────────────
43
+ COLOR_BODY_TEXT = RGBColor(0x21, 0x21, 0x21)
44
+ COLOR_LINK = RGBColor(0x05, 0x63, 0xC1)
45
+ COLOR_INLINE_CODE_BG = "E8E8E8"
46
+ COLOR_CODE_BLOCK_BG = "F8F8F8"
47
+ COLOR_CODE_BLOCK_BORDER = "DDDDDD"
48
+ COLOR_BLOCKQUOTE_BORDER = "BBBBBB"
49
+ COLOR_BLOCKQUOTE_TEXT = RGBColor(0x55, 0x55, 0x55)
50
+ COLOR_TABLE_HEADER_BG = "E3F2FD"
51
+ COLOR_TABLE_BORDER = "BBBBBB"
52
+ COLOR_HR = "CCCCCC"
53
+
54
+ # ── Spacing ─────────────────────────────────────────────────────────
55
+ LINE_SPACING = 1.15
56
+ PARA_SPACE_BEFORE = Pt(3)
57
+ PARA_SPACE_AFTER = Pt(6)
58
+ HEADING_SPACE_BEFORE = Pt(18)
59
+ HEADING_SPACE_AFTER = Pt(8)
60
+ CODE_BLOCK_SPACE = Pt(6)
61
+ LIST_INDENT = Inches(0.35)
62
+
63
+ # ── Page Setup ──────────────────────────────────────────────────────
64
+ PAGE_MARGIN_TOP = Cm(2.54)
65
+ PAGE_MARGIN_BOTTOM = Cm(2.54)
66
+ PAGE_MARGIN_LEFT = Cm(2.54)
67
+ PAGE_MARGIN_RIGHT = Cm(2.54)
68
+
69
+ # ── Math ────────────────────────────────────────────────────────────
70
+ MATH_DPI = 300
71
+ MATH_FONTSIZE_INLINE = 14
72
+ MATH_FONTSIZE_DISPLAY = 16
73
+ MATH_MAX_WIDTH = Inches(5.5)
74
+ MATH_INLINE_HEIGHT = Inches(0.22)
75
+
76
+ # ── Code Syntax Colors (VS Code-like theme) ────────────────────────
77
+ from pygments.token import Token
78
+
79
+ SYNTAX_COLORS = {
80
+ Token.Keyword: RGBColor(0x00, 0x00, 0xCC), # Blue
81
+ Token.Keyword.Constant: RGBColor(0x00, 0x00, 0xCC),
82
+ Token.Keyword.Declaration: RGBColor(0x00, 0x00, 0xCC),
83
+ Token.Keyword.Namespace: RGBColor(0x7B, 0x30, 0x7B), # Purple
84
+ Token.Keyword.Type: RGBColor(0x26, 0x7F, 0x99), # Teal
85
+ Token.Name.Function: RGBColor(0x79, 0x5E, 0x26), # Dark Yellow
86
+ Token.Name.Function.Magic: RGBColor(0x79, 0x5E, 0x26),
87
+ Token.Name.Class: RGBColor(0x26, 0x7F, 0x99), # Teal
88
+ Token.Name.Decorator: RGBColor(0x79, 0x5E, 0x26), # Dark Yellow
89
+ Token.Name.Builtin: RGBColor(0x26, 0x7F, 0x99), # Teal
90
+ Token.Name.Builtin.Pseudo: RGBColor(0x00, 0x00, 0xCC),
91
+ Token.Literal.String: RGBColor(0xA3, 0x15, 0x15), # Red
92
+ Token.Literal.String.Doc: RGBColor(0xA3, 0x15, 0x15),
93
+ Token.Literal.String.Single: RGBColor(0xA3, 0x15, 0x15),
94
+ Token.Literal.String.Double: RGBColor(0xA3, 0x15, 0x15),
95
+ Token.Literal.String.Escape: RGBColor(0xEE, 0x00, 0x00),
96
+ Token.Literal.String.Interpol: RGBColor(0xEE, 0x00, 0x00),
97
+ Token.Literal.String.Affix: RGBColor(0x00, 0x00, 0xCC),
98
+ Token.Literal.Number: RGBColor(0x09, 0x88, 0x58), # Green
99
+ Token.Literal.Number.Integer: RGBColor(0x09, 0x88, 0x58),
100
+ Token.Literal.Number.Float: RGBColor(0x09, 0x88, 0x58),
101
+ Token.Comment: RGBColor(0x6A, 0x99, 0x55), # Olive Green
102
+ Token.Comment.Single: RGBColor(0x6A, 0x99, 0x55),
103
+ Token.Comment.Multiline: RGBColor(0x6A, 0x99, 0x55),
104
+ Token.Comment.Hashbang: RGBColor(0x6A, 0x99, 0x55),
105
+ Token.Operator: RGBColor(0x33, 0x33, 0x33),
106
+ Token.Operator.Word: RGBColor(0x00, 0x00, 0xCC),
107
+ Token.Punctuation: RGBColor(0x33, 0x33, 0x33),
108
+ Token.Name.Tag: RGBColor(0x80, 0x00, 0x00), # HTML tags
109
+ Token.Name.Attribute: RGBColor(0xFF, 0x00, 0x00), # HTML attrs
110
+ }
111
+
112
+
113
+ def get_syntax_color(token_type):
114
+ """Get color for a Pygments token type, walking up the hierarchy."""
115
+ t = token_type
116
+ while t:
117
+ if t in SYNTAX_COLORS:
118
+ return SYNTAX_COLORS[t]
119
+ t = t.parent
120
+ return None
121
+
122
+
123
+ # ── XML Helpers ─────────────────────────────────────────────────────
124
+
125
+ def set_paragraph_shading(paragraph, fill_color):
126
+ """Set background color for an entire paragraph."""
127
+ pPr = paragraph._element.get_or_add_pPr()
128
+ shd = OxmlElement("w:shd")
129
+ shd.set(qn("w:val"), "clear")
130
+ shd.set(qn("w:color"), "auto")
131
+ shd.set(qn("w:fill"), fill_color)
132
+ pPr.append(shd)
133
+
134
+
135
+ def set_paragraph_borders(paragraph, color="CCCCCC", size="4", space="4"):
136
+ """Add borders around a paragraph."""
137
+ pPr = paragraph._element.get_or_add_pPr()
138
+ pBdr = OxmlElement("w:pBdr")
139
+ for side in ("top", "left", "bottom", "right"):
140
+ border = OxmlElement(f"w:{side}")
141
+ border.set(qn("w:val"), "single")
142
+ border.set(qn("w:sz"), size)
143
+ border.set(qn("w:space"), space)
144
+ border.set(qn("w:color"), color)
145
+ pBdr.append(border)
146
+ pPr.append(pBdr)
147
+
148
+
149
+ def set_run_shading(run, fill_color):
150
+ """Set background highlight for a run (inline code)."""
151
+ rPr = run._element.get_or_add_rPr()
152
+ shd = OxmlElement("w:shd")
153
+ shd.set(qn("w:val"), "clear")
154
+ shd.set(qn("w:color"), "auto")
155
+ shd.set(qn("w:fill"), fill_color)
156
+ rPr.append(shd)
157
+
158
+
159
+ def set_blockquote_style(paragraph, depth=1):
160
+ """Style a paragraph as a blockquote with left border."""
161
+ paragraph.paragraph_format.left_indent = Inches(0.4 * depth)
162
+ pPr = paragraph._element.get_or_add_pPr()
163
+ pBdr = OxmlElement("w:pBdr")
164
+ left = OxmlElement("w:left")
165
+ left.set(qn("w:val"), "single")
166
+ left.set(qn("w:sz"), "18")
167
+ left.set(qn("w:space"), "8")
168
+ left.set(qn("w:color"), COLOR_BLOCKQUOTE_BORDER)
169
+ pBdr.append(left)
170
+ pPr.append(pBdr)
171
+
172
+
173
+ def add_horizontal_rule(doc):
174
+ """Add a horizontal rule to the document."""
175
+ p = doc.add_paragraph()
176
+ p.paragraph_format.space_before = Pt(8)
177
+ p.paragraph_format.space_after = Pt(8)
178
+ pPr = p._element.get_or_add_pPr()
179
+ pBdr = OxmlElement("w:pBdr")
180
+ bottom = OxmlElement("w:bottom")
181
+ bottom.set(qn("w:val"), "single")
182
+ bottom.set(qn("w:sz"), "12")
183
+ bottom.set(qn("w:space"), "1")
184
+ bottom.set(qn("w:color"), COLOR_HR)
185
+ pBdr.append(bottom)
186
+ pPr.append(pBdr)
187
+
188
+
189
+ def set_table_header_shading(cell, fill_color=COLOR_TABLE_HEADER_BG):
190
+ """Set background color for a table header cell."""
191
+ tcPr = cell._element.get_or_add_tcPr()
192
+ shd = OxmlElement("w:shd")
193
+ shd.set(qn("w:val"), "clear")
194
+ shd.set(qn("w:color"), "auto")
195
+ shd.set(qn("w:fill"), fill_color)
196
+ tcPr.append(shd)
197
+
198
+
199
+ def setup_document_styles(doc):
200
+ """Configure the document with standard page setup and styles."""
201
+ # Page margins
202
+ for section in doc.sections:
203
+ section.top_margin = PAGE_MARGIN_TOP
204
+ section.bottom_margin = PAGE_MARGIN_BOTTOM
205
+ section.left_margin = PAGE_MARGIN_LEFT
206
+ section.right_margin = PAGE_MARGIN_RIGHT
207
+
208
+ # Default paragraph style
209
+ style = doc.styles["Normal"]
210
+ font = style.font
211
+ font.name = FONT_BODY
212
+ font.size = FONT_SIZE_BODY
213
+ font.color.rgb = COLOR_BODY_TEXT
214
+ pf = style.paragraph_format
215
+ pf.space_before = PARA_SPACE_BEFORE
216
+ pf.space_after = PARA_SPACE_AFTER
217
+ pf.line_spacing = LINE_SPACING
218
+
219
+ # Set East Asian font for Normal style
220
+ rPr = style._element.get_or_add_rPr()
221
+ rFonts = rPr.find(qn("w:rFonts"))
222
+ if rFonts is None:
223
+ rFonts = OxmlElement("w:rFonts")
224
+ rPr.insert(0, rFonts)
225
+ rFonts.set(qn("w:eastAsia"), FONT_BODY)
226
+
227
+ # Heading styles
228
+ for level in range(1, 7):
229
+ style_name = f"Heading {level}"
230
+ if style_name in doc.styles:
231
+ h_style = doc.styles[style_name]
232
+ h_font = h_style.font
233
+ h_font.name = FONT_HEADING
234
+ h_font.size = HEADING_SIZES.get(level, Pt(11))
235
+ h_font.color.rgb = HEADING_COLORS.get(level, COLOR_BODY_TEXT)
236
+ h_font.bold = True
237
+ h_pf = h_style.paragraph_format
238
+ h_pf.space_before = HEADING_SPACE_BEFORE
239
+ h_pf.space_after = HEADING_SPACE_AFTER
240
+ h_pf.keep_with_next = True
241
+
242
+ # East Asian font
243
+ h_rPr = h_style._element.get_or_add_rPr()
244
+ h_rFonts = h_rPr.find(qn("w:rFonts"))
245
+ if h_rFonts is None:
246
+ h_rFonts = OxmlElement("w:rFonts")
247
+ h_rPr.insert(0, h_rFonts)
248
+ h_rFonts.set(qn("w:eastAsia"), FONT_HEADING)
@@ -0,0 +1,182 @@
1
+ Metadata-Version: 2.4
2
+ Name: markdocx
3
+ Version: 0.1.0
4
+ Summary: Convert AI-generated Markdown textbooks to polished DOCX with native math equations and syntax-highlighted code
5
+ License-Expression: MIT
6
+ License-File: LICENSE
7
+ Requires-Python: >=3.14
8
+ Requires-Dist: latex2mathml==3.78.1
9
+ Requires-Dist: lxml==6.0.2
10
+ Requires-Dist: markdown-it-py==4.0.0
11
+ Requires-Dist: matplotlib==3.10.8
12
+ Requires-Dist: mdit-py-plugins==0.5.0
13
+ Requires-Dist: pillow==12.1.0
14
+ Requires-Dist: pygments==2.19.2
15
+ Requires-Dist: python-docx==1.2.0
16
+ Description-Content-Type: text/markdown
17
+
18
+ <div align="center">
19
+
20
+ # MD to DOCX
21
+
22
+ **A Markdown-to-Word converter built for AI-generated textbooks**
23
+
24
+ Convert Markdown files — complete with LaTeX math, syntax-highlighted code, tables, and images — into polished `.docx` documents in one command.
25
+
26
+ [![Python 3.10+](https://img.shields.io/badge/python-3.10%2B-blue.svg)](https://www.python.org/downloads/)
27
+ [![License: MIT](https://img.shields.io/badge/license-MIT-green.svg)](LICENSE)
28
+
29
+ </div>
30
+
31
+ ---
32
+
33
+ ## Why This Exists
34
+
35
+ Large language models (ChatGPT, Claude, Gemini, …) produce great Markdown, but the journey from `.md` to a well-formatted **Word document** is painful:
36
+
37
+ - LaTeX formulas become plain text or broken images
38
+ - Code blocks lose their highlighting
39
+ - Tables, lists, and blockquotes need manual reformatting
40
+
41
+ **MD to DOCX** bridges that gap. Feed it a Markdown file that follows a few simple rules and get a publication-ready `.docx` — math rendered as native Word OMML equations, code with VS Code–style colors, and everything else properly formatted.
42
+
43
+ ## Features
44
+
45
+ | Category | What you get |
46
+ |:---------|:-------------|
47
+ | **Math** | Inline (`$...$`) and display (`$$...$$`) LaTeX → native OMML equations in Word |
48
+ | **Code** | 30+ languages with Pygments syntax highlighting, VS Code light theme, language labels |
49
+ | **Tables** | Auto-formatted Table Grid — bold header row, left/center/right alignment, inline math in cells |
50
+ | **Lists** | Bullet (•◦▪) and numbered lists, up to 6 nesting levels |
51
+ | **Other** | Blockquotes, horizontal rules, clickable hyperlinks, local images, footnotes |
52
+
53
+ ## Quick Start
54
+
55
+ ### Installation
56
+
57
+ ```bash
58
+ git clone https://github.com/<your-username>/md_to_docx.git
59
+ cd md_to_docx
60
+
61
+ # Using uv (recommended)
62
+ uv sync
63
+
64
+ # Or using pip
65
+ pip install -r requirements.txt
66
+ ```
67
+
68
+ ### Usage
69
+
70
+ ```bash
71
+ # Convert a single file
72
+ python main.py input.md
73
+ python main.py input.md -o output.docx
74
+
75
+ # Convert an entire directory
76
+ python main.py ./chapters/ -o ./output/
77
+
78
+ # Recursively search subdirectories
79
+ python main.py ./chapters/ -o ./output/ -r
80
+
81
+ # Verbose logging
82
+ python main.py input.md -v
83
+ ```
84
+
85
+ ### CLI Options
86
+
87
+ | Flag | Description |
88
+ |:-----|:------------|
89
+ | `input` | Markdown file or directory to convert |
90
+ | `-o, --output` | Output file or directory path |
91
+ | `-r, --recursive` | Recursively find `.md` files in subdirectories |
92
+ | `-v, --verbose` | Show detailed processing logs |
93
+
94
+ ## How It Works
95
+
96
+ ```
97
+ Markdown file
98
+
99
+
100
+ md_parser.py ─── markdown-it-py tokenizer
101
+
102
+
103
+ docx_builder.py ─── walks the token stream, builds Word elements
104
+ ├── math_renderer.py ─── LaTeX → MathML → OMML (native Word equations)
105
+ ├── code_renderer.py ─── Pygments lexer → colored Word runs
106
+ └── styles.py ─── fonts, colors, spacing presets
107
+
108
+
109
+ .docx file ─── python-docx output
110
+ ```
111
+
112
+ ### Math Pipeline
113
+
114
+ LaTeX is converted to **native OMML** (Office Math Markup Language), not images. This means formulas are editable, scale perfectly, and look like they were typed in Word's equation editor.
115
+
116
+ ```
117
+ LaTeX string → latex2mathml → MathML → XSLT → OMML → Word paragraph
118
+ ```
119
+
120
+ ### Code Pipeline
121
+
122
+ ```
123
+ Source code → Pygments lexer + VS Code theme → colored Word runs inside a shaded table cell
124
+ ```
125
+
126
+ ## Project Structure
127
+
128
+ ```
129
+ md_to_docx/
130
+ ├── main.py # CLI entry point
131
+ ├── pyproject.toml # Project metadata & dependencies
132
+ ├── requirements.txt # Pip-compatible dependency list
133
+ ├── converter/
134
+ │ ├── __init__.py
135
+ │ ├── core.py # Top-level orchestrator
136
+ │ ├── md_parser.py # Markdown → token stream
137
+ │ ├── math_renderer.py # LaTeX → OMML (native Word math)
138
+ │ ├── code_renderer.py # Code → syntax-highlighted Word runs
139
+ │ ├── docx_builder.py # Token stream → DOCX elements
140
+ │ └── styles.py # Fonts, colors, and layout presets
141
+ └── rule/
142
+ ├── ai_gen_doc_rule.md # AI writing rules (Vietnamese)
143
+ └── ai_gen_doc_rule_en.md # AI writing rules (English)
144
+ ```
145
+
146
+ ## Dependencies
147
+
148
+ | Package | Version | Role |
149
+ |:--------|:--------|:-----|
150
+ | [python-docx](https://python-docx.readthedocs.io/) | 1.2.0 | DOCX generation |
151
+ | [markdown-it-py](https://github.com/executablebooks/markdown-it-py) | 4.0.0 | Markdown parsing |
152
+ | [mdit-py-plugins](https://github.com/executablebooks/mdit-py-plugins) | 0.5.0 | Math & footnote plugins |
153
+ | [latex2mathml](https://github.com/roniemartinez/latex2mathml) | 3.78.1 | LaTeX → MathML conversion |
154
+ | [lxml](https://lxml.de/) | 6.0.2 | XML/XSLT processing |
155
+ | [Pygments](https://pygments.org/) | 2.19.2 | Syntax highlighting |
156
+ | [matplotlib](https://matplotlib.org/) | 3.10.8 | LaTeX rendering (fallback) |
157
+ | [Pillow](https://python-pillow.org/) | 12.1.0 | Image processing |
158
+
159
+ ## AI Writing Rules
160
+
161
+ The `rule/` directory contains detailed guidelines for prompting AI models to produce Markdown that converts cleanly:
162
+
163
+ | File | Language | Description |
164
+ |:-----|:---------|:------------|
165
+ | `rule/ai_gen_doc_rule.md` | Vietnamese | Full rule set — heading structure, LaTeX constraints, code block format, tables, etc. |
166
+ | `rule/ai_gen_doc_rule_en.md` | English | Same rules, English version |
167
+
168
+ **How to use:** Paste the contents of the appropriate rule file into your AI system prompt (or at the start of the conversation) before asking it to write textbook content.
169
+
170
+ ## Contributing
171
+
172
+ Contributions are welcome. Please open an issue first to discuss what you'd like to change.
173
+
174
+ 1. Fork the repository
175
+ 2. Create a feature branch (`git checkout -b feature/amazing-feature`)
176
+ 3. Commit your changes (`git commit -m 'Add amazing feature'`)
177
+ 4. Push to the branch (`git push origin feature/amazing-feature`)
178
+ 5. Open a Pull Request
179
+
180
+ ## License
181
+
182
+ This project is licensed under the MIT License — see the [LICENSE](LICENSE) file for details.
@@ -0,0 +1,13 @@
1
+ markdocx/__init__.py,sha256=W3f27F3zCfAPH1b-ClQshSOETF_HOMJmEPl1Z82C_xo,269
2
+ markdocx/cli.py,sha256=8Z2ImBMOlfPvqs88qrKjHc5qicF3mj8mo7ya86ZYku4,3615
3
+ markdocx/code_renderer.py,sha256=u4fD_oeHu6XWG_VDWQNUCErTavL_sbVfY8iKSeb5hsY,4643
4
+ markdocx/core.py,sha256=9zKaj8HYsXp2mUED1pI-MMWpUJ8c5MOsBW3dJuzIFoc,3831
5
+ markdocx/docx_builder.py,sha256=2O9zn3hvTxHxVT_PJgvmk42ju3E4cp7tqyryVEzJ95M,28842
6
+ markdocx/math_renderer.py,sha256=-rlzc6scwDcpnROKQ-yLBVOnlyMQbJlbegYykdCKyaM,16830
7
+ markdocx/md_parser.py,sha256=itYFmkIQ7kAnAKYfUAE7b5osIEJ1wFX1yZmMCJbV--0,1372
8
+ markdocx/styles.py,sha256=s2LP3h18WyHH7Fw8J7T7IQIED543AYav5u8sfUoesEA,9809
9
+ markdocx-0.1.0.dist-info/METADATA,sha256=aLw-AofOVZGVCzxgKNCL4TQTpFz5sTTAQ_KJ1idJfTU,6533
10
+ markdocx-0.1.0.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
11
+ markdocx-0.1.0.dist-info/entry_points.txt,sha256=OwL_Kigr4PsJyDVXFzVE9TooU1bN7DyAil8Il0l1P1I,47
12
+ markdocx-0.1.0.dist-info/licenses/LICENSE,sha256=gECgcvYlt5qm4TtDOUfhWot49Ki-0PJUe23z1BIZC4c,1065
13
+ markdocx-0.1.0.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: hatchling 1.28.0
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ markdocx = markdocx.cli:main
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 shynneri
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.