We're sorry Aspose doesn't work properply without JavaScript enabled.

Free Support Forum - aspose.com

How to copy formatting of replacing document

I am using aspose word for find and replace in rtf document. how can I copy all formatting of sub document (document which will be replaced by string)? Right now content is getting formatting of main document. Please refer to method copyFormatting ( marked <<<<<<<< for easy find )
This is my current code:

public String AsposeMergeRTF()// start using this method
	String result="";
		ByteArrayInputStream in=new ByteArrayInputStream(mainRTF.getBytes());
		Document doc = new Document(in);
		ByteArrayInputStream subRTFInputstreaM=new ByteArrayInputStream(subRTF.getBytes());
		Document doc1 = new Document(subRTFInputstreaM);
		Pattern pattern = Pattern.compile("");
		subRTF = Utilities.FormatNullString(subRTF);
		if(Utilities.CheckNullString(tag)!=null && tag.length()>4 && (subRTF.contains("{\\rtf1\\")|| subRTF.contains("\\pard\\plain")))
			pattern = Pattern.compile("\\[%"+tag.substring(2,tag.length()-2)+"%\\]", Pattern.CASE_INSENSITIVE);
			FindReplaceOptions opts = new FindReplaceOptions();
			opts.setReplacingCallback(new ReplaceEvaluator(doc1));
			doc.getRange().replace(pattern, "", opts);
		else if(Utilities.CheckNullString(tag)!=null && tag.length()>4)
			// simple string replacement 
			doc.getRange().replace(tag, subRTF!=null?subRTF:"", new FindReplaceOptions(FindReplaceDirection.FORWARD));// replacing the token with string
			DocumentBuilder builder = new DocumentBuilder(doc);
			doc.appendDocument(doc1, ImportFormatMode.KEEP_SOURCE_FORMATTING); // merges two documents if token doesn't exist or empty or null
		 * for (Paragraph para : (Iterable<Paragraph>)
		 * doc.getChildNodes(NodeType.PARAGRAPH, true)) { if
		 * (para.toString(SaveFormat.TEXT).trim().equals("")) { para.remove(); } }
		ByteArrayOutputStream os = new ByteArrayOutputStream();
		com.aspose.words.SaveOptions saveOptions = com.aspose.words.SaveOptions.createSaveOptions(SaveFormat.RTF);
		doc.save(os, saveOptions);
		result=new String(os.toByteArray(), StandardCharsets.UTF_8);
	} catch (Exception e) {
	return result;

Class for replacement:-

static class ReplaceEvaluator implements IReplacingCallback
	    Document doc1;
	    public ReplaceEvaluator(Document d) 
	        doc1 = d;
	    public int replacing(ReplacingArgs e) throws Exception {
	        // This is a Run node that contains either the beginning or the complete match.
	        Node currentNode = e.getMatchNode();

	        // The first (and may be the only) run can contain text before the match,
	        // in this case it is necessary to split the run.
	        if (e.getMatchOffset() > 0)
	            currentNode = splitRun((Run) currentNode, e.getMatchOffset());

	        ArrayList runs = new ArrayList();

	        // Find all runs that contain parts of the match string.
	        int remainingLength = e.getMatch().group().length();
	        while ((remainingLength > 0) && (currentNode != null) && (currentNode.getText().length() <= remainingLength)) {
	            remainingLength = remainingLength - currentNode.getText().length();

	            // Select the next Run node.
	            // Have to loop because there could be other nodes such as BookmarkStart etc.
	            do {
	                currentNode = currentNode.getNextSibling();
	            } while ((currentNode != null) && (currentNode.getNodeType() != NodeType.RUN));

	        // Split the last run that contains the match if there is any text left.
	        if ((currentNode != null) && (remainingLength > 0)) {
	            splitRun((Run) currentNode, remainingLength);

	        //// to insert Table
	        DocumentBuilder builder = new DocumentBuilder((Document) e.getMatchNode().getDocument());
	        Run runFound = (Run) runs.get(0);

	        for (Run run : (Iterable<Run>) doc1.getChildNodes(NodeType.RUN, true)) {
	            copyFormatting(runFound.getFont(), run.getFont());

	        builder.insertDocument(doc1, ImportFormatMode.USE_DESTINATION_STYLES);

	        for (Run run : (Iterable<Run>) runs)

	        // Signal to the replace engine to do nothing because we have already done all what we wanted.
	        return ReplaceAction.SKIP;
    private Run splitRun(Run run, int position) throws Exception {
        Run afterRun = (Run) run.deepClone(true);
        run.setText(run.getText().substring((0), (0) + (position)));
        run.getParentNode().insertAfter(afterRun, run);
        return afterRun;

// This method I use to copy font but how to change this to copy bold and other attributes <<<<<<<<<<

    public static void copyFormatting(Object source, Object dest) throws Exception {
        if (source.getClass() != dest.getClass())
            throw new Exception("All objects must be of the same type");

        Method methodlist[] = source.getClass().getDeclaredMethods();

        // Iterate through each property in the source object.
        for (Method prop : methodlist) {
            // Continue processing only if the method starts with ‘get’.
            if (!prop.getName().startsWith("get"))

            // Skip indexed access items. Skip setting the internals of a style as these should not be changed.
            if (prop.getName() == "getItem" || prop.getName() == "getStyle" ||
                    prop.getName() == "getStyleName" || prop.getName() == "getStyleIdentifier")

            Object value;

            // Wrap this call as it can throw an exception. Skip if thrown
            try {
                // Get value by invoking getter method.
                value = prop.invoke(source);
            } catch (Exception e) {

            // Get the corresponding setter method.
            Method setter = null;
            try {
                setter = source.getClass().getDeclaredMethod(prop.getName().replace("get", "set"), prop.getReturnType());
            } catch (Exception e) {
                // do nothing if throws.

            // Skip if value can not be retrieved.
            if (value != null) {
                // If this property returns a class which belongs to the
                if (prop.getReturnType().getPackage() != null && prop.getReturnType().getPackage().getName().equals("com.aspose.words")) {
                    // Recurse into this class.
                    copyFormatting(prop.invoke(source), prop.invoke(dest));
                } else if (setter != null) {
                    // If we can write to this property then copy the value across.
                    setter.invoke(dest, prop.invoke(source));

Please let me know how to change copyformatting or other update to my code for copying formatting from sub document. Currently sub document content is copied at correct place but its font has been matched with main document.

Rahul Bhole


To ensure a timely and accurate response, please attach the following resources here for testing:

  • Your input Word documents.
  • Please attach the output Word file that shows the undesired behavior.
  • Please attach the expected output Word file that shows the desired behavior.
  • Please create a simple Java application ( source code without compilation errors ) that helps us to reproduce your problem on our end and attach it here for testing.

As soon as you get these pieces of information ready, we will start investigation into your issue and provide you more information. Thanks for your cooperation.

PS: To attach these resources, please zip and upload them.

Hi Tahir,
Thank you for quick reply. I have attached information requested in format you asked. Please find the attachment.Replacement Issue Aspose.zip (21.7 KB)

This is urgent issue, thank you for your quick response.


We are working over your query and will get back to you soon.


We tested the scenario and noticed the you are facing the expected behavior of Aspose.Words. The runFound has Black color and size 12.0 and it is applied to inserted Run nodes in copyFormatting method.

In your case, we suggest you please do not use copyFormatting method. You can get the expected output document without using this method.