Dear Scott and Atiefenbach,
Wow, thank you very much indeed for your valuable additional research.
That is more than I could have hoped for.
It sounds like this provides the basis for a pretty complete solution.
I implemented this partially as follows:
[Transaction( TransactionMode.Manual )]
class CmdNewTextNote : IExternalCommand
{
[DllImport( "user32.dll" )]
private static extern IntPtr GetDC( IntPtr hwnd );
[DllImport( "user32.dll" )]
private static extern Int32 ReleaseDC( IntPtr hwnd );
/// <summary>
/// Determine the current display
/// horizontal dots per inch.
/// </summary>
static float DpiX
{
get
{
Single xDpi, yDpi;
IntPtr dc = GetDC( IntPtr.Zero );
using( Graphics g = Graphics.FromHdc( dc ) )
{
xDpi = g.DpiX;
yDpi = g.DpiY;
}
if( ReleaseDC( IntPtr.Zero ) != 0 )
{
// GetLastError and handle...
}
return xDpi;
}
}
public Result Execute(
ExternalCommandData commandData,
ref string message,
ElementSet elements )
{
UIApplication uiapp = commandData.Application;
UIDocument uidoc = uiapp.ActiveUIDocument;
Document doc = uidoc.Document;
View view = doc.ActiveView;
XYZ p;
try
{
p = uidoc.Selection.PickPoint(
"Please pick text insertion point" );
}
catch( Autodesk.Revit.Exceptions.OperationCanceledException )
{
return Result.Cancelled;
}
TextNoteType textType
= new FilteredElementCollector( doc )
.OfClass( typeof( TextNoteType ) )
.FirstElement() as TextNoteType;
Debug.Print( "TextNoteType.Name = " + textType.Name );
// 6 mm Arial happens to be the first text type found
// 6 mm = 6 / 25.4 inch = 72 * 6 / 25.4 points = 17 pt.
// Nowadays, Windows does not assume that a point is
// 1/72", but moved to 1/96" instead.
float text_type_height_mm = 6;
float mm_per_inch = 25.4f;
float points_per_inch = 96; // not 72
float em_size = points_per_inch
* ( text_type_height_mm / mm_per_inch );
em_size += 2.5f;
Font font = new Font( "Arial", em_size,
FontStyle.Regular );
using( Transaction t = new Transaction( doc ) )
{
t.Start( "Create TextNote" );
string s = "The quick brown fox jumps over the lazy dog";
Size txtBox = System.Windows.Forms.TextRenderer
.MeasureText( s, font );
double w_inch = txtBox.Width / DpiX;
double v_scale = view.Scale; // ratio of true model size to paper size
Debug.Print(
"Text box width in pixels {0} = {1} inch, "
+ "view scale = {2}",
txtBox.Width, w_inch, v_scale );
double newWidth = w_inch / 12;
TextNote txNote = doc.Create.NewTextNote(
doc.ActiveView, p, XYZ.BasisX, XYZ.BasisY,
newWidth, TextAlignFlags.TEF_ALIGN_LEFT
| TextAlignFlags.TEF_ALIGN_BOTTOM, s );
txNote.TextNoteType = textType;
Debug.Print(
"NewTextNote lineWidth {0} times view scale "
+ "{1} = {2} generated TextNote.Width {3}",
Util.RealString( newWidth ),
Util.RealString( v_scale ),
Util.RealString( newWidth * v_scale ),
Util.RealString( txNote.Width ) );
// This fails.
//Debug.Assert(
// Util.IsEqual( newWidth * v_scale, txNote.Width ),
// "expected the NewTextNote lineWidth "
// + "argument to determine the resulting "
// + "text note width" );
txNote.Width = newWidth * v_scale;
t.Commit();
}
return Result.Succeeded;
}
}
Does that match your intentions and explanation, Scott?
I do not see where to obtain the "text type's Width Factor" that you mention. Where is that stored?
Furthermore, what element hosts the BuiltInParameter.LEADER_OFFSET_SHEET?
It sounds like you tested this for varous different fonts and text string samples.
Have you written some kind of test framework iterating over the available text note types, matching them with .NET fonts and trying them out with different strings?
In order to use this approach in a serious production environemnt, I would try to set up and use such a system and a corresponding unit test to verify that the chosen adjustments really do work correctly in all cases encountered.
Thank you!
Best regards,
Jeremy