Kean Walmsley


  • About the Author
    Kean on Google+

July 2014

Sun Mon Tue Wed Thu Fri Sat
    1 2 3 4 5
6 7 8 9 10 11 12
13 14 15 16 17 18 19
20 21 22 23 24 25 26
27 28 29 30 31    








« A handy jig for creating AutoCAD text using .NET – Part 1 | Main | A handy jig for creating AutoCAD text using .NET – Part 3 »

August 13, 2012

A handy jig for creating AutoCAD text using .NET – Part 2

In the last post, we saw a simple jig implementation to position, size and rotate standard AutoCAD text. In this post, we’re extending that implementation to handle font properties such as bold and italic text.

At first blush, this sounds pretty straightforward – how hard can it be, right? The complexity gets introduced when we consider that these are not properties that are exposed directly from the text itself, but from the associated text style (or – to be more accurate – from the font descriptor object associated with the text style).

Which begs the question: when the user chooses to change the text to bold, what is it they really want?

  1. To change the associated text style, so that all text that uses it becomes bold
  2. To use (or create) a different text style with the bold property set

It’s pretty clear to me that they actually want 2 rather than 1, but then the question gets raised of how we name and identify these additional text styles. I’ve gone with the following scheme:

  • Regular: “STANDARD” (e.g. we take the default, initial style)
  • Bold: “STANDARD_BOLD” (i.e. we add a suffix)
  • Italic: “STANDARD_ITALIC”
  • Bold-italic: “STANDARD_BOLDITALIC”

If this is in some ways at odds with your own naming scheme, it’s a simple matter to change it in the code.

As mentioned in the previous post, the “Bold” and “Italic” keywords are actually toggles, which allows us to avoid adding a “Regular” keyword, too.

Here is the updated C# code, with the new lines 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 

   83     // Constructor

   84 

   85     public TextPlacementJig(

   86       Transaction tr, Database db, Entity ent

   87     ) : base(ent)

   88     {

   89       _db = db;

   90       _tr = tr;

   91       _angle = 0;

   92       _txtSize = 1;

   93     }

   94 

   95     protected override SamplerStatus Sampler(

   96       JigPrompts jp

   97     )

   98     {

   99       // We acquire a point but with keywords

  100 

  101       JigPromptPointOptions po =

  102         new JigPromptPointOptions(

  103           "\nPosition of text"

  104         );

  105 

  106       po.UserInputControls =

  107         (UserInputControls.Accept3dCoordinates |

  108           UserInputControls.NullResponseAccepted |

  109           UserInputControls.NoNegativeResponseAccepted |

  110           UserInputControls.GovernedByOrthoMode);

  111 

  112       po.SetMessageAndKeywords(

  113         "\nSpecify position of text or " +

  114         "[Bold/Italic/LArger/Smaller/" +

  115          "ROtate90/LEft/Middle/RIght]: ",

  116         "Bold Italic LArger Smaller " +

  117         "ROtate90 LEft Middle RIght"

  118       );

  119 

  120       PromptPointResult ppr = jp.AcquirePoint(po);

  121 

  122       if (ppr.Status == PromptStatus.Keyword)

  123       {

  124         switch (ppr.StringResult)

  125         {

  126           case "Bold":

  127             {

  128               _toggleBold = true;

  129               break;

  130             }

  131           case "Italic":

  132             {

  133               _toggleItalic = true;

  134               break;

  135             }

  136           case "LArger":

  137             {

  138               // Multiple the text size by two

  139 

  140               _txtSize *= 2;

  141               break;

  142             }

  143           case "Smaller":

  144             {

  145               // Divide the text size by two

  146 

  147               _txtSize /= 2;

  148               break;

  149             }

  150           case "ROtate90":

  151             {

  152               // To rotate clockwise we subtract 90 degrees and

  153               // then normalise the angle between 0 and 360

  154 

  155               _angle -= Math.PI / 2;

  156               while (_angle < Math.PI * 2)

  157               {

  158                 _angle += Math.PI * 2;

  159               }

  160               break;

  161             }

  162           case "LEft":

  163             {

  164               // TODO

  165 

  166               break;

  167             }

  168           case "RIght":

  169             {

  170               // TODO

  171 

  172               break;

  173             }

  174           case "Middle":

  175             {

  176               // TODO

  177 

  178               break;

  179             }

  180         }

  181 

  182         return SamplerStatus.OK;

  183       }

  184       else if (ppr.Status == PromptStatus.OK)

  185       {

  186         // Check if it has changed or not (reduces flicker)

  187 

  188         if (

  189           _position.DistanceTo(ppr.Value) <

  190             Tolerance.Global.EqualPoint

  191         )

  192           return SamplerStatus.NoChange;

  193 

  194         _position = ppr.Value;

  195         return SamplerStatus.OK;

  196       }

  197 

  198       return SamplerStatus.Cancel;

  199     }

  200 

  201     protected override bool Update()

  202     {

  203       // Set properties on our text object

  204 

  205       DBText txt = (DBText)Entity;

  206 

  207       txt.Position = _position;

  208       txt.Height = _txtSize;

  209       txt.Rotation = _angle;

  210 

  211       // Set the bold and/or italic properties on the style

  212 

  213       if (_toggleBold || _toggleItalic)

  214       {

  215         TextStyleTable tab =

  216           (TextStyleTable)_tr.GetObject(

  217             _db.TextStyleTableId, OpenMode.ForRead

  218           );

  219 

  220         TextStyleTableRecord style =

  221           (TextStyleTableRecord)_tr.GetObject(

  222             txt.TextStyleId, OpenMode.ForRead

  223           );

  224 

  225         // A bit convoluted, but this check will tell us

  226         // whether the new style is bold/italic

  227 

  228         bool bold = !(style.Font.Bold == _toggleBold);

  229         bool italic = !(style.Font.Italic == _toggleItalic);

  230         _toggleBold = false;

  231         _toggleItalic = false;

  232 

  233         // Get the new style name based on the old name and

  234         // a suffix ("_BOLD", "_ITALIC" or "_BOLDITALIC")

  235 

  236         var oldName = style.Name.Split(new[] { '_' });

  237         string newName =

  238           oldName[0] +

  239           (bold || italic ? "_" +

  240             (bold ? "BOLD" : "") +

  241             (italic ? "ITALIC" : "")

  242             : "");

  243 

  244         // We only create a duplicate style if one doesn't

  245         // already exist

  246 

  247         if (tab.Has(newName))

  248         {

  249           txt.TextStyleId = tab[newName];

  250         }

  251         else

  252         {

  253           // We have to create a new style - clone the old one

  254 

  255           TextStyleTableRecord newStyle =

  256             (TextStyleTableRecord)style.Clone();

  257 

  258           // Set a new name to avoid duplicate keys

  259 

  260           newStyle.Name = newName;

  261 

  262           // Create a new font based on the old one, but with

  263           // our values for bold & italic

  264 

  265           FontDescriptor oldFont = style.Font;

  266           FontDescriptor newFont =

  267             new FontDescriptor(

  268               oldFont.TypeFace, bold, italic,

  269               oldFont.CharacterSet, oldFont.PitchAndFamily

  270             );

  271 

  272           // Set it on the style

  273 

  274           newStyle.Font = newFont;

  275 

  276           // Add the new style to the text style table and

  277           // the transaction

  278 

  279           tab.UpgradeOpen();

  280           ObjectId styleId = tab.Add(newStyle);

  281           _tr.AddNewlyCreatedDBObject(newStyle, true);

  282 

  283           // And finally set the new style on our text object

  284 

  285           txt.TextStyleId = styleId;

  286         }

  287       }

  288 

  289       return true;

  290     }

  291   }

  292 }

When we run the modified QT command, we can see we now have the ability to adjust font formatting properties on-the-fly, with the results showing as we jig the text:

Command: QT

Enter text string: Some more text

 

Jigging our regular text

Specify position of text or

[Bold/Italic/LArger/Smaller/ROtate90/LEft/Middle/RIght]: BO

 

Making it bold

Specify position of text or

[Bold/Italic/LArger/Smaller/ROtate90/LEft/Middle/RIght]: IT

 

And italic

Specify position of text or

[Bold/Italic/LArger/Smaller/ROtate90/LEft/Middle/RIght]:

Then place it

If we take a look using ArxDbg, we can see we have a number of new text styles in the table (although not one for italic, as we haven't passed that way):

The text styles we've been using

This was the trickiest part of the implementation. In the next post we’ll wrap up with the code needed to align the text relative to the cursor during our jig.

blog comments powered by Disqus

10 Random Posts