Finding the coordinates of a character in a text

Finding the coordinates of a character in a text

neam
Collaborator Collaborator
1,004 Views
7 Replies
Message 1 of 8

Finding the coordinates of a character in a text

neam
Collaborator
Collaborator

Hi every body:

I tried to split a text into two parts at the location of one character.
But I could not find the insertion point of the second text as an acceptable approximation.
The coordinates of the green point are approximate.
Thank you for guiding me.

0 Likes
Accepted solutions (2)
1,005 Views
7 Replies
Replies (7)
Message 2 of 8

hak_vz
Advisor
Advisor

Check this solution from @Kent1Cooper  for splitting mtext at position of three spaces

 

To split at position of ; code has to be modified something like:

(defun C:SM2
  (/ ss n mt mto1 txtinit inspt rot wid dsp mto2 wid1 wid2)
  (if (setq ss (ssget ":L" '((0 . "MTEXT"))))
    (repeat (setq n (sslength ss))
      (setq
        mt (ssname ss (setq n (1- n))); entity name [for Copy below]
        mto1 (vlax-ename->vla-object mt); as VLA object
        txtinit (vla-get-TextString mto1)
        inspt (vlax-get mto1 'InsertionPoint)
        rot (vla-get-rotation mto1)
        wid (cdr (assoc 42 (entget mt))); [not available as VLA property]
        dsp (vl-string-search ";" txtinit); double space position
      ); setq
      (command "_.copy" mt "" '(0 0) "")
      (setq mto2 (vlax-ename->vla-object (entlast)))
      (vla-put-TextString mto1 (substr txtinit 1 dsp)); string before double space
      (vla-put-TextString mto2 (substr txtinit (+ dsp 2))); string after double space
      (redraw mt); needed to get reduced widths correctly [otherwise uses original]
      (redraw (entlast)); likewise
      (setq
        wid1 (cdr (assoc 42 (entget mt))); reduced width of original
        wid2 (cdr (assoc 42 (entget (entlast)))); width of after-spaces piece
      ); setq
      (vlax-put mto1 'insertionpoint (polar inspt (+ rot pi) (/ (- wid wid1) 2)))
      (vlax-put mto2 'insertionpoint (polar inspt rot (/ (- wid wid2) 2)))
    ); repeat
  ); if
  (princ)
); defun

You may also need convert from text to mtext

(defun c:t2m ( / *error* ss i )
	(defun *error* ()
		(setvar 'cmdecho 1)
		(princ)
	)
	(setq ss (ssget '((0 . "TEXT"))) i -1)
	(setvar 'cmdecho 0)
	(while (< (setq i (1+ i)) (sslength ss))
		(command "_.txt2mtxt" (ssname ss i) "")
	)
	(setvar 'cmdecho 1)
	(princ "\nDone!")
	(princ)
)

Miljenko Hatlak

EESignature

Did you find this post helpful? Feel free to Like this post.
Did your question get successfully answered? Then click on the ACCEPT SOLUTION button.
0 Likes
Message 3 of 8

neam
Collaborator
Collaborator

dear hak_vz

thank you for reply

Points that were unclear to me:

1)The desired character is not always the same.
It is better to type the character by the user.

 

2)Why must it be mtext?
3)And that the separate texts are not inserted in their place on the main text.

0 Likes
Message 4 of 8

hak_vz
Advisor
Advisor

Mtext has property width that is not present in text object (key 42). One may use textbox function for this if using text.

This code also considers text rotation. Code can be modified to accept different character as split and to split original to more than two parts.

 

Miljenko Hatlak

EESignature

Did you find this post helpful? Feel free to Like this post.
Did your question get successfully answered? Then click on the ACCEPT SOLUTION button.
0 Likes
Message 5 of 8

neam
Collaborator
Collaborator

You might want to take a look at my Lisp.

Of course, I know that is a complete beginner.

0 Likes
Message 6 of 8

Kent1Cooper
Consultant
Consultant
Accepted solution

Here's a modification of my code that @hak_vz posted, to allow Text or Mtext and to ask the User for the character at which to split the original.  Lightly tested in your sample drawing.  As in its original version, it actually splits the object into two parts, but it could be made to only find the position of the separator character instead [within a small variability].

 

(defun C:ST2C ; = Split Text/Mtext into 2 pieces around specified Character
;; starting from top-/middle-/bottom-LEFT justifications,
  (/ bbwid char ss n txt txto1 txtinit inspt rot wid charp txto2)
  (defun bbwid (obj / minpt maxpt)
    (vla-getboundingbox obj 'minpt 'maxpt)
    (- (car (vlax-safearray->list maxpt)) (car (vlax-safearray->list minpt)))
  ); defun -- bbwid
  (while
    (not
      (and
        (setq char (getstring "\nSingle character for break location: "))
        (= (strlen char) 1)
      ); and
    ); not
    (prompt "\nMust be one character only.")
  ); while
  (if (setq ss (ssget ":L" (list '(0 . "*TEXT") (cons 1 (strcat "*" char "*"))))); Text or Mtext
    (repeat (setq n (sslength ss))
      (setq
        txt (ssname ss (setq n (1- n))); entity name [for Copy below]
        txto1 (vlax-ename->vla-object txt); as VLA object
        txtinit (vla-get-TextString txto1); initial text content
        inspt (vlax-get txto1 'InsertionPoint)
        rot (vla-get-rotation txto1)
        wid
          (if (= (vla-get-ObjectName txto1) "AcDbText")
            (bbwid txto1); then [Text]
            (cdr (assoc 42 (entget txt))); [not available as VLA property]; else [Mtext]
          ); if
        charp (vl-string-search char txtinit); character position
      ); setq
      (command "_.copy" txt "" '(0 0) "")
      (setq txto2 (vlax-ename->vla-object (entlast)))
      (vla-put-TextString txto1 (substr txtinit 1 charp))
        ; string before designated character plus one space
      (vla-put-TextString txto2 (substr txtinit (1+ charp))); string from designated character
      (redraw txt); needed to get reduced widths correctly [otherwise uses original]
      (setq
        wid ; reduced width of original to before character
          (if (= (vla-get-ObjectName txto1) "AcDbText")
            (bbwid txto1); then
            (cdr (assoc 42 (entget txt))); else
          ); if
      ); setq
      (vlax-put txto2 'insertionpoint (polar inspt rot (+ wid (/ (vla-get-Height txto1) 4))))
      ; move over by reduced first-part width plus 1/4 of height
    ); repeat
  ); if
  (princ)
); defun

 

The splitting character is case-sensitive.  If there's more than one occurrence of that, it splits it at the first one only.

It can be enhanced to do things like remember your specified character to offer as default on subsequent use, and of course for the usual stuff -- *error* handling, Undo begin/end wrapping, etc.  But first see whether it does what you want.

Note that it's designed for left-justified objects, and for zero [or, if you want, 180°] rotation.  It could be made to account for different justifications.  Different rotations would be more complicated, since it uses the bounding box approach -- it might be necessary to align the UCS with the object.  And it can give different results than expected if Mtext has multiple lines.

The positioning of the second part can shift a little, depending on the font.  You can change the amount of added move-over width if 1/4 of the height doesn't give you good results.

Kent Cooper, AIA
Message 7 of 8

Kent1Cooper
Consultant
Consultant
Accepted solution

Looking at your code, I think I see why it doesn't locate the second-half insertion point very well.

 

The GetTextLength function uses only the string content, not the entire entity data list of the Text object.  That means it will calculate based on current defaults for Style [and its font], height, etc.  Those will not always be the same as for the object being processed.

 

The determination of the length to the separator [lenx variable] takes the overall length from the textbox and divides it by the raw number of characters in the string, and multiplies that by how many characters in the separator occurs.  That doesn't take into account anything about what the intervening characters are.  The approach should work well enough if the font involved has a constant character width/spacing [such as the Courier family], but with a proportional-width font, all bets are off.  If, for example, the part before the separator contains a lot of narrow characters, and the part after a lot of wide ones, or vice versa, that will throw the proportional position way off from where the separator actually falls in the string.

Kent Cooper, AIA
Message 8 of 8

john.uhden
Mentor
Mentor

Here's my simple solution for locating a character of a TEXT entity.  Dealing with MTEXT would require more code, but you could explode the mtext, find the character position, and then Undo the explosion.

The locations are approximate, but close.

(defun @charpos (char e / ent str str1 str2 tb1 tb2 h p0 ang
                 pos d1 d2 davg p)
  (and
    (setq ent (entget e))
    (= (cdr (assoc 0 ent)) "TEXT")
    (setq str (cdr (assoc 1 ent)))
    (setq p0 (cdr (assoc 10 ent)))
    (setq h (cdr (assoc 40 ent)))
    (setq ang (cdr (assoc 50 ent)))
    (setq p0 (polar p0 (+ ang (/ pi 2)) (/ h 2)))
    (setq pos (vl-string-position (ascii char) str))
    (setq str1 (substr str 1 pos))
    (setq str2 (substr str 1 (1+ pos)))
    (setq tb1 (textbox (subst (cons 1 str1)(assoc 1 ent) ent)))
    (setq tb2 (textbox (subst (cons 1 str2)(assoc 1 ent) ent)))
    (setq d1 (apply 'distance tb1))
    (setq d2 (apply 'distance tb2))
    (setq davg (/ (+ d1 d2) 2.02))
    (setq p (polar p0 ang davg))
    ; you can scrap the next 2 lines; they are for display only
    (setvar "pdmode" 34)(setvar "pdsize" 1.0)
    (entmake (list '(0 . "POINT")(cons 10 p)))
  )
  p
)

John F. Uhden