1# Copyright (c) 2021 Manfred Moitzi 2# License: MIT License 3from pathlib import Path 4import ezdxf 5from ezdxf.tools.text import ( 6 MTextEditor, ParagraphProperties, MTextParagraphAlignment, 7) 8from ezdxf.tools.text_layout import lorem_ipsum 9 10OUTBOX = Path("~/Desktop/Outbox").expanduser() 11ATTRIBS = { 12 "char_height": 0.7, 13 "style": "OpenSans", 14} 15 16# use constants defined in MTextEditor: 17NP = MTextEditor.NEW_PARAGRAPH 18 19 20def recreate_mtext_py_example(msp, location): 21 # replicate example "mtext.py": 22 attribs = dict(ATTRIBS) 23 attribs["width"] = 15.0 24 editor = MTextEditor(f"recreate mtext.py result:{NP}normal ").overline( 25 "over line").append(" normal" + NP + "normal ").strike_through( 26 "strike through").append(" normal" + NP).underline( 27 "under line").append(" normal") 28 msp.add_mtext(str(editor), attribs).set_location(insert=location) 29 30 31def using_colors(msp, location): 32 attribs = dict(ATTRIBS) 33 attribs["width"] = 10.0 34 editor = MTextEditor("using colors:" + NP) 35 # Change colors by name: red, green, blue, yellow, cyan, magenta, white 36 editor.color("red").append("RED" + NP) 37 # The color stays the same until changed 38 editor.append("also RED" + NP) 39 # Change color by ACI (AutoCAD Color Index) 40 editor.aci(3).append("GREEN" + NP) 41 # Change color by RGB tuples 42 editor.rgb((0, 0, 255)).append("BLUE" + NP) 43 msp.add_mtext(str(editor), attribs).set_location(insert=location) 44 45 46def changing_text_height_absolute(msp, location): 47 attribs = dict(ATTRIBS) 48 attribs["width"] = 40.0 # need mor space to avoid text wrapping 49 editor = MTextEditor( 50 "changing text height absolute: default height is 0.7" + NP) 51 # doubling the default height = 1.4 52 editor.height(1.4) 53 editor.append("text height: 1.4" + NP) 54 editor.height(3.5).append("text height: 3.5" + NP) 55 editor.height(0.7).append("back to default height: 0.7" + NP) 56 msp.add_mtext(str(editor), attribs).set_location(insert=location) 57 58 59def changing_text_height_relative(msp, location): 60 attribs = dict(ATTRIBS) 61 attribs["width"] = 40.0 # need mor space to avoid text wrapping 62 editor = MTextEditor( 63 "changing text height relative: default height is 0.7" + NP) 64 # this is the default text height in the beginning: 65 current_height = attribs["char_height"] 66 # The text height can only be changed by a factor: 67 editor.scale_height(2) # scale by 2 = 1.4 68 # keep track of the actual height: 69 current_height *= 2 70 editor.append("text height: 1.4" + NP) 71 # to set an absolute height, calculate the required factor: 72 desired_height = 3.5 73 factor = desired_height / current_height 74 editor.scale_height(factor).append("text height: 3.5" + NP) 75 current_height = desired_height 76 # and back to 0.7 77 editor.scale_height(0.7 / current_height).append( 78 "back to default height: 0.7" + NP) 79 msp.add_mtext(str(editor), attribs).set_location(insert=location) 80 81 82def changing_fonts(msp, location): 83 attribs = dict(ATTRIBS) 84 attribs["width"] = 15.0 85 editor = MTextEditor("changing fonts:" + NP) 86 editor.append("Default: Hello World!" + NP) 87 editor.append("SimSun: ") 88 # The font name for changing MTEXT fonts inline is the font family name! 89 # The font family name is the name shown in font selection widgets in 90 # desktop applications: "Arial", "Times New Roman", "Comic Sans MS" 91 # 92 # change font in a group to revert back to the default font at the end: 93 simsun_editor = MTextEditor().font("SimSun").append("你好,世界" + NP) 94 # reverts the font back at the end of the group: 95 editor.group(str(simsun_editor)) 96 # back to default font OpenSans: 97 editor.append("Times New Roman: ") 98 # change font outside of a group until next font change: 99 editor.font("Times New Roman").append("Привет мир!" + NP) 100 # If the font does not exist, a replacement font will be used: 101 editor.font("Does not exist").append("This is the replacement font!") 102 msp.add_mtext(str(editor), attribs).set_location(insert=location) 103 104 105def indent_first_line(msp, location): 106 # Indentation is a multiple of the default text height (MTEXT char_height) 107 attribs = dict(ATTRIBS) 108 attribs["char_height"] = 0.25 109 attribs["width"] = 7.5 110 editor = MTextEditor("Indent the first line:" + NP) 111 props = ParagraphProperties( 112 indent=1, # indent first line = 1x0.25 drawing units 113 align=MTextParagraphAlignment.JUSTIFIED 114 ) 115 editor.paragraph(props) 116 editor.append(" ".join(lorem_ipsum(100))) 117 msp.add_mtext(str(editor), attribs).set_location(insert=location) 118 119 120def indent_except_fist_line(msp, location): 121 # Indentation is a multiple of the default text height (MTEXT char_height) 122 attribs = dict(ATTRIBS) 123 attribs["char_height"] = 0.25 124 attribs["width"] = 7.5 125 editor = MTextEditor("Indent left paragraph side:" + NP) 126 indent = 0.7 # 0.7 * 0.25 = 0.175 drawing units 127 props = ParagraphProperties( 128 # first line indentation is relative to "left", this reverses the 129 # left indentation: 130 indent=-indent, # first line 131 # indent left paragraph side: 132 left=indent, 133 align=MTextParagraphAlignment.JUSTIFIED 134 ) 135 editor.paragraph(props) 136 editor.append(" ".join(lorem_ipsum(100))) 137 msp.add_mtext(str(editor), attribs).set_location(insert=location) 138 139 140def bullet_list(msp, location): 141 attribs = dict(ATTRIBS) 142 attribs["char_height"] = 0.25 143 attribs["width"] = 7.5 144 # There are no special commands to build bullet list, the list is build of 145 # indentation and a tabulator stop. Each list item needs a marker as an 146 # arbitrary string. 147 bullet = "•" # alt + numpad 7 148 editor = MTextEditor("Bullet List:" + NP) 149 editor.bullet_list( 150 indent=1, 151 bullets=[bullet] * 3, # each list item needs a marker 152 content=[ 153 "First item", 154 "Second item", 155 " ".join(lorem_ipsum(30)), 156 ]) 157 msp.add_mtext(str(editor), attribs).set_location(insert=location) 158 159 160def numbered_list(msp, location): 161 attribs = dict(ATTRIBS) 162 attribs["char_height"] = 0.25 163 attribs["width"] = 7.5 164 # There are no special commands to build numbered list, the list is build of 165 # indentation and a tabulator stop. There is no automatic numbering, 166 # but therefore the absolute freedom for using any string as list marker: 167 editor = MTextEditor("Numbered List:" + NP) 168 editor.bullet_list( 169 indent=1, 170 bullets=["1.", "2.", "3."], 171 content=[ 172 "First item", 173 "Second item", 174 " ".join(lorem_ipsum(30)), 175 ]) 176 # Indentation and tab stops are multiples of the default text height (MTEXT 177 # char_height)! 178 msp.add_mtext(str(editor), attribs).set_location(insert=location) 179 180 181def stacking(msp, location): 182 attribs = dict(ATTRIBS) 183 attribs["char_height"] = 0.25 184 attribs["width"] = 4 185 editor = MTextEditor("Stacked text:" + NP) 186 187 # place fraction with down scaled text height in a group: 188 stack = MTextEditor().scale_height(0.6).stack("1", "2", "^") 189 editor.append("over: ").group(str(stack)).append(NP) 190 191 stack = MTextEditor().scale_height(0.6).stack("1", "2", "/") 192 editor.append("fraction: ").group(str(stack)).append(NP) 193 194 stack = MTextEditor().scale_height(0.6).stack("1", "2", "#") 195 editor.append("slanted: ").group(str(stack)).append(NP) 196 197 # additional formatting in numerator and denominator is not supported 198 # by AutoCAD or BricsCAD. 199 # switching colors inside the fraction to red does not work: 200 numerator = MTextEditor().color("red").append("1") 201 stack = MTextEditor().scale_height(0.6).stack(str(numerator), "2", "#") 202 editor.append("color red: ").group(str(stack)).append(NP) 203 msp.add_mtext(str(editor), attribs).set_location(insert=location) 204 205 206def create(dxfversion): 207 """ 208 Important: 209 210 MTEXT FORMATTING IS NOT PORTABLE ACROSS CAD APPLICATIONS! 211 212 Inline MTEXT codes are not supported by every CAD application and even 213 if inline codes are supported the final rendering may vary. 214 Inline codes are very well supported by AutoCAD (of course!) and BricsCAD, 215 but don't expect the same rendering in other CAD applications. 216 217 The drawing add-on of ezdxf may support some features in the future, 218 but very likely with a different rendering result than AutoCAD/BricsCAD. 219 220 """ 221 doc = ezdxf.new(dxfversion, setup=True) 222 msp = doc.modelspace() 223 recreate_mtext_py_example(msp, location=(0, 0)) 224 using_colors(msp, location=(0, 10)) 225 changing_text_height_absolute(msp, location=(0, 25)) 226 changing_text_height_relative(msp, location=(0, 40)) 227 changing_fonts(msp, location=(15, 14)) 228 indent_first_line(msp, location=(15, 6)) 229 indent_except_fist_line(msp, location=(24, 6)) 230 bullet_list(msp, location=(33, 6)) 231 numbered_list(msp, location=(33, 2)) 232 stacking(msp, location=(33, 14)) 233 doc.set_modelspace_vport(height=60, center=(15, 15)) 234 return doc 235 236 237for dxfversion in ["R2000", "R2004", "R2007", "R2010", "R2013", "R2018"]: 238 doc = create(dxfversion) 239 filename = f"mtext_editor_{dxfversion}.dxf" 240 doc.saveas(OUTBOX / filename) 241 print(f"saved {filename}") 242