diff --git a/Markdown.pl b/Markdown.pl
index f63a85c..0ddb0b2 100755
--- a/Markdown.pl
+++ b/Markdown.pl
@@ -36,7 +36,7 @@ my ($hasxml, $hasxml_err); BEGIN { ($hasxml, $hasxml_err) = (0, "") }
 my ($hasxmlp, $hasxmlp_err); BEGIN { ($hasxmlp, $hasxmlp_err) = (0, "") }
 @ISA = qw(Exporter);
 @EXPORT_OK = qw(Markdown ProcessRaw GenerateStyleSheet SetWikiOpts SplitURL
-		escapeXML unescapeXML);
+		escapeXML unescapeXML ResolveFragment);
 $INC{__PACKAGE__.'.pm'} = $INC{basename(__FILE__)} unless exists $INC{__PACKAGE__.'.pm'};
 
 close(DATA) if fileno(DATA);
@@ -751,6 +751,7 @@ sub ProcessRaw {
 #               fancy style sheet when calling Markdown directly.
 #   auto_number => <= 0 (default) no numbering, 1 number h1s,
 #               2 number h1s, h2s, 3 number h1-h3s, ... >= 6 number h1-h6s
+#   anchors    => existence of this key triggers return of anchors HASH
 #   yamlmode   => 0 (no YAML processing), > 0 (YAML on), < 0 (YAML ignore)
 #               if 0, the YAML front matter processor is completely
 #               disabled and any YAML front matter that might be present
@@ -827,6 +828,18 @@ sub ProcessRaw {
 #  The following are OUTPUT values that can only be retrieved when
 #  Markdown is called with a HASH ref as the second argument
 #
+#   anchors    => if the 'anchors' key exists in the input HASH ref
+#               will be set to a HASH ref containing lookup keys
+#               for valid fragment ids in the document (only those
+#               created from Markdown markup) with the value the
+#               actual fragment link to use.  Do not use this directly
+#               but pass it as the first argument to the ResolveFragment
+#               function to resolve a "fuzzy" fragment name to its
+#               actual fragment name in the generated output.
+#               NOTE: to activate return of anchors the 'anchors' key
+#               simply must exist in the input HASH ref passed to the
+#               Markdown function, its value will be replaced on output.
+#
 #   h1         => will be set to the tag-stripped value of the first
 #               non-empty H1 generated by Markdown-style markup.
 #               note that literal <h1>...</h1> values are NOT picked up.
@@ -912,6 +925,11 @@ sub _SanitizeOpts {
     $o->{yamlmode} = -1 unless looks_like_number($o->{yamlmode});
     $o->{yamlvis} = 0 unless looks_like_number($o->{yamlvis});
     delete $o->{yaml};
+
+    # The anchors hash will only be returned if the key exists
+    # (the key's value doesn't matter), set the value to an empty
+    # HASH ref just in case to make sure it's always a HASH ref.
+    $o->{anchors} = {} if exists($o->{anchors});
 }
 
 my %_yamlopts;
@@ -1064,6 +1082,7 @@ sub Markdown {
 
     utf8::encode($text);
     if (ref($_[0]) eq "HASH") {
+	${$_[0]}{anchors} = {%g_anchors_id} if exists(${$_[0]}{anchors});
 	if (defined($opt{h1}) && $opt{h1}) {
 	    utf8::encode($opt{h1});
 	    ${$_[0]}{h1} = $opt{h1};
@@ -1735,28 +1754,29 @@ sub _SplitUrlTitlePart {
 }
 
 
-sub _FindFragmentMatch {
-    my $url = shift;
+sub _FindFragmentMatchInternal {
+    my ($anchors_id, $url, $undefifnomatch) = @_;
     if (defined($url) && $url =~ /^#\S/) {
 	# try very hard to find a match
 	my $idbase = _strip(lc(substr($url, 1)));
 	my $idbase0 = $idbase;
 	my $id = _MakeAnchorId($idbase);
-	if (defined($g_anchors_id{$id})) {
-	    $url = $g_anchors_id{$id};
+	$undefifnomatch and $url = undef;
+	if (defined($$anchors_id{$id})) {
+	    $url = $$anchors_id{$id};
 	} else {
 	    $idbase =~ s/-/_/gs;
 	    $id = _MakeAnchorId($idbase);
-	    if (defined($g_anchors_id{$id})) {
-		$url = $g_anchors_id{$id};
+	    if (defined($$anchors_id{$id})) {
+		$url = $$anchors_id{$id};
 	    } else {
 		$id = _MakeAnchorId($idbase0, 1);
-		if (defined($g_anchors_id{$id})) {
-		    $url = $g_anchors_id{$id};
+		if (defined($$anchors_id{$id})) {
+		    $url = $$anchors_id{$id};
 		} else {
 		    $id = _MakeAnchorId($idbase, 1);
-		    if (defined($g_anchors_id{$id})) {
-			$url = $g_anchors_id{$id};
+		    if (defined($$anchors_id{$id})) {
+			$url = $$anchors_id{$id};
 		    }
 		}
 	    }
@@ -1766,6 +1786,47 @@ sub _FindFragmentMatch {
 }
 
 
+sub _FindFragmentMatch {
+    return _FindFragmentMatchInternal(\%g_anchors_id, @_);
+}
+
+
+sub _ToUTF8 {
+    my $input = shift;
+    my $output;
+    if (Encode::is_utf8($input) || utf8::decode($input)) {
+	$output = $input;
+    } else {
+	$output = $encoder->decode($input, Encode::FB_DEFAULT);
+    }
+    return $output;
+}
+
+
+# $_[0] -> HASH ref of anchors (e.g. the "anchors" OUTPUT from Markdown)
+# $_[1] -> fragment to resolve, may optionally start with '#'
+# An empty string ("") or hash ("#") is returned as-is.
+# returns undef if no match otherwise resolved fragment name
+# which will start with a '#' if $_[1] started with '#' otherwise will not.
+# This function can be used to connect up links to "implicit" anchors.
+# All Markdown-format H1-H6 headers have an implicit anchor added
+# based on the header item text.  Passing that text to this function
+# will cough up the matching implicit anchor if there is one.
+sub ResolveFragment
+{
+    my ($anchors, $frag) = @_;
+    defined($frag) or return undef;
+    $frag eq "" || $frag eq "#" and return $frag;
+    my $hadhash = ($frag =~ s/^#//);
+    $frag =~ /^\S/ or return undef;
+    ref($anchors) eq 'HASH' or return undef;
+    my $ans = _FindFragmentMatchInternal($anchors, '#'._ToUTF8($frag), 1);
+    $hadhash || !defined($ans) or $ans =~ s/^#//;
+    defined($ans) and utf8::encode($ans);
+    return $ans;
+}
+
+
 # Return a suitably encoded <img...> tag string
 # On input NONE of $url, $alt or $title should be xmlencoded
 # but $url should already be url-encoded if needed, but NOT g_escape_table'd