Skip to content

Instantly share code, notes, and snippets.

@akafael
Created April 18, 2020 01:37
Show Gist options
  • Save akafael/93a9acb15e5cc57ca3478e961e5431eb to your computer and use it in GitHub Desktop.
Save akafael/93a9acb15e5cc57ca3478e961e5431eb to your computer and use it in GitHub Desktop.
Convert FreeMind maps to orgmode using XSLT
<?xml version="1.0" encoding="UTF-8" ?>
<!--
MINDMAPEXPORTFILTER org;orgmode Orgmode
v. 0.1
This code released under the GPL. : (http://www.gnu.org/copyleft/gpl.html)
Document : mm2orgmode.xsl
Created on : 17 April, 2020
Authors : Rafael Lima
Description: Transforms freeplane mm to orgmode orgmode.
* Nodes become headings and subheadings, Notes become paragraphs.
* Attributes of root node become document metadata.
* Details are not handled.
* HTML tables are converted to Pandoc pipe table format.
* Tested with Emacs.
May not work:
* Orgmode internal links
Please test and suggest improvements to author, or feel free to customize
while crediting previous authors.
******************************************************************************
Based on mm2markdown.xsl and mm2text.xsl, original notice appears below
******************************************************************************
This code released under the GPL. : (http://www.gnu.org/copyleft/gpl.html)
Document : mm2markdown.xsl
Created on : 20 November, 2013
Authors : Lee Hachadoorian Lee.Hachadoorian@gmail.com and Peter Yates pyates@gmail.com
Description: Transforms freeplane mm to markdown md.
Document : mm2text.xsl
Created on : 01 February 2004, 17:17
Author : joerg feuerhake joerg.feuerhake@free-penguin.org
Description: transforms freeplane mm
format to html, handles crossrefs and adds numbering. feel free to
customize it while leaving the ancient authors mentioned. thank you
ChangeLog: See: http://freeplane.sourceforge.net/
-->
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text" indent="no"/>
<xsl:strip-space elements="map node" />
<xsl:key name="refid" match="node" use="@ID" />
<xsl:template match="/">
<xsl:apply-templates />
</xsl:template>
<!-- Create nodes as headlines in orgmode -->
<xsl:template match="/map">
<xsl:apply-templates select="node"/>
</xsl:template>
<!-- Create nodes as headlines in orgmode -->
<xsl:template match="node">
<xsl:variable name="thisid" select="@ID" />
<xsl:variable name="target" select="arrowlink/@DESTINATION" />
<xsl:choose>
<!-- Root node attributes create ATRIBUTE block -->
<xsl:when test="count(ancestor::*) = 1">
<!-- Only create yaml block if there are attributes -->
<xsl:if test="count(attribute) > 0">
<xsl:text>&#xA;---&#xA;</xsl:text>
<xsl:apply-templates select="attribute" />
<xsl:text>---</xsl:text>
</xsl:if>
<!-- Use root node text as document TITLE key -->
<xsl:text>#+TITLE: </xsl:text>
</xsl:when>
<!-- Create the headers from non-root node text using star signs -->
<xsl:otherwise>
<xsl:text>&#xA;</xsl:text>
<xsl:call-template name="starSign">
<xsl:with-param name="howMany" select="count(ancestor::*) - 1"/>
</xsl:call-template>
<xsl:text> </xsl:text>
</xsl:otherwise>
</xsl:choose>
<!-- Node text -->
<xsl:if test="@TEXT">
<xsl:value-of select="normalize-space(@TEXT)" />
<xsl:text>&#xA;</xsl:text>
</xsl:if>
<!-- Convert notes as paragraph -->
<xsl:apply-templates select="richcontent[@TYPE='NODE']"/>
<xsl:apply-templates select="richcontent[@TYPE='DETAILS']"/>
<xsl:apply-templates select="richcontent[@TYPE='NOTE']"/>
<!-- Convert arrow links between nodes to reference and internal doc link -->
<xsl:if test="arrowlink/@DESTINATION != ''">
<xsl:text> ref: </xsl:text>
<xsl:for-each select="key('refid', $target)">
<xsl:text>[[</xsl:text>
<xsl:value-of select="@TEXT" />
<xsl:text>]]</xsl:text>
</xsl:for-each>
<xsl:text>&#xA;</xsl:text>
</xsl:if>
<xsl:apply-templates select="node"/>
</xsl:template>
<!-- Attributes template -->
<xsl:template match="attribute">
<!-- Define Meta using key sintax -->
<xsl:text>#+</xsl:text>
<!-- Translate to Upcase -->
<xsl:variable name="lowercase" select="'abcdefghijklmnopqrstuvwxyz'" />
<xsl:variable name="uppercase" select="'ABCDEFGHIJKLMNOPQRSTUVWXYZ'" />
<xsl:value-of select="translate(@NAME, $lowercase,$uppercase)" />: '<xsl:value-of select="@VALUE" />'
</xsl:template>
<!-- Template to print header *signs for each node-->
<xsl:template name="starSign">
<xsl:param name="howMany">1</xsl:param>
<xsl:if test="$howMany &gt; 0">
<!-- Add 1 star signs (*) to result tree. -->
<xsl:text>*</xsl:text>
<!-- Print remaining ($howMany - 1) star signs. -->
<xsl:call-template name="starSign">
<xsl:with-param name="howMany" select="$howMany - 1"/>
</xsl:call-template>
</xsl:if>
</xsl:template>
<!-- Create paragraph -->
<xsl:template match="richcontent">
<xsl:text>&#xA;</xsl:text>
<xsl:apply-templates />
<xsl:text>&#xA;</xsl:text>
</xsl:template>
<!-- Normalizer space -->
<xsl:template match="child::text()">
<xsl:value-of select="translate(normalize-space(.),'&#160;',' ')" />
</xsl:template>
<!-- Insert newline for html breaks, paras, etc. -->
<xsl:template match="p|br|div|li|pre">
<xsl:if test="preceding-sibling::*">
<xsl:text>&#xA;</xsl:text>
</xsl:if>
<xsl:apply-templates/>
</xsl:template>
<!-- Convert Italics to orgmode syntax -->
<xsl:template match="i|em">
<xsl:text> /</xsl:text>
<xsl:apply-templates/>
<xsl:text>/ </xsl:text>
</xsl:template>
<!-- Convert Bold to orgmode syntax -->
<xsl:template match="b|strong">
<xsl:text> **</xsl:text>
<xsl:apply-templates/>
<xsl:text>** </xsl:text>
</xsl:template>
<!-- Strikethrough formatting -->
<xsl:template match="strike">
<xsl:text> ++</xsl:text>
<xsl:apply-templates/>
<xsl:text>++ </xsl:text>
</xsl:template>
<!-- Subscript formatting -->
<xsl:template match="sub">
<xsl:text> _{</xsl:text>
<xsl:apply-templates/>
<xsl:text>} </xsl:text>
</xsl:template>
<!-- Superscript formatting -->
<xsl:template match="sup">
<xsl:text> ^{</xsl:text>
<xsl:apply-templates/>
<xsl:text>} </xsl:text>
</xsl:template>
<!-- Convert <hr> to orgmode horizontal rule -->
<xsl:template match="hr">
<xsl:text>&#xA;-----&#xA;</xsl:text>
</xsl:template>
<!-- Convert html table to pandoc pipe table -->
<xsl:template match="table">
<xsl:text>&#xA;</xsl:text>
<xsl:apply-templates />
</xsl:template>
<!-- Template to print table header separator row -->
<xsl:template name="tableHeaderDashes">
<xsl:param name="howManyCols">1</xsl:param>
<xsl:if test="$howManyCols &gt; 0">
<!-- Add left pipe and dashes to result tree. -->
<xsl:text>| ------ </xsl:text>
<!-- Print remaining ($howManyCols - 1) pipe dashes. -->
<xsl:call-template name="tableHeaderDashes">
<xsl:with-param name="howManyCols" select="$howManyCols - 1"/>
</xsl:call-template>
</xsl:if>
<xsl:if test="$howManyCols = 0">
<xsl:text>|</xsl:text>
<xsl:text>&#xA;</xsl:text>
</xsl:if>
</xsl:template>
<!-- Template to print table rows -->
<xsl:template match="tr">
<xsl:if test="count(preceding-sibling::tr)=1">
<xsl:call-template name="tableHeaderDashes">
<xsl:with-param name="howManyCols" select="count(td)" />
</xsl:call-template>
</xsl:if>
<xsl:apply-templates />
<xsl:text> |&#xA;</xsl:text>
</xsl:template>
<xsl:template match="td|th">
<xsl:text>| </xsl:text>
<xsl:apply-templates />
</xsl:template>
</xsl:stylesheet>
@akafael
Copy link
Author

akafael commented Apr 18, 2020

I created this converter to enable a faster migration alternative from my Freemind old notes to orgmode using the Freemind XLST export tool.

Current features:

Notes for my future self:

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment