Earlier in this series, we implemented a jig to rotate, size and place text more easily in an AutoCAD drawing, which we then extended to allow adjustment of font-level properties. In this post, we’re going to add some additional functionality to allow the text to be justified differently around the cursor location.
It has to be said that the code introduced in this post isn’t very extensive, but nonetheless tricky to get right (and therefore deserving of its own post). It’s also worth noting that the changes would be much more complicated if we hadn’t designed the code to add the text to the database prior to passing it to the jig: that’s a pre-requisite for the code in this post to work properly.
The primary property we want to modify is DBText.HorizontalMode. We will set this to the following items in the TextHorizontalMode enumeration, depending on the keyword chosen: TextLeft, TextMid and TextRight. That’s the easy bit: the tricky part is that we also need to change the AlignmentPoint property, in the case where the HorizontalMode is other than TextLeft, and then call AdjustAlignment().
Here’s the updated C# code, with new lines marked in red (and here’s the source file for you to download):
1 using Autodesk.AutoCAD.ApplicationServices;
2 using Autodesk.AutoCAD.DatabaseServices;
3 using Autodesk.AutoCAD.EditorInput;
4 using Autodesk.AutoCAD.Geometry;
5 using Autodesk.AutoCAD.GraphicsInterface;
6 using Autodesk.AutoCAD.Runtime;
7 using System;
8
9 public class Commands
10 {
11 [CommandMethod("QT")]
12 static public void QuickText()
13 {
14 Document doc =
15 Application.DocumentManager.MdiActiveDocument;
16 Database db = doc.Database;
17 Editor ed = doc.Editor;
18
19 PromptStringOptions pso =
20 new PromptStringOptions("\nEnter text string");
21 pso.AllowSpaces = true;
22 PromptResult pr = ed.GetString(pso);
23
24 if (pr.Status != PromptStatus.OK)
25 return;
26
27 Transaction tr =
28 doc.TransactionManager.StartTransaction();
29 using (tr)
30 {
31 BlockTableRecord btr =
32 (BlockTableRecord)tr.GetObject(
33 db.CurrentSpaceId, OpenMode.ForWrite
34 );
35
36 // Create the text object, set its normal and contents
37
38 DBText txt = new DBText();
39 txt.Normal =
40 ed.CurrentUserCoordinateSystem.
41 CoordinateSystem3d.Zaxis;
42 txt.TextString = pr.StringResult;
43
44 // We'll add the text to the database before jigging
45 // it - this allows alignment adjustments to be
46 // reflected
47
48 btr.AppendEntity(txt);
49 tr.AddNewlyCreatedDBObject(txt, true);
50
51 // Create our jig
52
53 TextPlacementJig pj = new TextPlacementJig(tr, db, txt);
54
55 // Loop as we run our jig, as we may have keywords
56
57 PromptStatus stat = PromptStatus.Keyword;
58 while (stat == PromptStatus.Keyword)
59 {
60 PromptResult res = ed.Drag(pj);
61 stat = res.Status;
62 if (
63 stat != PromptStatus.OK &&
64 stat != PromptStatus.Keyword
65 )
66 return;
67 }
68
69 tr.Commit();
70 }
71 }
72
73 class TextPlacementJig : EntityJig
74 {
75 // Declare some internal state
76
77 Database _db;
78 Transaction _tr;
79 Point3d _position;
80 double _angle, _txtSize;
81 bool _toggleBold, _toggleItalic;
82 TextHorizontalMode _align;
83
84 // Constructor
85
86 public TextPlacementJig(
87 Transaction tr, Database db, Entity ent
88 ) : base(ent)
89 {
90 _db = db;
91 _tr = tr;
92 _angle = 0;
93 _txtSize = 1;
94 }
95
96 protected override SamplerStatus Sampler(
97 JigPrompts jp
98 )
99 {
100 // We acquire a point but with keywords
101
102 JigPromptPointOptions po =
103 new JigPromptPointOptions(
104 "\nPosition of text"
105 );
106
107 po.UserInputControls =
108 (UserInputControls.Accept3dCoordinates |
109 UserInputControls.NullResponseAccepted |
110 UserInputControls.NoNegativeResponseAccepted |
111 UserInputControls.GovernedByOrthoMode);
112
113 po.SetMessageAndKeywords(
114 "\nSpecify position of text or " +
115 "[Bold/Italic/LArger/Smaller/" +
116 "ROtate90/LEft/Middle/RIght]: ",
117 "Bold Italic LArger Smaller " +
118 "ROtate90 LEft Middle RIght"
119 );
120
121 PromptPointResult ppr = jp.AcquirePoint(po);
122
123 if (ppr.Status == PromptStatus.Keyword)
124 {
125 switch (ppr.StringResult)
126 {
127 case "Bold":
128 {
129 _toggleBold = true;
130 break;
131 }
132 case "Italic":
133 {
134 _toggleItalic = true;
135 break;
136 }
137 case "LArger":
138 {
139 // Multiple the text size by two
140
141 _txtSize *= 2;
142 break;
143 }
144 case "Smaller":
145 {
146 // Divide the text size by two
147
148 _txtSize /= 2;
149 break;
150 }
151 case "ROtate90":
152 {
153 // To rotate clockwise we subtract 90 degrees and
154 // then normalise the angle between 0 and 360
155
156 _angle -= Math.PI / 2;
157 while (_angle < Math.PI * 2)
158 {
159 _angle += Math.PI * 2;
160 }
161 break;
162 }
163 case "LEft":
164 {
165 _align = TextHorizontalMode.TextLeft;
166 break;
167 }
168 case "RIght":
169 {
170 _align = TextHorizontalMode.TextRight;
171 break;
172 }
173 case "Middle":
174 {
175 _align = TextHorizontalMode.TextMid;
176 break;
177 }
178 }
179
180 return SamplerStatus.OK;
181 }
182 else if (ppr.Status == PromptStatus.OK)
183 {
184 // Check if it has changed or not (reduces flicker)
185
186 if (
187 _position.DistanceTo(ppr.Value) <
188 Tolerance.Global.EqualPoint
189 )
190 return SamplerStatus.NoChange;
191
192 _position = ppr.Value;
193 return SamplerStatus.OK;
194 }
195
196 return SamplerStatus.Cancel;
197 }
198
199 protected override bool Update()
200 {
201 // Set properties on our text object
202
203 DBText txt = (DBText)Entity;
204
205 txt.Position = _position;
206 txt.Height = _txtSize;
207 txt.Rotation = _angle;
208 txt.HorizontalMode = _align;
209 if (_align != TextHorizontalMode.TextLeft)
210 {
211 txt.AlignmentPoint = _position;
212 txt.AdjustAlignment(_db);
213 }
214
215 // Set the bold and/or italic properties on the style
216
217 if (_toggleBold || _toggleItalic)
218 {
219 TextStyleTable tab =
220 (TextStyleTable)_tr.GetObject(
221 _db.TextStyleTableId, OpenMode.ForRead
222 );
223
224 TextStyleTableRecord style =
225 (TextStyleTableRecord)_tr.GetObject(
226 txt.TextStyleId, OpenMode.ForRead
227 );
228
229 // A bit convoluted, but this check will tell us
230 // whether the new style is bold/italic
231
232 bool bold = !(style.Font.Bold == _toggleBold);
233 bool italic = !(style.Font.Italic == _toggleItalic);
234 _toggleBold = false;
235 _toggleItalic = false;
236
237 // Get the new style name based on the old name and
238 // a suffix ("_BOLD", "_ITALIC" or "_BOLDITALIC")
239
240 var oldName = style.Name.Split(new[] { '_' });
241 string newName =
242 oldName[0] +
243 (bold || italic ? "_" +
244 (bold ? "BOLD" : "") +
245 (italic ? "ITALIC" : "")
246 : "");
247
248 // We only create a duplicate style if one doesn't
249 // already exist
250
251 if (tab.Has(newName))
252 {
253 txt.TextStyleId = tab[newName];
254 }
255 else
256 {
257 // We have to create a new style - clone the old one
258
259 TextStyleTableRecord newStyle =
260 (TextStyleTableRecord)style.Clone();
261
262 // Set a new name to avoid duplicate keys
263
264 newStyle.Name = newName;
265
266 // Create a new font based on the old one, but with
267 // our values for bold & italic
268
269 FontDescriptor oldFont = style.Font;
270 FontDescriptor newFont =
271 new FontDescriptor(
272 oldFont.TypeFace, bold, italic,
273 oldFont.CharacterSet, oldFont.PitchAndFamily
274 );
275
276 // Set it on the style
277
278 newStyle.Font = newFont;
279
280 // Add the new style to the text style table and
281 // the transaction
282
283 tab.UpgradeOpen();
284 ObjectId styleId = tab.Add(newStyle);
285 _tr.AddNewlyCreatedDBObject(newStyle, true);
286
287 // And finally set the new style on our text object
288
289 txt.TextStyleId = styleId;
290 }
291 }
292
293 return true;
294 }
295 }
296 }
When we run the updated QT command…
Command: QT
Enter text string: Yet more text
Specify position of text or
[Bold/Italic/LArger/Smaller/ROtate90/LEft/Middle/RIght]: M
Specify position of text or
[Bold/Italic/LArger/Smaller/ROtate90/LEft/Middle/RIght]: RI
Specify position of text or
[Bold/Italic/LArger/Smaller/ROtate90/LEft/Middle/RIght]: I
Specify position of text or
[Bold/Italic/LArger/Smaller/ROtate90/LEft/Middle/RIght]: RO
Specify position of text or
[Bold/Italic/LArger/Smaller/ROtate90/LEft/Middle/RIght]: RO
Specify position of text or
[Bold/Italic/LArger/Smaller/ROtate90/LEft/Middle/RIght]: RO
Specify position of text or
[Bold/Italic/LArger/Smaller/ROtate90/LEft/Middle/RIght]: S
Specify position of text or
[Bold/Italic/LArger/Smaller/ROtate90/LEft/Middle/RIght]:
The overall functionality seems very useful (thanks for the suggestion, Thomas! :-) and certainly streamlines the creation of simple text inside a drawing.
That’s all I currently have planned for this series of posts, but be sure to let me know if there’s functionality you would like to see added (something related to Annotation Scaling, perhaps?).