diff --git a/commonmark-ext-gfm-strikethrough/src/main/java/org/commonmark/ext/gfm/strikethrough/StrikethroughExtension.java b/commonmark-ext-gfm-strikethrough/src/main/java/org/commonmark/ext/gfm/strikethrough/StrikethroughExtension.java
index aa7dff716fdf4dc39ab6126e43af92c12506e05c..f87f3e9c8860418984811b312e4d9f2fede31245 100644
--- a/commonmark-ext-gfm-strikethrough/src/main/java/org/commonmark/ext/gfm/strikethrough/StrikethroughExtension.java
+++ b/commonmark-ext-gfm-strikethrough/src/main/java/org/commonmark/ext/gfm/strikethrough/StrikethroughExtension.java
@@ -17,6 +17,9 @@ import org.commonmark.renderer.text.TextContentNodeRendererContext;
 import org.commonmark.renderer.text.TextContentNodeRendererFactory;
 import org.commonmark.renderer.text.TextContentRenderer;
 
+import java.util.Collections;
+import java.util.Set;
+
 /**
  * Extension for GFM strikethrough using {@code ~} or {@code ~~} (GitHub Flavored Markdown).
  * <p>Example input:</p>
@@ -100,6 +103,11 @@ public class StrikethroughExtension implements Parser.ParserExtension, HtmlRende
             public NodeRenderer create(MarkdownNodeRendererContext context) {
                 return new StrikethroughMarkdownNodeRenderer(context);
             }
+
+            @Override
+            public Set<Character> getSpecialCharacters() {
+                return Collections.singleton('~');
+            }
         });
     }
 
diff --git a/commonmark-ext-gfm-strikethrough/src/test/java/org/commonmark/ext/gfm/strikethrough/StrikethroughMarkdownRendererTest.java b/commonmark-ext-gfm-strikethrough/src/test/java/org/commonmark/ext/gfm/strikethrough/StrikethroughMarkdownRendererTest.java
index b722018b8ad734d9338e90ccfaff8179b0d0296a..96df48cec006909806effdf837efd729e2764b10 100644
--- a/commonmark-ext-gfm-strikethrough/src/test/java/org/commonmark/ext/gfm/strikethrough/StrikethroughMarkdownRendererTest.java
+++ b/commonmark-ext-gfm-strikethrough/src/test/java/org/commonmark/ext/gfm/strikethrough/StrikethroughMarkdownRendererTest.java
@@ -19,10 +19,10 @@ public class StrikethroughMarkdownRendererTest {
     @Test
     public void testStrikethrough() {
         assertRoundTrip("~foo~ ~bar~\n");
-        assertRoundTrip("~~f~oo~~ ~~bar~~\n");
+        assertRoundTrip("~~foo~~ ~~bar~~\n");
+        assertRoundTrip("~~f\\~oo~~ ~~bar~~\n");
 
-        // TODO this new special character needs to be escaped:
-//        assertRoundTrip("\\~foo\\~\n");
+        assertRoundTrip("\\~foo\\~\n");
     }
 
     protected String render(String source) {
diff --git a/commonmark-ext-gfm-tables/src/main/java/org/commonmark/ext/gfm/tables/TablesExtension.java b/commonmark-ext-gfm-tables/src/main/java/org/commonmark/ext/gfm/tables/TablesExtension.java
index d23f6f5fc35bb6bfaa86044ded3510dd0c15550f..92e1f0ba41c0c807bf122464352704262f43a9f7 100644
--- a/commonmark-ext-gfm-tables/src/main/java/org/commonmark/ext/gfm/tables/TablesExtension.java
+++ b/commonmark-ext-gfm-tables/src/main/java/org/commonmark/ext/gfm/tables/TablesExtension.java
@@ -17,6 +17,9 @@ import org.commonmark.renderer.text.TextContentNodeRendererContext;
 import org.commonmark.renderer.text.TextContentNodeRendererFactory;
 import org.commonmark.renderer.text.TextContentRenderer;
 
+import java.util.Collections;
+import java.util.Set;
+
 /**
  * Extension for GFM tables using "|" pipes (GitHub Flavored Markdown).
  * <p>
@@ -72,6 +75,11 @@ public class TablesExtension implements Parser.ParserExtension, HtmlRenderer.Htm
             public NodeRenderer create(MarkdownNodeRendererContext context) {
                 return new TableMarkdownNodeRenderer(context);
             }
+
+            @Override
+            public Set<Character> getSpecialCharacters() {
+                return Collections.emptySet();
+            }
         });
     }
 }
diff --git a/commonmark/src/main/java/org/commonmark/internal/util/AsciiMatcher.java b/commonmark/src/main/java/org/commonmark/internal/util/AsciiMatcher.java
index d31020fa37ea1343cc3dd425c673715eb52e0535..dd7e8d5eb7c5c3fcce03ebfa00dba0008e735565 100644
--- a/commonmark/src/main/java/org/commonmark/internal/util/AsciiMatcher.java
+++ b/commonmark/src/main/java/org/commonmark/internal/util/AsciiMatcher.java
@@ -1,6 +1,7 @@
 package org.commonmark.internal.util;
 
 import java.util.BitSet;
+import java.util.Set;
 
 public class AsciiMatcher implements CharMatcher {
     private final BitSet set;
@@ -33,6 +34,14 @@ public class AsciiMatcher implements CharMatcher {
             this.set = set;
         }
 
+        public Builder c(char c) {
+            if (c > 127) {
+                throw new IllegalArgumentException("Can only match ASCII characters");
+            }
+            set.set(c);
+            return this;
+        }
+
         public Builder anyOf(String s) {
             for (int i = 0; i < s.length(); i++) {
                 c(s.charAt(i));
@@ -40,11 +49,10 @@ public class AsciiMatcher implements CharMatcher {
             return this;
         }
 
-        public Builder c(char c) {
-            if (c > 127) {
-                throw new IllegalArgumentException("Can only match ASCII characters");
+        public Builder anyOf(Set<Character> characters) {
+            for (Character c : characters) {
+                c(c);
             }
-            set.set(c);
             return this;
         }
 
diff --git a/commonmark/src/main/java/org/commonmark/renderer/markdown/CoreMarkdownNodeRenderer.java b/commonmark/src/main/java/org/commonmark/renderer/markdown/CoreMarkdownNodeRenderer.java
index c7fa9be7a48250be991655817e6aef087b2912b8..2db7ef30cb6e56486cf1e256ad50a821a7827f64 100644
--- a/commonmark/src/main/java/org/commonmark/renderer/markdown/CoreMarkdownNodeRenderer.java
+++ b/commonmark/src/main/java/org/commonmark/renderer/markdown/CoreMarkdownNodeRenderer.java
@@ -22,10 +22,8 @@ import java.util.regex.Pattern;
  */
 public class CoreMarkdownNodeRenderer extends AbstractVisitor implements NodeRenderer {
 
-    private final AsciiMatcher textEscape =
-            AsciiMatcher.builder().anyOf("[]<>`*&\n\\").build();
-    private final CharMatcher textEscapeInHeading =
-            AsciiMatcher.builder(textEscape).anyOf("#").build();
+    private final AsciiMatcher textEscape;
+    private final CharMatcher textEscapeInHeading;
     private final CharMatcher linkDestinationNeedsAngleBrackets =
             AsciiMatcher.builder().c(' ').c('(').c(')').c('<').c('>').c('\n').c('\\').build();
     private final CharMatcher linkDestinationEscapeInAngleBrackets =
@@ -46,6 +44,9 @@ public class CoreMarkdownNodeRenderer extends AbstractVisitor implements NodeRen
     public CoreMarkdownNodeRenderer(MarkdownNodeRendererContext context) {
         this.context = context;
         this.writer = context.getWriter();
+
+        textEscape = AsciiMatcher.builder().anyOf("[]<>`*&\n\\").anyOf(context.getSpecialCharacters()).build();
+        textEscapeInHeading = AsciiMatcher.builder(textEscape).anyOf("#").build();
     }
 
     @Override
diff --git a/commonmark/src/main/java/org/commonmark/renderer/markdown/MarkdownNodeRendererContext.java b/commonmark/src/main/java/org/commonmark/renderer/markdown/MarkdownNodeRendererContext.java
index 8fe0f73d52a025efe9fbe632d0f584a761082184..5805c458b0ee4b38a2aaf7386ef013050767fd32 100644
--- a/commonmark/src/main/java/org/commonmark/renderer/markdown/MarkdownNodeRendererContext.java
+++ b/commonmark/src/main/java/org/commonmark/renderer/markdown/MarkdownNodeRendererContext.java
@@ -2,6 +2,8 @@ package org.commonmark.renderer.markdown;
 
 import org.commonmark.node.Node;
 
+import java.util.Set;
+
 public interface MarkdownNodeRendererContext {
 
     /**
@@ -16,4 +18,10 @@ public interface MarkdownNodeRendererContext {
      * @param node the node to render
      */
     void render(Node node);
+
+    /**
+     * @return additional special characters that need to be escaped if they occur in normal text; currently only ASCII
+     * characters are allowed
+     */
+    Set<Character> getSpecialCharacters();
 }
diff --git a/commonmark/src/main/java/org/commonmark/renderer/markdown/MarkdownNodeRendererFactory.java b/commonmark/src/main/java/org/commonmark/renderer/markdown/MarkdownNodeRendererFactory.java
index 7b3134277cad55814dea0e2f863cb941497776ec..adfe8a07b7cd7f2cb5f1676202eaf0400bea791e 100644
--- a/commonmark/src/main/java/org/commonmark/renderer/markdown/MarkdownNodeRendererFactory.java
+++ b/commonmark/src/main/java/org/commonmark/renderer/markdown/MarkdownNodeRendererFactory.java
@@ -2,6 +2,8 @@ package org.commonmark.renderer.markdown;
 
 import org.commonmark.renderer.NodeRenderer;
 
+import java.util.Set;
+
 /**
  * Factory for instantiating new node renderers ƒor rendering.
  */
@@ -14,4 +16,10 @@ public interface MarkdownNodeRendererFactory {
      * @return a node renderer
      */
     NodeRenderer create(MarkdownNodeRendererContext context);
+
+    /**
+     * @return the additional special characters that this factory would like to have escaped in normal text; currently
+     * only ASCII characters are allowed
+     */
+    Set<Character> getSpecialCharacters();
 }
diff --git a/commonmark/src/main/java/org/commonmark/renderer/markdown/MarkdownRenderer.java b/commonmark/src/main/java/org/commonmark/renderer/markdown/MarkdownRenderer.java
index 4dee17ed622173f8be7a2933d852c79c03c451c2..926105202e8f20c3a36e5481c7ed62604ed66f05 100644
--- a/commonmark/src/main/java/org/commonmark/renderer/markdown/MarkdownRenderer.java
+++ b/commonmark/src/main/java/org/commonmark/renderer/markdown/MarkdownRenderer.java
@@ -6,8 +6,7 @@ import org.commonmark.node.Node;
 import org.commonmark.renderer.NodeRenderer;
 import org.commonmark.renderer.Renderer;
 
-import java.util.ArrayList;
-import java.util.List;
+import java.util.*;
 
 /**
  * Renders nodes to CommonMark Markdown.
@@ -32,6 +31,11 @@ public class MarkdownRenderer implements Renderer {
             public NodeRenderer create(MarkdownNodeRendererContext context) {
                 return new CoreMarkdownNodeRenderer(context);
             }
+
+            @Override
+            public Set<Character> getSpecialCharacters() {
+                return Collections.emptySet();
+            }
         });
     }
 
@@ -111,13 +115,21 @@ public class MarkdownRenderer implements Renderer {
     private class RendererContext implements MarkdownNodeRendererContext {
         private final MarkdownWriter writer;
         private final NodeRendererMap nodeRendererMap = new NodeRendererMap();
+        private final Set<Character> additionalTextEscapes;
 
         private RendererContext(MarkdownWriter writer) {
+            // Set fields that are used by interface
             this.writer = writer;
+            Set<Character> escapes = new HashSet<Character>();
+            for (MarkdownNodeRendererFactory factory : nodeRendererFactories) {
+                escapes.addAll(factory.getSpecialCharacters());
+            }
+            additionalTextEscapes = Collections.unmodifiableSet(escapes);
 
             // The first node renderer for a node type "wins".
             for (int i = nodeRendererFactories.size() - 1; i >= 0; i--) {
                 MarkdownNodeRendererFactory nodeRendererFactory = nodeRendererFactories.get(i);
+                // Pass in this as context here, which uses the fields set above
                 NodeRenderer nodeRenderer = nodeRendererFactory.create(this);
                 nodeRendererMap.add(nodeRenderer);
             }
@@ -132,5 +144,10 @@ public class MarkdownRenderer implements Renderer {
         public void render(Node node) {
             nodeRendererMap.render(node);
         }
+
+        @Override
+        public Set<Character> getSpecialCharacters() {
+            return additionalTextEscapes;
+        }
     }
 }