General XMetaL Discussion
XMetaL Community Forum › General XMetaL Discussion › Makro Search Entity and Replace it
-
ndudek December 19, 2013 at 6:56 am
Makro Search Entity and Replace it
December 19, 2013 at 6:56 amParticipants 21Replies 22Last Activity 9 years, 3 months agoHi,
I'm looking for a way to find an Entity and Replace it with text. The XMetaL Find & Replace Dialog offers a way to do it but i would like to use it in a macro. I tried to access these search options but I can't find them. So is there a way to find entities and replace them?
I know there is a workaround (switching views and replace the entities in plain text view) but maybe there is a better way to do it?
Derek Read December 20, 2013 at 12:43 am
Reply to: Makro Search Entity and Replace it
December 20, 2013 at 12:43 amThere is no API dedicated to this type of thing, but I think you can probably do what you want with a combination of other APIs.
With the following document open in XMetaL Author (Enterprise or Essential)…
[code=Journalist demo document]
]>
a&old;b&somethingelse;c&old;d a&somethingelse;b&test;c&somethingelse;d December 19, 2013 2:21:52 PM [/code]
…running the following script will replace all the “old” entity references with references to the “new” entity.
[code=sample script]//XMetaL Script Language JScript:
if((ActiveDocument.ViewType == sqViewNormal)||(ActiveDocument.ViewType == sqViewTagsOn)) {
var entRefToReplace = “old”;
var entRefToInsert = “new”; //this entity ref must be declared
var rng1 = ActiveDocument.Range;
rng1.MoveToDocumentStart();
var rng2 = rng1.Duplicate;
rng1.GotoNext(0);
while(rng2.IsLessThan(rng1)) {
if(rng2.ContainerName == “.ENTREF”) {
if(rng2.ContainerNode.nodeName == entRefToReplace) {
Application.Alert(“found one: ” + rng2.ContainerNode.nodeName);
rng2.SelectElement();
rng2.Delete();
rng2.InsertEntity(entRefToInsert);
}
}
rng2.GotoNext(0);
rng1.GotoNext(0);
}
}[/code]ndudek January 6, 2014 at 1:57 pm
Reply to: Makro Search Entity and Replace it
January 6, 2014 at 1:57 pmHi Derek,
thanks for your input. I'm close to the final solution but it doesn't work.
It seems like there is something wrong with the “IsLessThan” Method. I'm working in VBScript but that shouldn't be the problem.
Following Document:
[code]
]>
t est
[/code]Following Macro:
[code]
ActiveDocument.Host.DisplayAlerts = 0
Dim entRefToReplace
entRefToReplace = “nbsp”
Dim textToInsert
textToInsert = ” “
Dim rng1
Set rng1 = ActiveDocument.Range
rng1.HomeKey
Dim rng2
Set rng2 = rng1.Duplicate
rng1.GotoNext(0)
while rng2.IsLessThan(rng1)
if rng2.ContainerName = “.ENTREF” Then
if rng2.ContainerNode.nodeName = entRefToReplace Then
rng2.SelectElement
rng2.Delete
rng2.Text = textToInsert
End if
End if
rng2.GotoNext(0)
rng1.GotoNext(0)
wend
ActiveDocument.Host.DisplayAlerts = -1
[/code]It just replace nothing here. Maybe because there is no Element after the nbsp?
Derek Read January 6, 2014 at 7:52 pm
Reply to: Makro Search Entity and Replace it
January 6, 2014 at 7:52 pmMight be a logic problem in my code. I'd suggest debugging and stepping through your script step by step to see if you can find the problem with the logic.
To start a script debugger with VBScript add the keyword “stop” to the start of your script (you need to have a script debugger installed of course). If you don't have something like Visual Studio installed you can use the free script debugger Microsoft provides. I think this is the current link to it: http://www.microsoft.com/en-us/download/details.aspx?id=22185
Derek Read January 6, 2014 at 8:30 pm
Reply to: Makro Search Entity and Replace it
January 6, 2014 at 8:30 pmIs the ultimate goal for this script to replace only nbsp with a normal space?
If so perhaps we could simplify it a lot by simply doing a text replacement on the entire document as a string.I'm suggesting this because my brain is stuck figuring out the logic from my original script. I see why it does not work but not how to fix it. It doesn't work when rng1 cannot go further than the last entity, which is what happens with your example document. GotoNext() is working as designed, but is no good for this usage as far as I can tell. I think MoveRight() might get you closer, but I think that would make the script very slow.
A script that loads the whole document into a string, replaces all the entities using regular expression matching and then replaces the entire document with the new version might do it. I wonder if that would work for your use case (not sure about your timing or other requirements). The user's current selection will be lost, so I think it only makes sense to do this either as part of something where users are not involved at all, or just after document open.
I'm much faster at JScript than VBScript so here's a JScript example:
[code]//XMetaL Script Language JScript:
var rng = ActiveDocument.Range;
rng.SelectAll();
var wholeDocAtRoot = rng.TextWithRM;
Application.Alert(wholeDocAtRoot);
var rx = / /g;
var newDoc = wholeDocAtRoot.replace(rx,” “);
Application.Alert(newDoc);
rng.Delete();
rng.PasteString(newDoc);[/code]Derek Read January 7, 2014 at 12:15 am
Reply to: Makro Search Entity and Replace it
January 7, 2014 at 12:15 amNote that if other entities also need replacement you could obviously just place the replacement portion of this code in a loop.
ndudek January 7, 2014 at 6:36 am
Reply to: Makro Search Entity and Replace it
January 7, 2014 at 6:36 amI think the problem with GotoNext is that there is no element after the entity that's why it won't work with the example I posted.
Replacing the text would help but is there a way to keep the current users selection? That would be the best solution.The ultimate goal for the script is to switch between entitys displayed as a container and the character that represents the entity but not displayed in a container.
I attached a screenshot to give you an example what the script does.We have documents that contain the entity and are displayed in these containers. And some of them don't have them which leads to confusion. So the idea is to give the user control whether he want's to have containers or not.
Derek Read January 7, 2014 at 8:16 pm
Reply to: Makro Search Entity and Replace it
January 7, 2014 at 8:16 pmThe only way to keep the current selection is to make modifications similar to my first attempt that you used as an example. You are correct in why it is failing. GotoNext isn't able to move anymore and so it ends prematurely in some cases. I'll need to think about that some more, but I hesitate to do so when that might be a waste of effort.
It sounds like you are trying to recreate existing functionality. Perhaps you are not aware of the feature that shows the entity reference in Tags On view and the entity's expanded text in Normal view? All you need to do is switch views and no changes to the document are actually made in this case (the document is just rendered differently). There is an INI setting to disable this, but it is enabled by default.
I might not be understanding you though, as I'm not sure what you mean by “container”. I assume you just mean the entity reference, which in your example is the entity reference.
ndudek January 9, 2014 at 10:42 am
Reply to: Makro Search Entity and Replace it
January 9, 2014 at 10:42 amThe Problem is that the nbsp entity isn't displayed if the xml (plain text) contains the character which is character.
I attached you another screenshot, i think we are talking about the same, just to clarify the red underlined entity is the “container”.
The blue underlined whitespace is the character which is the nbsp displayed in normal view.We are working in Tags on view, always and there are sometimes entitys and sometimes not. To help the users we would like to be able to switch in the tags on view view between displaying entities and displaying the characters.
Derek Read January 10, 2014 at 1:53 am
Reply to: Makro Search Entity and Replace it
January 10, 2014 at 1:53 am[I don't see a screenshot here. If you are having difficultly with that perhaps you should open a support case with XMetaL Support so we can discuss via email.]
It sounds like the issue is really that a no-break space is rendered like a “regular” space (which is normal since they do generally look identical but behave differently). Is your ultimate problem that your authors are unable to figure out which character is which when comparing a space to a no-break space (U+0020 vs U+0160)?
In your final document (what you need to save to disk) do you need to save the entity or the character to disk?
If you can avoid the use of entities then other means for identifying no-break spaces might be implemented. If you really need to use entities then I can have a look at these scripts again.
ndudek January 10, 2014 at 11:58 am
Reply to: Makro Search Entity and Replace it
January 10, 2014 at 11:58 amSeems like i forgot to attach it.
So now it is attached.Users are unable to difference between normal spaces and non breaking spaces if non breaking spaces are displayed as normal spaces. If they are displayed as entities they can so that is the goal.
Derek Read January 10, 2014 at 11:05 pm
Reply to: Makro Search Entity and Replace it
January 10, 2014 at 11:05 pmAfter sleeping on this I had a thought. While stepping though this with a debugger I was pretty sure I saw that the last call to GotoNext() will always get to an entity, even if it is at the end of the document (no more elements to move to). The real issue is that the other call to GotoNext for the other range doesn't move past it so the comparison that gets the while loop to run one more time stops. So, if we just check one last time after the loop to see if we're on an entity then that should fix it I think.
Here's my original script modified to do that:
[code]//XMetaL Script Language JScript:
if((ActiveDocument.ViewType == sqViewNormal)||(ActiveDocument.ViewType == sqViewTagsOn)) {
var entRefToReplace = “old”;
var entRefToInsert = “new”; //this entity ref must be declared
var rng1 = ActiveDocument.Range;
rng1.MoveToDocumentStart();
var rng2 = rng1.Duplicate;
rng1.GotoNext(0);
while(rng2.IsLessThan(rng1)) {
if(rng2.ContainerName == “.ENTREF”) {
if(rng2.ContainerNode.nodeName == entRefToReplace) {
Application.Alert(“found one: ” + rng2.ContainerNode.nodeName);
rng2.SelectElement();
rng2.Delete();
rng2.InsertEntity(entRefToInsert);
}
}
rng2.GotoNext(0);
rng1.GotoNext(0);
}
//do one final check in case the last thing we ended on was an entity reference
if(rng2.ContainerName == “.ENTREF”) {
if(rng2.ContainerNode.nodeName == entRefToReplace) {
Application.Alert(“found one: ” + rng2.ContainerNode.nodeName);
rng2.SelectElement();
rng2.Delete();
rng2.InsertEntity(entRefToInsert);
}
}
}
[/code]Your's would be something like this then I think:
[code]Dim entRefToReplace
entRefToReplace = “nbsp”
Dim textToInsert
textToInsert = ” “
Dim rng1
Set rng1 = ActiveDocument.Range
rng1.HomeKey
Dim rng2
Set rng2 = rng1.Duplicate
rng1.GotoNext(0)
while rng2.IsLessThan(rng1)
if rng2.ContainerName = “.ENTREF” Then
if rng2.ContainerNode.nodeName = entRefToReplace Then
rng2.SelectElement
rng2.Delete
rng2.Text = textToInsert
End if
End if
rng2.GotoNext(0)
rng1.GotoNext(0)
wend
'do one final check in case the last thing we ended on was an entity reference
if rng2.ContainerName = “.ENTREF” Then
if rng2.ContainerNode.nodeName = entRefToReplace Then
rng2.SelectElement
rng2.Delete
rng2.Text = textToInsert
End if
End if[/code]Note: Make sure the space in that string is the type you want. It is likely to have been lost between my editor and the editor for the forum and on the way back out.
Having done that I don't know that this solution using entities is really the best, but I suppose if your authors like it then I should't argue with them.
ndudek January 24, 2014 at 7:17 pm
Reply to: Makro Search Entity and Replace it
January 24, 2014 at 7:17 pmThat helped me a lot.
Sometimes the solution is so easy but you think to comlicated.
Anyway we now can do what we want so thank you for your assistance.edporterIII January 11, 2016 at 7:26 pm
Reply to: Makro Search Entity and Replace it
January 11, 2016 at 7:26 pmIs the ultimate goal for this script to replace only nbsp with a normal space?
If so perhaps we could simplify it a lot by simply doing a text replacement on the entire document as a string.I'm suggesting this because my brain is stuck figuring out the logic from my original script. I see why it does not work but not how to fix it. It doesn't work when rng1 cannot go further than the last entity, which is what happens with your example document. GotoNext() is working as designed, but is no good for this usage as far as I can tell. I think MoveRight() might get you closer, but I think that would make the script very slow.
A script that loads the whole document into a string, replaces all the entities using regular expression matching and then replaces the entire document with the new version might do it. I wonder if that would work for your use case (not sure about your timing or other requirements). The user's current selection will be lost, so I think it only makes sense to do this either as part of something where users are not involved at all, or just after document open.
I'm much faster at JScript than VBScript so here's a JScript example:
[code]//XMetaL Script Language JScript:
var rng = ActiveDocument.Range;
rng.SelectAll();
var wholeDocAtRoot = rng.TextWithRM;
Application.Alert(wholeDocAtRoot);
var rx = / /g;
var newDoc = wholeDocAtRoot.replace(rx,” “);
Application.Alert(newDoc);
rng.Delete();
rng.PasteString(newDoc);[/code]Sorry to bring this post back from the dead, but your code example works for me. However, it removes all of the track changes PIs on paste.
Is there any way to search/replace with regular expressions that will preserve the track changes markup?
Derek Read January 11, 2016 at 11:27 pm
Reply to: Makro Search Entity and Replace it
January 11, 2016 at 11:27 pmWhat about this example?
http://forums.xmetal.com/index.php/topic,3275.msg8475.html#msg8475 -
AuthorPosts
- You must be logged in to reply to this topic.