Convert text into an attribute and import it to a block via a LISP routine

Convert text into an attribute and import it to a block via a LISP routine

Anonymous
Not applicable
8,429 Views
20 Replies
Message 1 of 21

Convert text into an attribute and import it to a block via a LISP routine

Anonymous
Not applicable

Good evening everyone...

 

I have an issue and I think LISP could help me....it is my last hope to save hours of work time.....

A company has handed over various drawings that contain blocks but the information related to the blocks is written either in text or mtext format... So when I try to extract the data so that I can handle it in a csv file I have absolutely no connection between the text and the block. 

What I would like to do is run a lisp that can select the text which is closest to a block (with proximity or relative distance criteria), convert it to an attribute and then integrate it to that block. 

Is this even possible?

In an attempt to make it more clear, I have attached a screenshot. Currently I have block "X" with no information and some adjacent text (REC22). I want the lisp to integrade the REC22 text to block with a tag that can be predefined by me (i.e. DESC_1) and insert the text as an attribute value.

 

0 Likes
Accepted solutions (2)
8,430 Views
20 Replies
Replies (20)
Message 2 of 21

JTBWorld
Advisor
Advisor

If you don't find a solution or help to do it for free, I can help with this for the cost and time spent on it. 


Jimmy Bergmark
JTB World - Software development and consulting for CAD and license usage reports
https://jtbworld.com

0 Likes
Message 3 of 21

3wood
Advisor
Advisor

You can try ALTEXT. It can use a customized formula to handle block attributes.

 

Original texts and block inserts - texts are around block inserts:

altext_B1.PNG

Step 1

Save the codes in Example 11 of ALTEXT Formula as a "lsp" file and load ALTEXT.vlx.

Step 2

Ensure the block has an attribute. You can redefine the block and use BATTMAN if necessary.

Step 3

Select one block and "Select similar" from the short-cut menu.

Step 4

Run ALTEXT and tick the "Use formula" option. Select the lsp file saved in Step 1 as the formula.

altext_b2.PNG

 

Result.

altext_b3.PNG

0 Likes
Message 4 of 21

Moshe-A
Mentor
Mentor

@Anonymous  hi,

 

here is a command MT2BL to integrate a mtext into block 😀 but it has it's conditions:

 

You need to define the block with one attribute and what that means?

say you have a block symbol (like in your pic) open it in bedit and add a tag attribute with what ever tag name you choose and place it on base point 0,0,0. set a reasonable height and angle.

 

run MT2BL command:

it start by echoing select object(s) ... you select the block + the text\mtext in one shoot. the function filter your selection so you can choose only 2 objects a block and a text but you'll be alerted if you fail meaning selecting 2 texts or 2 blocks is forbidden. 

 

as you can understand MT2BL does not care about the block name or the attribute tag name all it cares is the block  contains only one attribute.

 

your selection will be replaces by a new insert 😀

 

MT2BL copies these properties from the mtext to the attribute:

layer, text style, height, angle and of course the position.

 

a minor inaccuracy in attribute position may occur due to a difference in text alignment.

 

enjoy

Moshe

 

 

 

; integrate mtext into block
(defun c:mt2bl (/ count_attdef _dxfCode textCentroid textBottomLeft ; local functions
                  ss elist0 elist1 elist2 tmp bname n)

 ; return number of attributes definition in block 
 (defun count_attdef (elist / ctr)
  (setq bname (cdr (assoc '2 elist)))

  (setq ctr 0) 
  (vlax-for AcDbEntity (vla-item (vla-get-blocks (vla-get-activedocument (vlax-get-acad-object))) bname)
   (if (eq (vla-get-objectName AcDbEntity) "AcDbAttributeDefinition")  
    (setq ctr (1+ ctr))
   )

   (vlax-release-object AcDbEntity) 
  ); vlax-for

  ctr
 ); count_attdef

  
 ; annonymous function 
 (setq _dxfCode (lambda (c e) (cdr (assoc c e))))

  
 (defun textCentroid (elist / AcDbEntity MinPoint MaxPoint)
  (setq AcDbEntity (vlax-ename->vla-object (cdar elist)))
  (vla-getboundingBox AcDbEntity 'MinPoint 'MaxPoint)
  (vlax-release-object AcDbEntity)
   
  (setq pt (mapcar '(lambda (x0 x1) (/ (+ x0 x1) 2)) (vlax-safearray->list MinPoint) (vlax-safearray->list MaxPoint)))
 ); mtextCentroid


 (defun textBottomLeft (elist / AcDbEntity MinPoint MaxPoint)
  (setq AcDbEntity (vlax-ename->vla-object (cdar elist)))
  (vla-getboundingBox AcDbEntity 'MinPoint 'MaxPoint)
  (vlax-release-object AcDbEntity)

  (vlax-safearray->list MinPoint)
 ); textBottomLeft 

  
 ; here start c:mt2bl
 (setvar "cmdecho" 0)
 (command "._undo" "_begin")

 (setq savAttdia (getvar "attdia"))
 (setq savAttreq (getvar "attreq"))

 (setvar "attdia" 0)
 (setvar "attreq" 1)
  
 (cond
  ((not (setq ss (ssget '((0 . "insert,text,mtext"))))) ; select an insert and a text\mtext
  ); case
  ((< (sslength ss) 2)
   (vlr-beep-reaction)
   (prompt "\ntoo few objects selected.") 
  ); case
  ((> (sslength ss) 2)
   (vlr-beep-reaction)
   (prompt "\ntoo many objects selected.") 
  ); case
  ((not
     (or
       (and
           (wcmatch (cdr (assoc '0 (setq elist0 (entget (ssname ss 0))))) "INSERT")
           (wcmatch (cdr (assoc '0 (setq elist1 (entget (ssname ss 1))))) "TEXT,MTEXT")
       )  
       (and
           (wcmatch (cdr (assoc '0 (setq elist1 (entget (ssname ss 1))))) "INSERT")
           (wcmatch (cdr (assoc '0 (setq elist0 (entget (ssname ss 0))))) "TEXT,MTEXT")
       )
     ); or
   ); not
   (vlr-beep-reaction)
   (prompt "\nrequire 1 insert and 1 text.") 
  ); case
  ( t
   (if (eq (cdr (assoc '0 elist1)) "INSERT")
    (setq tmp elist0 elist0 elist1 elist1 tmp)
   )

   (setq n (count_attdef elist0))
   (cond    
    ((= n 0)
     (vlr-beep-reaction)
     (prompt "\nblock selected has no attributes.") 
    ) 
    ((> n 1)
     (vlr-beep-reaction)
     (prompt "\nblock selected has too many attributes.") 
    ) 
    ( t
     (command "._insert" bname (trans (_dxfCode 10 elist0) 0 1) "xyz" (_dxfCode 41 elist0) (_dxfCode 42 elist0)
              			      (_dxfCode 43 elist0) (angtos (_dxfCode 50 elist0) 0) (cdr (assoc '1 elist1)))
     (setq elist2 (entget (entnext (entlast))))
     
     (setq elist2 (subst (cons  '8 (_dxfCode  8 elist1)) (assoc  '8 elist2) elist2)) ; set layer
     (setq elist2 (subst (cons  '7 (_dxfCode  7 elist1)) (assoc  '7 elist2) elist2)) ; set text style 
     (setq elist2 (subst (cons '40 (_dxfCode 40 elist1)) (assoc '40 elist2) elist2)) ; set height
     (setq elist2 (subst (cons '50 (_dxfCode 50 elist1)) (assoc '50 elist2) elist2)) ; set angle
     
     (cond
      ((or
         (not (cdr (assoc '11 elist2)))
         (equal (cdr (assoc '11 elist2)) '(0.0 0.0 0.0))
       )
       (entmod (subst (cons '10 (textBottomLeft elist1)) (assoc '10 elist2) elist2)) ; position attribute by dxf10
      )
      ( t
       (entmod (subst (cons '11 (textCentroid elist1)) (assoc '11 elist2) elist2))   ; position attribute by dxf11
      ) 
     ); cond

     (entdel (cdar elist0))
     (entdel (cdar elist1))
    ); case
   ); cond
  ); case
 ); cond

 (setvar "attdia" savAttdia)
 (setvar "attreq" savAttreq)
  
 (command "._undo" "_end")
 (setvar "cmdecho" 1)
 (princ)
); c:mt2bl

 

 

0 Likes
Message 5 of 21

Kent1Cooper
Consultant
Consultant

@Anonymous wrote:

.... drawings that contain blocks but the information related to the blocks is written either in text or mtext format...


The big question in my mind is whether those rectangles in your image are multiple Insertions of the same Block name.  [We can't tell from an image.]  If so, then I suspect you don't want to take a nearby piece of Text/Mtext, convert it to an Attribute, and integrate it into the Block, because after that has happened once, the next one will be adding a second Attribute into the same Blockdefinition, and the next one a third, etc.  That concern argues for something like @3wood's suggestion to give the Block an Attribute [one time], and let the process be to take the text content from the nearby Text/Mtext object, and assign that to the Attribute that's already in the Block.

Kent Cooper, AIA
0 Likes
Message 6 of 21

Moshe-A
Mentor
Mentor

@Anonymous ,

 

fine tuning ... put the whole process in a loop.

 

 

0 Likes
Message 7 of 21

Anonymous
Not applicable

Good morning  everyone,

Thank you for your responses.

Unfortunately it is not an option for me to install third party software without the approval of our IT department.

 

 @Kent1Cooper 

 

Indeed these rectangles have multiple insertions in a drawing. However they do not have the same text. They might have a different - or the same depending on the case - text (Left side of the attached image...Text 1/2/..../6).

What I want is to convert the text to a single attribute with different content and insert it to the nearest block. The attribute location within the drawing shall remain the same as the text.

I don't mind if the block has to be renamed for the conversion as long as the new name is not a temp one or a unique one (i need to use the QSELECT command for counting them). For example if I had a block named RECT and it was repeated 20 times with 20 different adjacent texts, after running the lisp I would still want a block (don't mind if it is named RECT or RECT_B) repeated 20 times  with a single attribute tag and 20 different values. 

In the case I cannot retain the original block name (RECT) I would like to have the option to set the new name myself. 

 

Thank you all.

0 Likes
Message 8 of 21

pbejse
Mentor
Mentor

- Is the target block already have an attribute defiintion

--- YES 

------ is it on the same spot as text or anywhere near it? 

--- NO: Do you already have a the block attribute for replacement?

------ YES: Use Express Tool "BLOCKREPLACE"

------ NO:Do you want to inherit the TEXT properties? 

---------  YES:

---------  NO: Please specify height. style etc...

 

More importantly

Would it be possible to post a sample drawing file and not just an image?

 

 

0 Likes
Message 9 of 21

Anonymous
Not applicable

Hello @pbejse 

I have tried to upload a sample file. 

The blocks don't have defined attributes at the moment.

I don't have a block attribute ready for replacement. 

Yes I want to inherit the text properties. 

Keep in mind, I already have 20-30 defined blocks without attributes. So I want to apply this action to all of them. I also want to minimize manual labor, to reduce the possibilities of mistakes. I want the lisp to do a quick "scan" find the available block names and perform the action automatically. Take the text, inherit the properties and location of text to apply it to the attribute. 

 

Thank you in advance

 

 

0 Likes
Message 10 of 21

pbejse
Mentor
Mentor
Accepted solution

@Anonymous wrote:

Hello @pbejse 

I have tried to upload a sample file. 

The blocks don't have defined attributes at the moment.

I don't have a block attribute ready for replacement. 

Yes I want to inherit the text properties. 

...

 


 Try this:

 

(defun c:Text2AttB (/ ss i TheSelectedTextObject ll ur blk l sstext dataFromSelectedBlockObject
	       		dataIneedFromSelectedBlockObject entText)
  (if (and
	(setq ss (ssget "_:L" '((0 . "INSERT"))))
	(setq fuzz (getreal "\nEnter Fuzz factor: "))
	)
	
	    (repeat (setq i (sslength ss))
	      (setq blk (ssname ss (setq i (1- i))))	    
	      (setq dataFromSelectedBlockObject (entget blk))
	      (setq dataIneedFromSelectedBlockObject
		     (mapcar
		       (function
			 (lambda (dxfcodes)
			   (assoc
			     dxfcodes
			     dataFromSelectedBlockObject
			   )
			 )
		       )
		       '( 2 8 10 50 41 42 43 50 )
		     )
	      )
	      (Vla-getboundingbox (vlax-ename->vla-object blk) 'll 'ur)
	      (setq ll (vlax-safearray->list ll)
		    ur (vlax-safearray->list ur) cnt 0)
	      
		(While  (and (< cnt 10)
			 (not (setq sstext (ssget "C"  ll ur '((0 . "TEXT"))))))
			(setq ur (polar ur (* pi 0.25) (* fuzz (sqrt 2)))
			      ll (polar ll (* pi 1.25) (* fuzz (sqrt 2))) cnt (1+ cnt))
		  )
	      
	      (if
		sstext
		(progn
		  (setq entText (entget (ssname sstext 0)))
		  (setq dataIneedFromSelectedTextObject
			     (mapcar
			       (function
				 (lambda (dxfcodes)
				   (assoc
				     dxfcodes
				     entText
				   )
				 )
			       )
			       '(1 40 7 10 50 41 8)
			     )
		        )
		  (entmake
			(append
			  (list	'(0 . "INSERT") '(66 . 1))
			  	dataIneedFromSelectedBlockObject )
		      	)
		      (entmake
			(append
			  (list '(0 . "ATTRIB")
			    '(2 . "TAG")
			    '(70 . 0)
			  )
			  (cons '(1 . "-") (Cdr dataIneedFromSelectedTextObject))
			)
		      )
	      (entmake '((0 . "SEQEND")))
	      (entdel (ssname sstext 0))
		  
		(Vla-put-textstring
			  (car (vlax-invoke
			    (vlax-ename->vla-object (entlast))
			    'GetAttributes
			  )
		       )
		  (Cdr (Car dataIneedFromSelectedTextObject))
		)
	    )
	  )
	)
    )
  (princ)
)

 

 

I tested this on the drawing sample, a fuzz factor of 10. missed 3 items, 20 missed 1, and 30 is just right.

So it really depends on how far the TEXT.  you should be able to play with fuzz value.

 

Hope this helps

 

0 Likes
Message 11 of 21

Anonymous
Not applicable

@pbejse 

OMG... I am speechless! I am so grateful!!!

It works like a charm. It retains the block name. The attribute retains the former text layer within the Enhanced Attribute Editor....It is perfect.

I am losing like 10-20 texts out of 400 simply out of geographical constrains but I can manually edit them. Just for you to understand, I created a block named M which contains two texts (REC100, REC200) in the vicinity of the block. Your LISP incorporates only REC100 as an attribute and leaves out REC200 (it should be a second attribute). These are the 10-20 texts I am missing...but its ok I can do it manually.

 

One more thing :

Is there a way to export all these blocks in a csv list file that will contain the following columns:

 

Block Name │ Handle │ Insertion_Point_Coordinates(X_Y_Z) │Rotation Angle │Attribute_TAG1 │Value_TAG1 │Attribute_TAG3 │ Value_TAG2

 

Thank you in advance!

0 Likes
Message 12 of 21

pbejse
Mentor
Mentor

@Anonymous wrote:

@pbejse 

.... Your LISP incorporates only REC100 as an attribute and leaves out REC200 (it should be a second attribute). These are the 10-20 texts I am missing...but its ok I can do it manually.,,


(defun c:Text2AttB (/ ss i TheSelectedTextObject ll ur blk l sstext dataFromSelectedBlockObject
	       		dataIneedFromSelectedBlockObject entText)
  (if (and
	(setq ss (ssget "_:L" '((0 . "INSERT"))))
	(setq fuzz (getreal "\nEnter Fuzz factor: "))
	)
	
	    (repeat (setq i (sslength ss))
	      (setq blk (ssname ss (setq i (1- i))))	    
	      (setq dataFromSelectedBlockObject (entget blk))
	      (setq dataIneedFromSelectedBlockObject
		     (mapcar
		       (function
			 (lambda (dxfcodes)
			   (assoc
			     dxfcodes
			     dataFromSelectedBlockObject
			   )
			 )
		       )
		       '( 2 8 10 50 41 42 43 50 )
		     )
	      )
	      (Vla-getboundingbox (vlax-ename->vla-object blk) 'll 'ur)
	      (setq str nil
		     ll (vlax-safearray->list ll)
		    ur (vlax-safearray->list ur) cnt 0)
	      
		(While  (and (< cnt 10)
			 (not (setq sstext (ssget "C"  ll ur '((0 . "TEXT"))))))
			(setq ur (polar ur (* pi 0.25) (* fuzz (sqrt 2)))
			      ll (polar ll (* pi 1.25) (* fuzz (sqrt 2))) cnt (1+ cnt))
		  )
	      
	      (if
		sstext
		(progn
		  (entmake
			(append
			  (list	'(0 . "INSERT") '(66 . 1))
			  	dataIneedFromSelectedBlockObject )
		      	)
		  (repeat (Setq n (sslength sstext))
			(setq entText (entget (ssname sstext (setq n (1- n)))))
			  (setq dataIneedFromSelectedTextObject
				     (mapcar
				       (function
					 (lambda (dxfcodes)
					   (assoc
					     dxfcodes
					     entText
					   )
					 )
				       )
				       '(1 40 7 10 50 41 8)
				     )
			        )
		      (setq str (cons (cdr (car dataIneedFromSelectedTextObject)) str))
		      (entmake
			(append
			  (list '(0 . "ATTRIB")
			    (cons 2  (Strcat "TAG" (itoa (1+ n))))
			    '(70 . 0)
			  )
			  (cons '(1 . "-") (Cdr dataIneedFromSelectedTextObject))
			)
		      )
		    (entdel (ssname sstext n))		    
		    )
	      (entmake '((0 . "SEQEND")))
		  (mapcar 'vla-put-textstring
			  (vlax-invoke (vlax-ename->vla-object (entlast))
			    'GetAttributes
			  )
				(reverse str))
	    )
	  )
	)
    )
  (princ)
)

 


@Anonymous wrote:

One more thing :

Is there a way to export all these blocks in a csv list file that will contain the following columns:

 

Block Name │ Handle │ Insertion_Point_Coordinates(X_Y_Z) │Rotation Angle │Attribute_TAG1 │Value_TAG1 │Attribute_TAG3 │ Value_TAG2


Try DATAEXTRACTION, if that doesnt work for you, the come back and we will sort it out.

 

HTH

0 Likes
Message 13 of 21

Anonymous
Not applicable

Correct me if I am wrong, but DATAEXTRACTION does not give me the hexadecimal unique handle of the items and I need it as a unique identifier of items...

 

Thanks in advance.

0 Likes
Message 14 of 21

pbejse
Mentor
Mentor
Accepted solution
(defun c:GiveMeYourData ( / _sort addtolist _concatenate opf ss csvFile e data atrb AllData)
(defun _concatenate (lst)
	(apply 'strcat (mapcar '(lambda (st)
					     (strcat  st ",")) lst ))
  )  
(Defun _sort (s l) (vl-sort l '(lambda (a b)(< (s a)(s b)))))
(defun addtolist ( key val lst / old )
    (if (setq old (assoc key lst))
        (subst (append old (list val)) old lst)
        (cons  (list key val) lst)
    )
) 
  (if (And
	(setq ss (ssget "_X" '((0 . "INSERT")(66 . 1))))
	(setq csvFile (getfiled "Output File" "" "csv" 1))
	)
  	(progn
	  (repeat (setq i (sslength ss))
	    	(setq e (vlax-ename->vla-object (ssname ss (setq i (1- i)))))
	    	(setq data (mapcar '(lambda (p)(vlax-get e p))
		       '("EffectiveName" "Handle" "InsertionPoint" "Rotation"))
		       atrb (_sort car (mapcar '(lambda (at)
						  (list (vla-get-tagstring at)(vla-get-textstring at)))
				    (vlax-invoke e 'GetAttributes)))
		       )
	    	(setq AllData (addtolist (car data) (append (Cdr data) atrb) AllData))
	    )
	  (setq opf (open csvFile "w"))
	  (setq AllData (_sort car AllData))
	  (while (setq a (car AllData))
	    	 (Foreach itm (cdr a)
		   (write-line
			   (_concatenate
			      (apply 'append 			     
				     (list
				       (list (car a)(car itm))
					   (mapcar 'rtos (Cadr itm))
					   (list (rtos (/ (* (Caddr itm) 180.0) pi) 2 0))
					   (apply 'append (cdddr itm))
					   )
				     ) 
			      )
			   opf
			   )
		   )
	    (setq AllData (cdr AllData))
	    )
	    (Close opf)
	    (startapp "explorer" csvFile)
	    )
	  )(princ)
	)

HTH

0 Likes
Message 15 of 21

Anonymous
Not applicable

Hi @pbejse 

I tried the LISP and it almost runs perfectly. THANK YOU SO MUCH.

I have one tiny remark and I don't know if we can solve it via the lisp or if it has to be done via VBA in excel...

Let me try to describe the issue: 

Your LISP returns the attribute values in the following format:

Block Name_Handle_X_Y_Z_Rotation_AttTag1_AttValue1_AttTag2_AttValue2_AttTag3_AttValue3....

Some different blocks share some common AttTags but in a different BATTORDER. 

To give you an example, lets assume that I have a block named "Ball" and another one named "Shoe". I run your LISP and it returns the following csv:

 

Ball   F7D16   500.235   100.235     90   COLOUR   RED   DIAMETER 10CM AIRPRESSURE 1ATM

Shoe FC1F6   328.235    198.235   0   90   SIZE 9   BRAND NIKE  COLOUR  GREEN

 

Would it be possible to put all identical Attribute Tags (contained in different blocks) in the same column ? That would mean that my sample case would return the following results:

 

Ball   F7D16   500.235   100.235     90   COLOUR   RED   DIAMETER 10CM AIRPRESSURE 1ATM

Shoe FC1F6   328.235    198.235   0   90   COLOUR  GREEN    SIZE 9   BRAND NIKE

 

Thank you so much for your help.. I appreciate it greatly.

0 Likes
Message 16 of 21

Anonymous
Not applicable

Good morning @pbejse .

I used your lisp quite a lot and thank you about it.. I noticed a small glitch though.

If a block does not contain any attributes, it is not recorded in the excel spreadsheet. Could it be possible to find a workaround that includes these kinds of blocks and just leave the attribute fields blank apart from the ones that are already recorded that contain attributes?

 

Thank you in advance.

Dgoutz

Best regards,

0 Likes
Message 17 of 21

nbortolussi
Participant
Participant
@pbejse,
so far this lisp is the closest i have gotten to what i need. I just posted my question yesterday, but i decided to keep searching the forum. my starting point is multiple pairs of objects (a TEXT and a POINT). they are pretty close to one another. i use another lisp to get these pairs to a place where i can use the lisp you wrote here. The other lisp replaces all my POINTS with a block I select. the block i am using has just one defined attribute. At this point i can use your lisp and it creates a copy of the block with the text in the attribute value!!!
How can i get it so that it doesnt copy the blocks and instead modifies the blocks i selected?

Even better still, it would be great to have just one lisp that would replace all my selected text objects with the block i choose and also use the TEXT as the value for the one attribute that the block is defined with. the blocks insertion point would need to go to the TEXT objects "justify" point location.
0 Likes
Message 18 of 21

Sea-Haven
Mentor
Mentor

It would have been better to just post the link to here in your other post, so where is it ? Need a sample dwg.

0 Likes
Message 19 of 21

pbejse
Mentor
Mentor

 


@nbortolussi wrote:
How can i get it so that it doesnt copy the blocks and instead modifies the blocks i selected?
...

Definitely doable

 


@nbortolussi wrote:
so far this lisp is the closest i have gotten to what i need. I just posted my question yesterday, 

You're referring to this post? replace multiple point objects with block and add nearest text as block attribute 

 

0 Likes
Message 20 of 21

nbortolussi
Participant
Participant
@pbejse,
yes, that was my post, and it was just solved. thank you for your consideration.
0 Likes