Tool: Find and Replace text in markup

Forum for the PDF-XChange Editor - Free and Licensed Versions

Moderators: TrackerSupp-Daniel, Tracker Support, Paul - Tracker Supp, Vasyl-Tracker Dev Team, Chris - Tracker Supp, Sean - Tracker, Ivan - Tracker Software, Tracker Supp-Stefan

Mathew
User
Posts: 239
Joined: Thu Jun 19, 2014 7:30 pm

Tool: Find and Replace text in markup

Post by Mathew »

There's an excellent search/find tool built-in but sometimes I need to replace text in markup. Would be great if this were added to the find tool, but in the meantime, here's the script I use:

Extract the zip file and save to the JavaScripts folder.
This version will add a menu item "Find/Replace..." below Find... in the classic toolbars. I think it will add it to the File menu on the ribbon UI.:
find replace annot tx v1.2.zip
This version adds a button to the Add-on Toolbar:
find replace annot tx v1.2.1.zip
It Selecting that item gives you a dialog for find and replace:
image.png
It also allows regular expressions - use $1, $2, etc for the parenthesis replacement.

Use the dropdowns on the right side to pick saved previous search/replace terms. It saves up to 10.

Limitations:
  • In rich text it will miss matches that overlap between spans (ie searching "the text" will fail on text with formatting such as "it won't find 'the text' here")
  • Only searches and replaces within annotations (base content is not changeable by scripts)
  • No feedback on which text was changed - it does add a note in the console about how many annotations were changed
  • Adds many undos - but this may be useful for going back and seeing the changes
You do not have the required permissions to view the files attached to this post.
Last edited by Mathew on Tue Mar 19, 2024 6:38 pm, edited 4 times in total.
User avatar
Paul - Tracker Supp
Site Admin
Posts: 6903
Joined: Wed Mar 25, 2009 10:37 pm
Location: Chemainus, Canada

Re: Tool: Find and Replace text in markup

Post by Paul - Tracker Supp »

Hi, Mathew

this is really neat. I see it works with annotations that have text on the page, but not popups associated with annotations like Highlight when the option to copy the highlighted text to a popup is enabled.

Is that by design?

Really nice work. I see the tool in the Ribbon UI under "Move to Trash":
image.png
Kind regards,
Paul - Tracker Supp
You do not have the required permissions to view the files attached to this post.
Best regards

Paul O'Rorke
Tracker Support North America
http://www.tracker-software.com
Mathew
User
Posts: 239
Joined: Thu Jun 19, 2014 7:30 pm

Re: Tool: Find and Replace text in markup

Post by Mathew »

Good point. Not by design - updated to also change those.
find replace annot tx v1.1.zip
You do not have the required permissions to view the files attached to this post.
User avatar
Paul - Tracker Supp
Site Admin
Posts: 6903
Joined: Wed Mar 25, 2009 10:37 pm
Location: Chemainus, Canada

Re: Tool: Find and Replace text in markup

Post by Paul - Tracker Supp »

Hi, Mathew

Boom!

That is so slick. I'll be using this for sure. Very much appreciated.

Kind regards,
Paul - Tracker Supp
Best regards

Paul O'Rorke
Tracker Support North America
http://www.tracker-software.com
Mathew
User
Posts: 239
Joined: Thu Jun 19, 2014 7:30 pm

Re: Tool: Find and Replace text in markup

Post by Mathew »

Slight improvement: Added case sensitivity option, and dropdowns with saved previous searches.
find replace annot tx v1.2.zip
You do not have the required permissions to view the files attached to this post.
User avatar
David.P
User
Posts: 1521
Joined: Thu Feb 28, 2008 8:16 pm

Re: Tool: Find and Replace text in markup

Post by David.P »

wow.png
Mathew done it again! 🤯
You do not have the required permissions to view the files attached to this post.
David.P
PDF-XChange Pro
KD952
User
Posts: 60
Joined: Mon Feb 13, 2023 6:13 am

Re: Tool: Find and Replace text in markup

Post by KD952 »

Thank you, Mathew!

Awesome work as usual!
Clapping Hands.jpg
You do not have the required permissions to view the files attached to this post.
selim17
User
Posts: 6
Joined: Sun Mar 17, 2024 12:11 pm

Re: Tool: Find and Replace text in markup

Post by selim17 »

Hello Mathew. I contacted user support. They gave me your post. Thank you! I am a heavy user of the software,.. particularly the commenting functions, .... but I am not a programmer. Some questions please:
...
1. Windows defender reported virus. It prevents download. I tried all 3 files in this post.
2. I have Editor Plus. I cannot find the JavaScript folder. I browsed this:
Users\MYNAME\AppData\Roaming\Tracker Software\PDFXEditor\3.0
3. If I create JavaScript folder and put your tool in it and after remove it... will the Editor Plus software remain unaffected?
...
Regards
20240317_082836, search and replace virus detected.jpg
You do not have the required permissions to view the files attached to this post.
User avatar
Tracker Supp-Stefan
Site Admin
Posts: 17960
Joined: Mon Jan 12, 2009 8:07 am
Location: London

Re: Tool: Find and Replace text in markup

Post by Tracker Supp-Stefan »

Hello selim17,

It would appear like your AV product simply does not allow you to download any .js files.
I do not want to advise you to temporarily override it - so please try getting those files e.g. on another machine where you have a different AAV software.

For the JavaScripts folder - it might not exist indeed.
So please navigate to this folder:
%Appdata%\Tracker Software\PDFXEditor\3.0\ (it should exist if you have the Editor installed), and then inside there create the \JavaScripts one manually. Once done - you can then place the .js files inside that folder.

Yes - you can add and remove scripts from that folder - and on Editor restart the Editor will check for that folder, and if it finds it and there are any .js files inside - it will process them accordingly. If there are no files (or the whole folder does not exist) - the rest of the Editor features would still work perfectly fine.

Kind regards,
Stefan
Mathew
User
Posts: 239
Joined: Thu Jun 19, 2014 7:30 pm

Re: Tool: Find and Replace text in markup

Post by Mathew »

Hi Selim,

I don't know why it's triggering an alert as a virus. If anyone knows a better way I could save the file so that doesn't happen, please let me know. Currently I just compress the text file with a .js suffix inside a zip archive - there should only be one file "find replace annot tx.js" in there.

The file makes no permanent changes to either PDFX-Change or your computer. If you make the JavaScripts folder, and put the javascript file (extracted from the zip) in there, it will run when PDFX-Change starts up to create the menu item.

If you just want to test it, you can copy and paste from below into the Javascript console on PDFX-Change (CTRL-J) and run it from there: In that case you should run it without any document open or it will add the menu item, but it will only work with the current open document (you'll have to quit and restart to do the same on a different document).

Once the v1.2 tool has been run, PDFX-Change will save the last used search terms to a file GlobData, so if you want to remove absolutely everything, you'd also delete that file too.

- Mathew.

This is the entire script:

Code: Select all

/* add a find/replace menu item
	v1.2 Mar 15, 2024	case sensitive option, save searches
	v1.1 Mar 14, 2024	replace also on popups
	v1.0 Mar 14, 2024	Initial release
*/

app.addMenuItem( {
	cName: "findReplaceAnnTx",
	cUser: "Find/Replace…",
	cParent: "Edit",
	nPos: 21, // after Find... in the Edit menu. The position could easily be wrong!
	//cEnable: "event.rc = (event.target != null);",
	cExec: 'replaceAnnotTx.run(this)'}
);

// using a generic object
var replaceAnnotTx = {
	SAVE_LENGTH : 10, // Number of search terms to save
	dialog: {
		data:{},
		initialize: function (dialog) {
			let def = this.data;
			
			if ( !Object.keys(def).length ) {
				def = { "ftxt": "", "rtxt": "", "usRE": false, "casS": false };
			} else {
				["ftxt","rtxt"].forEach( i => def[i] = this.dd( def[i] ));
			}
			dialog.load( def );
		},
		commit:function (dialog) { // called when OK pressed
			this.data = dialog.store();
		},
		// make dropdown object
		dd:function(arr) {
			let ob = { "":1 }; // blank as selected item
			if (! Array.isArray(arr))
				arr = [arr];
			for (let i in arr) {
				ob[arr[i]] = -2-i;
			}
			return ob;
		},
		description: {
			name: "Find & Replace", // Dialog box title
			align_children: "align_left",
			elements:
			[
				{
					type: "cluster",
					name: "Search and Replace expressions",
					align_children: "align_left",
					elements:
					[
						{
							type: "view",
							align_children: "align_row",
							elements:
							[
								{
									type: "static_text",
									name: "Find:",
									alignment: "align_right",
									width:50
								},
								{
									item_id: "ftxt",
									type: "edit_text",
									PopupEdit: true,
									alignment: "align_left",
									width:300
								}
							]
						},
						{
							type: "view",
							align_children: "align_row",
							elements:
							[
								{
									type: "static_text",
									name: "Replace:",
									alignment: "align_right",
									width:50
								},
								{
									item_id: "rtxt",
									type: "edit_text",
									PopupEdit: true,
									alignment: "align_left",
									width:300
								}
							]
						},
						{
							type: "check_box",
							name: "Regular Expression",
							//char_width: 25,
							item_id: "usRE"
						},
						{
							type: "check_box",
							name: "Case Sensitive",
							//char_width: 25,
							item_id: "casS"
						},
					]
				},
				{
					alignment: "align_right",
					type: "ok_cancel",
					ok_name: "Ok",
					cancel_name: "Cancel"
				}
			]
		}
	},
	
	// *** run the dialog ***
	run: function(t) {
		// load globals
		let globals = this.global.get();
		Object.assign(this.dialog.data, globals);
		if ("ok" == app.execDialog(this.dialog)) {
			console.println( this.doReplace( t, this.dialog.data ));
			// save the data
			this.global.set( this.mkArrays( globals ) );
		} else {
			return "User Cancelled";
		}
	},

	// save the search data as arrays
	mkArrays: function ( gData ) {
		let setObs = ["ftxt","rtxt"]; // fields to make into arrays
		let newData = this.dialog.data; // assume this has been initialized
		// build arrays
		if (gData) {
			for (let i of setObs) {
				let arr = gData[i];
				if (arr) {
					if ( !Array.isArray(arr) )
						arr = [ arr ];
					// the dialog returns a string from PopupEdit in newData[i]
					if ( -1 == arr.indexOf( newData[i] ) ) {
						newData[i] = [newData[i]].concat( arr );
					} else {
						// it's already in there, so just save previous version
						newData[i] = arr;
					}
					// newData[i] is now an array
					if ( newData[i].length > this.SAVE_LENGTH )
						newData[i] = newData[i].slice(0,this.SAVE_LENGTH);
				}
			}
		}
		return newData;
	},

	doReplace: function( t, results ) {
		let fSrc = results["ftxt"];
		if ( "" == fSrc) return "Nothing to change"; // don't search for empty string
		
		// build regex
		let useRex = results["usRE"];
		// this is a bit messy because if there are actually regex expressions, it will still use them
		fSrc = useRex ? fSrc : fSrc.replace(/([\.\(\)\\\|\[\]\{\}\+\-\*\$\^\,\?])/g,"\\$1");
		let caseSens = results["casS"] ? "":"i";
		let fRE = new RegExp(fSrc,"g" + caseSens );
		let reTx =  results["rtxt"]; //useRex ? results["rtxt"] : results["rtxt"].replace(/\$/g,'\\$');
		// Step through all pages and replace
		// try to get selected annotations
		let anns = t.selectedAnnots;
		// if nothing selected, get all annotations
		if (0==anns.length)
			anns = t.getAnnots();
		let replacements = 0;
		for (let ann of anns){
			let aProps = ann.richContents;
			// maybe use aProps.reduce( (a,i) => a+i.text,'') on the rich text before the find replace?
			// a possible solution: http://james.padolsey.com/javascript/replacing-text-in-the-dom-solved/
			// uses an index on the joined text for each match, which then is replaced sequentially on the elements
			if (aProps.length>0) {
				// working with rich contents
				let changed = false;
				for (let i=0; i<aProps.length; i++) {
					if (aProps[i] && fRE.test(aProps[i].text)) {
						aProps[i].text = aProps[i].text.replace(fRE,reTx);
						changed = true;
						replacements++;
					}
				}
				if (changed) ann.richContents = aProps;
			} else {
				// no rich contents
				aProps = ann.contents;
				if (aProps && fRE.test(aProps)){
					aProps=aProps.replace(fRE,reTx);
					ann.contents = aProps;
					replacements++;
				}
			}
		}
		return "Replaced in "+replacements+" locations.";
	}
}
// Add trusted functions to access global variables
replaceAnnotTx.global = new class {
	constructor(name) {
		this.get = app.trustedFunction(() => {
			app.beginPriv();
			try{
			if ( global[name] )
				return JSON.parse( global[name] );
			}catch{
				console.println("Error accessing global variable '"+name+"'. Try:\n either uncheck 'Enable global object security policy',\n or (preferably) edit the file 'GlobData': Delete the line that begins with /D after the line \n/"+name+" <<");
			}});
		this.set = app.trustedFunction( value => {
			app.beginPriv();
			try{
			global[name] = JSON.stringify( value );
			global.setPersistent( name, true);
			}catch{}});
	}
}("FindReplaceGlobalVals")
User avatar
TrackerSupp-Daniel
Site Admin
Posts: 8624
Joined: Wed Jan 03, 2018 6:52 pm

Re: Tool: Find and Replace text in markup

Post by TrackerSupp-Daniel »

Hello, Mathew

False positives are fairly common when sharing anything designed to execute, such as JS files. This occurs with high enough frequency when I am emailing clients that I have begun to convert the smaller JS scripts I send to a plain *.txt file (before placing it inside a zip folder), and mentioning that they need to change the file extension for it to work.
It may be a crude workaround, but it usually prevents overzealous security software from giving these warnings. I think you are fine continuing to share the files as you have been on the forums here, since as a download link there are alternative solutions to getting these files (unlike my emails, where leaving the JS file intact would see my mail bounce).

@selim, you may have an option if you right click on one of those downloads, to "override/ignore" the security warning and proceed with download. Might I ask what security software you are using? I have both Windows Defender, and Malewarebytes running in tandem on my end, and get no warning when downloading Matthew's files, nor can I recall ever seeing that particular error in MS Edge, which I use as my daily driver.
Normally, if there is an error, I would get a notice like this, and a right click lets me "keep" or "report as safe":
image.png
Kind regards,
You do not have the required permissions to view the files attached to this post.
Dan McIntyre - Support Technician
Tracker Software Products (Canada) LTD

+++++++++++++++++++++++++++++++++++
Our Web site domain and email address has changed as of 26/10/2023.
https://www.pdf-xchange.com
Support@pdf-xchange.com
selim17
User
Posts: 6
Joined: Sun Mar 17, 2024 12:11 pm

Re: Tool: Find and Replace text in markup

Post by selim17 »

Thank you all for quick and helpful comments. I followed. Downloaded. This time it did not complain. Used Windows Defender manually. No virus warning. Installed in the folder as explained. It works ! :D I wanted to drag and place on the toolbar. It does not show up on the list. See image. Is there any way I can do this? Because I work with many documents and heavily I have to optimize and every shortcut key and every icon I place on the toolbar counts into productivity.
//
FYI. I don't know why it didn't block download this time. I only have Windows Defender. I work in a very big Engineering company based out of Germany. Our laptops are administered remotely by people I don't know. Maybe they are doing something. Updates that are happening in the background I have no clue.
//
Again FYI. When I first asked this question Daniel McIntyre above from End User Support team responded and said that PDF-Xchange has been developing a search and replace tool. It is apparently quite comprehensive. Supposed to come out in the near future.

20240319_105555, I cannot add the search and replace tool to toolbar PDF-Xchange.jpg
You do not have the required permissions to view the files attached to this post.
Last edited by selim17 on Tue Mar 19, 2024 3:26 pm, edited 1 time in total.
User avatar
Tracker Supp-Stefan
Site Admin
Posts: 17960
Joined: Mon Jan 12, 2009 8:07 am
Location: London

Re: Tool: Find and Replace text in markup

Post by Tracker Supp-Stefan »

Hello selim17,

Custom operations that you have added with .js code will appear on an "Add-on Tools" toolbar:
image.png
You can not assign shortcuts to those, nor can you move them to another location.

Our own Fine and Replace is indeed under development and we hope to be able to present it to all of you soon!

Kind regards,
Stefan
You do not have the required permissions to view the files attached to this post.
selim17
User
Posts: 6
Joined: Sun Mar 17, 2024 12:11 pm

Re: Tool: Find and Replace text in markup

Post by selim17 »

Hello Stefan, thank you! I have to apologize and admit that I tried to find this "Add-On Tools" and I have failed .. :?
I think that I have seen it before but now it escapes me. Can you help?
User avatar
TrackerSupp-Daniel
Site Admin
Posts: 8624
Joined: Wed Jan 03, 2018 6:52 pm

Re: Tool: Find and Replace text in markup

Post by TrackerSupp-Daniel »

Hello, selim17

It should always appear at the right of your Ribbon tabs, as show in Stefan's screenshot, provided that you have JS present in one of our JS folders, and have restarted the Editor since it was placed there on this device.

And a quick clarification about this item for anyone else reading here, to avoid getting hopes up too high:
Selim wrote:Again FYI. When I first asked this question Daniel McIntyre above from End User Support team responded and said that PDF-Xchange has been developing a search and replace tool. It is apparently quite comprehensive. Supposed to come out in the near future.
"Near Future" is a bit of a misleading way to phrase this. My exact statement was:
Daniel - by email wrote:We do plan to offer full search and replace functionality in the future, but in PDF such actions are highly complex, and when we offer this we hope to ensure it is a complete implementation, not a partial one, so we are taking extra time to sort this out. Keep an eye on our future updates, it may still be a few months out, but hopefully we can see it implemented in the not to distant future.
To be quite clear, there is still no set date for when this will be ready, I am hopeful that it is "not too distant" but that should not be misconstrued as my saying it is coming in the near future.

Kind regards,
Dan McIntyre - Support Technician
Tracker Software Products (Canada) LTD

+++++++++++++++++++++++++++++++++++
Our Web site domain and email address has changed as of 26/10/2023.
https://www.pdf-xchange.com
Support@pdf-xchange.com
selim17
User
Posts: 6
Joined: Sun Mar 17, 2024 12:11 pm

Re: Tool: Find and Replace text in markup

Post by selim17 »

Hello Dan. Thank you for calibrating the expectations on vocabulary.
User avatar
TrackerSupp-Daniel
Site Admin
Posts: 8624
Joined: Wed Jan 03, 2018 6:52 pm

Tool: Find and Replace text in markup

Post by TrackerSupp-Daniel »

:)
Dan McIntyre - Support Technician
Tracker Software Products (Canada) LTD

+++++++++++++++++++++++++++++++++++
Our Web site domain and email address has changed as of 26/10/2023.
https://www.pdf-xchange.com
Support@pdf-xchange.com
selim17
User
Posts: 6
Joined: Sun Mar 17, 2024 12:11 pm

Re: Tool: Find and Replace text in markup

Post by selim17 »

Hello Mathew, after further checks... the add-on tools does not appear on my computer. It appears that this is bcs of the way you coded it. You cannot always win. :D Nevertheless... thank you for this product. Selim
KD952
User
Posts: 60
Joined: Mon Feb 13, 2023 6:13 am

Re: Tool: Find and Replace text in markup

Post by KD952 »

Hello selim17!

Mathew coded the tool so that "it will add a menu item "Find/Replace..." below Find... in the classic toolbars."

See picture
image.png

Kind regards,
Daniel
You do not have the required permissions to view the files attached to this post.
KD952
User
Posts: 60
Joined: Mon Feb 13, 2023 6:13 am

Re: Tool: Find and Replace text in markup

Post by KD952 »

Hello selim17!

If you prefer to use tool buttons like Stefan suggested
image.png

here a version of the tool that adds a menu item and a tool button.
find replace annot tx v1.2.1.zip
Kind regards,
Daniel
You do not have the required permissions to view the files attached to this post.
User avatar
TrackerSupp-Daniel
Site Admin
Posts: 8624
Joined: Wed Jan 03, 2018 6:52 pm

Re: Tool: Find and Replace text in markup

Post by TrackerSupp-Daniel »

Hello, KD952

Nice! Thanks KD, that'll probably be helpful!

Kind regards,
Dan McIntyre - Support Technician
Tracker Software Products (Canada) LTD

+++++++++++++++++++++++++++++++++++
Our Web site domain and email address has changed as of 26/10/2023.
https://www.pdf-xchange.com
Support@pdf-xchange.com
Mathew
User
Posts: 239
Joined: Thu Jun 19, 2014 7:30 pm

Re: Tool: Find and Replace text in markup

Post by Mathew »

:) I love it that you find it useful also. I added the v1.2.1 to the first post.

Weird part is now I'm getting windows defender automatically blocking the download too.
image.png
You do not have the required permissions to view the files attached to this post.
User avatar
TrackerSupp-Daniel
Site Admin
Posts: 8624
Joined: Wed Jan 03, 2018 6:52 pm

Re: Tool: Find and Replace text in markup

Post by TrackerSupp-Daniel »

Hello, Mathew

That is very strange, I still cannot seem to experience any issues with downloading the files... nor do any warnings appear on this end.

Kind regards,
Dan McIntyre - Support Technician
Tracker Software Products (Canada) LTD

+++++++++++++++++++++++++++++++++++
Our Web site domain and email address has changed as of 26/10/2023.
https://www.pdf-xchange.com
Support@pdf-xchange.com
Mathew
User
Posts: 239
Joined: Thu Jun 19, 2014 7:30 pm

Re: Tool: Find and Replace text in markup

Post by Mathew »

:? Well there's no malicious code in there - so hopefully this clears itself up by itself. If not, the code is pasted above, and can be saved as a text file named with the suffix .js in the JavaScripts folder.

Here's the revised code that Daniel posted:

Code: Select all

/* add a find/replace menu item
	v1.2.1 Mar 19, 2024	icon and toolbar button added by @KD952
	v1.2 Mar 15, 2024	case sensitive option, save searches
	v1.1 Mar 14, 2024	replace also on popups
	v1.0 Mar 14, 2024	Initial release
*/

app.addMenuItem( {
	cName: "findReplaceAnnTx",
	cUser: "Find/Replace…",
	cParent: "Edit",
	nPos: 21, // after Find... in the Edit menu. The position could easily be wrong!
	//cEnable: "event.rc = (event.target != null);",
	cExec: 'replaceAnnotTx.run(this)'}
);


var myIcon = {count:0, width:20, height:20, read:function(nBytes=this.data.length/2){return this.data.slice(this.count,this.count+=2*nBytes)}, data:"0000000000000000000000000000000000000000FF808080FF808080FF808080FF808080FF808080FF808080FF808080000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FF808080FF808080FF808080FF808080FF808080FF868686FF808080FF808080FF8080800000000000000000FF808080FF7F7F7F000000000000000000000000000000000000000000000000FF808080FF808080FF8080800000000000000000000000000000000000000000000000000000000000000000FF808080FF808080FF808080FF89898900000000000000000000000000000000FF808080FF808080000000000000000000000000000000000000000000000000000000000000000000000000FF808080FF808080FF808080FF808080FF808080FF8181810000000000000000FF808080FF808080000000000000000000000000000000000000000000000000000000000000000000000000FF808080FF808080FF808080FF808080FF808080FF808080FF8080800000000000000000FF808080FF8080800000000000000000000000000000000000000000000000000000000000000000FF808080FF808080FF808080FF808080FF808080FF808080FF8080800000000000000000FF808080FF8080800000000000000000000000000000000000000000000000000000000000000000FF808080FF808080FF808080FF808080FF808080FF808080FF808080000000000000000000000000FF808080FF80808000000000000000000000000000000000000000000000000000000000FF808080FF808080FF808080FF808080FF808080FF808080FF80808000000000000000000000000000000000FF808080FF808080000000000000000000000000000000000000000000000000FF808080FF808080FF808080FF808080FF808080FF808080FF8080800000000000000000000000000000000000000000FF808080FF8080800000000000000000000000000000000000000000FF808080FF808080FF808080FF808080FF808080FF808080FF8080800000000000000000FF808080000000000000000000000000FF808080FF80808000000000000000000000000000000000FF808080FF808080FF808080FF808080FF808080FF808080FF8080800000000000000000FF808080FF808080000000000000000000000000FF808080FF808080FF8080800000000000000000FF808080FF808080FF808080FF808080FF838383FF808080FF808080000000000000000000000000FF808080FF80808000000000000000000000000000000000FF8080800000000000000000FF808080FF808080FF808080FF808080FF808080FF808080FF808080000000000000000000000000FF808080FF808080FF808080000000000000000000000000000000000000000000000000FF808080FF808080FF808080FF808080FF808080FF808080FF808080000000000000000000000000FF808080FF808080FF808080000000000000000000000000000000000000000000000000FF808080FF8080800000000000000000FF808080FF808080FF808080000000000000000000000000FF808080FF808080FF808080FF808080000000000000000000000000000000000000000000000000FF808080000000000000000000000000FF808080FF808080FF80808000000000FF808080FF808080FF808080FF808080FF808080FF808080FF80808000000000000000000000000000000000FF808080FF8080800000000000000000FF808080FF8080800000000000000000FF808080FF808080FF808080000000000000000000000000FF808080FF808080FF808080000000000000000000000000FF808080FF808080FF808080FF808080FF80808000000000000000000000000000000000000000000000000000000000000000000000000000000000FF808080FF808080FF8080800000000000000000FF808080FF808080FF80808000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FF808080FF808080000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"};

app.addToolButton({
    cName: "findReplaceAnnTx",
    oIcon: myIcon,
    cLabel: "",
    cExec: 'replaceAnnotTx.run(this)',
    cTooltext: "Find and Replace",
    cEnable: "event.rc = (event.target != null);",
    nPos: -1
});


// using a generic object
var replaceAnnotTx = {
	SAVE_LENGTH : 10, // Number of search terms to save
	dialog: {
		data:{},
		initialize: function (dialog) {
			let def = this.data;
			
			if ( !Object.keys(def).length ) {
				def = { "ftxt": "", "rtxt": "", "usRE": false, "casS": false };
			} else {
				["ftxt","rtxt"].forEach( i => def[i] = this.dd( def[i] ));
			}
			dialog.load( def );
		},
		commit:function (dialog) { // called when OK pressed
			this.data = dialog.store();
		},
		// make dropdown object
		dd:function(arr) {
			let ob = { "":1 }; // blank as selected item
			if (! Array.isArray(arr))
				arr = [arr];
			for (let i in arr) {
				ob[arr[i]] = -2-i;
			}
			return ob;
		},
		description: {
			name: "Find & Replace", // Dialog box title
			align_children: "align_left",
			elements:
			[
				{
					type: "cluster",
					name: "Search and Replace expressions",
					align_children: "align_left",
					elements:
					[
						{
							type: "view",
							align_children: "align_row",
							elements:
							[
								{
									type: "static_text",
									name: "Find:",
									alignment: "align_right",
									width:50
								},
								{
									item_id: "ftxt",
									type: "edit_text",
									PopupEdit: true,
									alignment: "align_left",
									width:300
								}
							]
						},
						{
							type: "view",
							align_children: "align_row",
							elements:
							[
								{
									type: "static_text",
									name: "Replace:",
									alignment: "align_right",
									width:50
								},
								{
									item_id: "rtxt",
									type: "edit_text",
									PopupEdit: true,
									alignment: "align_left",
									width:300
								}
							]
						},
						{
							type: "check_box",
							name: "Regular Expression",
							//char_width: 25,
							item_id: "usRE"
						},
						{
							type: "check_box",
							name: "Case Sensitive",
							//char_width: 25,
							item_id: "casS"
						},
					]
				},
				{
					alignment: "align_right",
					type: "ok_cancel",
					ok_name: "Ok",
					cancel_name: "Cancel"
				}
			]
		}
	},
	
	// *** run the dialog ***
	run: function(t) {
		// load globals
		let globals = this.global.get();
		Object.assign(this.dialog.data, globals);
		if ("ok" == app.execDialog(this.dialog)) {
			console.println( this.doReplace( t, this.dialog.data ));
			// save the data
			this.global.set( this.mkArrays( globals ) );
		} else {
			return "User Cancelled";
		}
	},

	// save the search data as arrays
	mkArrays: function ( gData ) {
		let setObs = ["ftxt","rtxt"]; // fields to make into arrays
		let newData = this.dialog.data; // assume this has been initialized
		// build arrays
		if (gData) {
			for (let i of setObs) {
				let arr = gData[i];
				if (arr) {
					if ( !Array.isArray(arr) )
						arr = [ arr ];
					// the dialog returns a string from PopupEdit in newData[i]
					if ( -1 == arr.indexOf( newData[i] ) ) {
						newData[i] = [newData[i]].concat( arr );
					} else {
						// it's already in there, so just save previous version
						newData[i] = arr;
					}
					// newData[i] is now an array
					if ( newData[i].length > this.SAVE_LENGTH )
						newData[i] = newData[i].slice(0,this.SAVE_LENGTH);
				}
			}
		}
		return newData;
	},

	doReplace: function( t, results ) {
		let fSrc = results["ftxt"];
		if ( "" == fSrc) return "Nothing to change"; // don't search for empty string
		
		// build regex
		let useRex = results["usRE"];
		// this is a bit messy because if there are actually regex expressions, it will still use them
		fSrc = useRex ? fSrc : fSrc.replace(/([\.\(\)\\\|\[\]\{\}\+\-\*\$\^\,\?])/g,"\\$1");
		let caseSens = results["casS"] ? "":"i";
		let fRE = new RegExp(fSrc,"g" + caseSens );
		let reTx =  results["rtxt"]; //useRex ? results["rtxt"] : results["rtxt"].replace(/\$/g,'\\$');
		// Step through all pages and replace
		// try to get selected annotations
		let anns = t.selectedAnnots;
		// if nothing selected, get all annotations
		if (0==anns.length)
			anns = t.getAnnots();
		let replacements = 0;
		for (let ann of anns){
			let aProps = ann.richContents;
			// maybe use aProps.reduce( (a,i) => a+i.text,'') on the rich text before the find replace?
			// a possible solution: http://james.padolsey.com/javascript/replacing-text-in-the-dom-solved/
			// uses an index on the joined text for each match, which then is replaced sequentially on the elements
			if (aProps.length>0) {
				// working with rich contents
				let changed = false;
				for (let i=0; i<aProps.length; i++) {
					if (aProps[i] && fRE.test(aProps[i].text)) {
						aProps[i].text = aProps[i].text.replace(fRE,reTx);
						changed = true;
						replacements++;
					}
				}
				if (changed) ann.richContents = aProps;
			} else {
				// no rich contents
				aProps = ann.contents;
				if (aProps && fRE.test(aProps)){
					aProps=aProps.replace(fRE,reTx);
					ann.contents = aProps;
					replacements++;
				}
			}
		}
		return "Replaced in "+replacements+" locations.";
	}
}
// Add trusted functions to access global variables
replaceAnnotTx.global = new class {
	constructor(name) {
		this.get = app.trustedFunction(() => {
			app.beginPriv();
			try{
			if ( global[name] )
				return JSON.parse( global[name] );
			}catch{
				console.println("Error accessing global variable '"+name+"'. Try:\n either uncheck 'Enable global object security policy',\n or (preferably) edit the file 'GlobData': Delete the line that begins with /D after the line \n/"+name+" <<");
			}});
		this.set = app.trustedFunction( value => {
			app.beginPriv();
			try{
			global[name] = JSON.stringify( value );
			global.setPersistent( name, true);
			}catch{}});
	}
}("FindReplaceGlobalVals")
Last edited by Mathew on Wed Mar 20, 2024 9:49 pm, edited 1 time in total.
User avatar
Paul - Tracker Supp
Site Admin
Posts: 6903
Joined: Wed Mar 25, 2009 10:37 pm
Location: Chemainus, Canada

Tool: Find and Replace text in markup

Post by Paul - Tracker Supp »

:)
Best regards

Paul O'Rorke
Tracker Support North America
http://www.tracker-software.com
User avatar
Mirada2000
User
Posts: 12
Joined: Wed Feb 14, 2024 4:14 pm

Re: Tool: Find and Replace text in markup

Post by Mirada2000 »

I have the same problem, unfortunately impossible to download, why not include it in PDFXChanger?
User avatar
Tracker Supp-Stefan
Site Admin
Posts: 17960
Joined: Mon Jan 12, 2009 8:07 am
Location: London

Re: Tool: Find and Replace text in markup

Post by Tracker Supp-Stefan »

Hello Mirada2000,

As discussed above - we are working on a feature that would be included in the software directly, however that is still some time away. In the mean time Mathew's JS will do a find and replace in annotations.

Please try some of the advice above on ways to download that .JS file.

Kind regards,
Stefan
User avatar
Mirada2000
User
Posts: 12
Joined: Wed Feb 14, 2024 4:14 pm

Re: Tool: Find and Replace text in markup

Post by Mirada2000 »

Hello, but can I search for and replace text in the whole pdf file with this javascript, I can't do it?
User avatar
TrackerSupp-Daniel
Site Admin
Posts: 8624
Joined: Wed Jan 03, 2018 6:52 pm

Re: Tool: Find and Replace text in markup

Post by TrackerSupp-Daniel »

Hello, Mirada2000

Unfortunately no, Javascript does not have access to base content like this. We are working on an official Find and replace feature for the future, but I cannot say when that will be ready just yet.

Kind regards,
Dan McIntyre - Support Technician
Tracker Software Products (Canada) LTD

+++++++++++++++++++++++++++++++++++
Our Web site domain and email address has changed as of 26/10/2023.
https://www.pdf-xchange.com
Support@pdf-xchange.com
MedBooster
User
Posts: 1070
Joined: Mon Nov 15, 2021 8:38 pm

Re: Tool: Find and Replace text in markup

Post by MedBooster »

Hi Mathew

how could I show the name of the tool like for the others?
image.png

PS it seems that your newest version is not included in the 1st thread post. Thanks for doing that for the other tools (threads)''

viewtopic.php?t=42190&sid=be785536f6b94 ... 47cf87b811
You do not have the required permissions to view the files attached to this post.
Wishlist
Bookmarks with page numbers
Optional fixed small icon size in the toolbar
Shift to UNLOCK aspect ratio/i]
Allow more "toolbars" to the title bar
AltGr issues with character input and keyboard shortcuts
KD952
User
Posts: 60
Joined: Mon Feb 13, 2023 6:13 am

Re: Tool: Find and Replace text in markup

Post by KD952 »

Hello MedBooster!

Just open the program with the windows Editor
image.png
and alter the code as shown:
image(1).png
Kind regards,
Daniel
You do not have the required permissions to view the files attached to this post.
User avatar
Tracker Supp-Stefan
Site Admin
Posts: 17960
Joined: Mon Jan 12, 2009 8:07 am
Location: London

Tool: Find and Replace text in markup

Post by Tracker Supp-Stefan »

:)