Feature request: Export TP to Word Outline

Hello,

Ultimately, i find that i need to move some TP outlines to Word for proper document writing. It would be convenient to export a TP doc so that each outline level ends up as corresponding Header level in Word, including the automatic removal of the tabs and the - and : characters that indicate projects and notes (but not other instances of dashes and colons in the text itself).

Now, if i paste a TP doc into Word, i have to do a lot of reformatting.

Would you consider a script? You can use the Ruby library to produce Markdown and then use Pandoc. Your script can process both so you just have to run it once to produce the results you want. Check the library and if you have any questions, let me know. I might be able to help.

Thank you. I haven’t run Ruby before, but i’m happy to give it a try. i don’t see a download link though.

Here is an old script, which still seems to work, for exporting as OPML through Jesse’s ItemSerializer interface.

(It’s JavaScript for Automation, which should work in Script Editor, for example, with the language tab at top left set to JavaScript)

Pandoc should then get you from OPML to a docx outline, I think.

(function(dctOptions) {
    'use strict';

    // TASKPAPER CONTEXT

    function serialized(editor, options) {
        return ItemSerializer.serializeItems(
            editor.outline.items,
            editor,
            ItemSerializer[
                options.format.toUpperCase() + 'MimeType'
            ]
        );
    }

    // JAVASCRIPT FOR AUTOMATION CONTEXT
    // writeFile :: FilePath -> String -> IO ()
    function writeFile(strPath, strText) {
        $.NSString.alloc.initWithUTF8String(strText)
            .writeToFileAtomicallyEncodingError(
                $(strPath)
                .stringByStandardizingPath, false,
                $.NSUTF8StringEncoding, null
            );
    }

    function exportFromTP3(docTP3, strPath, strFormat) {
        var strSerial = docTP3.evaluate({
            script: serialized.toString(),
            withOptions: {
                format: strFormat
            }
        });

        return (
            writeFile(strPath, strSerial),
            strSerial
        );
    }

    var tp3 = Application('TaskPaper'),
        ds = tp3.documents;

    if (ds.length) {
        var d = ds[0],
            a = Application('System Events'),
            sa = (a.includeStandardAdditions = true, a),

            pathFile = d.file(),
            lstParts = pathFile ? pathFile.toString()
            .split('/') : undefined,

            strFolder = lstParts ? (
                lstParts.slice(0, -1)
                .join('/') + '/'
            ) : undefined,

            strFormat = dctOptions.format.toLowerCase(),
            lstSupported = ['opml', 'bml', 'text'],
            strKnownFormat = (
                lstSupported
                .indexOf(strFormat) !== -1 ? strFormat : undefined
            );

        if (strKnownFormat) {
            sa.activate();
            var pathWrite = sa.chooseFileName({
                withPrompt: 'Save as ' + strKnownFormat.toUpperCase(),
                defaultName: strFolder ? (
                    lstParts.slice(-1)[0].split('.')[0] + '.' +
                    strKnownFormat
                ) : 'Untitled.' + strKnownFormat,
                defaultLocation: strFolder ? (
                    Path(strFolder)
                ) : sa.pathTo('desktop')
            });

            return pathWrite ? (
                exportFromTP3(d, pathWrite.toString(), strKnownFormat)
            ) : undefined;
        } else {
            return "Unknown format: '" + dctOptions.format + "'" +
                "\nExpected: ( '" + lstSupported.join("' | '") + "' )";
        };

    } else return undefined;

})({
    format: 'opml'
});

// 'bml' or 'BML'  (HTML outline e.g. FoldingText for Atom)
// 'opml' or 'OPML'

Hey, thanks very much :-). i’ve got the taskpaper to opml bit working. now have to go learn something about pandoc.

1 Like

I want to chime in to say that, TaskPaper is used by some of us as writers to organize our writing projects, and then, export them to a proper word processing format for publication and distribution.

I appreciate the scripted solution offered here. I have not yet understood enough to be comfortable using scripting for such purposes in TaskPaper. But I can see how this is possible. I find that very encouraging.

I want to suggest that, as I have been posting in other threads, - and taking nothing away from WriteRoom and its followers, that TaskPaper is a great writing tool for writer’s. It is great for organizing my writing - and then, exporting what I have written to a word processor.

Writing takes a tremendous amount of organizational brain power. Not only are you collecting ideas and fleshing them out, you are also tracking submission deadlines, checking off tasks, etc. It is also great the way I can collapse sections I don’t want to see in front of me yet find information in sections in an instant using tags and saved searches. As previously mentioned, global searches would be a great addition to TaskPaper so that when something is collapsed you can still find it easily in the document.

Yes, WriteRoom is the app Jessie made to write in. And, TaskPaper is also a great writing app that Jessie made for writers who are writing for a different reason than those who are writing in WriteRoom. I don’t see any conflict in saying that TaskPaper is a great writer’s app when Jessie already offers a writing app in WriteRoom. As I see it, they serve different purposes. WriteRoom is for writing from start to finish. TaskPaper is an organizational, plain text, writer’s tool.

Long live TaskPaper.

Hey Doug, sorry for the late response. Here is the link to the library. You might be able to simply produce the HTML file and then copy and paste into Word without the additional step. There is a markdown export option there that you can use with something like Marked and get the Word document you want without an additional step too.

Ruby library

Let me know if you need any additional help.

Thanks very much Victor. Glad to see this too!

i tried using the script, but i got an error message looking for some *.less files. i looked through the script, and i cannot find where it looks for these files. (i’ve never read ruby before)

ruby ~/src/TaskPaperRuby/taskpaper-to-html.rb ~/Desktop/Projects_2018.taskpaper ~/Desktop/Proj
ects_2018.html ~/Library/Application\ Support/TaskPaper/StyleSheets/Atom-One-Dark-Blue.less

Couldn’t find file: /Applications/TaskPaper.app/Contents/Resources/base.less
Couldn’t find file: …/Library/Application Support/TaskPaper/theme.less
NameError: variable @text-color is undefined in /tmp/taskpaperthemeconvertertemp.txt on line 4, column 9:

3 li[data-type=task], li[data-type=note] {
4 color: @text-color;
5 }

this is above my capacity now (I’m not a programmer, just slightly better than the average user).

but i’ve managed to get the taskpaper to javascript to opml to pandoc to docx pipeline working.

pandoc ~/Desktop/Projects_2018.opml -f opml -t docx -o Projects_2018.docx

(and open in outline view in Word)

the task dashes are still there, but i think i can figure out to remove just those.

Hopefully, this is something that Jesse sees as worthwhile to add as a menu item in TP4!

Thank you complexpoint and Victor!

Sorry man. I know the feeling. Let me see if I can help you out. This may only take you 5-10 minutes but it will simplify your life. You will only have to run the script once and get what you need.

  1. I am assuming you have already installed pandoc and ruby, and downloaded the TaskPaper-Ruby library. Now install this gem for ruby like this,
    gem install pandoc-ruby

  2. Inside your TaskPaper-Ruby library, create a file INSIDE the src directory with the name taskpaper+mmd.rb

  3. Include this source in that file,

#!/usr/bin/ruby

# TaskPaperRuby
#
# This extends Matt's library to produce Markdown
#
# Made by Victor Gutierrez

require_relative 'taskpaperdocument'

class TaskPaperDocument
  def to_mmd(only_type = nil)
    @root_item ? @root_item.to_mmd(only_type) : ''
  end
end

require_relative 'taskpaperitem'

class TaskPaperItem
  def to_mmd(only_type = nil)
    output = ''
    if @type != TYPE_NULL
      posn = 0
      # This is pretty self-explanatory. It doesn't work perfectly with the projects since it leaves an empty space
      remove_all_tags
      if @type == TYPE_TASK
        output += @content.to_s
        # This removes the task identifier which happens to be `-`. In order to do that, we remove the first
        # two characters in the string
        # output += (@content[2..-1]).to_s
      end
      if @type == TYPE_NOTE
        # Here the notes will be italiziced.
        output += "*#{@content}*"
      end
      if @type == TYPE_PROJECT
        # First, add an extra line before a project begins to create a better separation.
        output += TaskPaperItem.linebreak.to_s

        # This right here will add the right heading level according to where the project is in TaskPaper
        $i = -1
        begin
          output += '#'
          $i += 1
        end while $i < effective_level

        # Since we don't want the project marker from taskpaper which is `:` at the end of the string, we remove it
        output += " #{@content[posn..-2]}"

      end

      # Add a space after every item.
      output += TaskPaperItem.linebreak.to_s
    end
    @children.each do |child|
      output += child.to_mmd(only_type)
    end
    output = output.to_s
  end
end

The source is kind of self explanatory. I documented a couple of changes that you might want to make. I also made it easy to see the changes. So that for example, in the TYPE_NOTE, the text is italicized using Markdown. If you prefer to do bold or something, just change that using markdown.

Right now I also made it so that it removes all tags, but you can change that by removing the remove_all_tags line. I would recommend simply to document that line so you don’t forget later on.

Now, for the next part,

  1. Create a file inside your TaskPaper-Ruby library. Now, this is NOT INSIDE YOUR src, but the path above. Name it something you would remember. I am going to use, taskpaper-to-mmd.rb

  2. Add the following code,

#!/usr/bin/ruby

# TaskPaperRuby
#
# This script converts TaskPaper files to DOCX.
#
# Made by Victor Gutierrez
#
github_url = "http://github.com/mattgemmell/TaskPaperRuby"
#
# ==========================================================

require_relative 'src/taskpaperdocument'
require_relative 'src/taskpaper+mmd'
require 'pandoc-ruby'

# Handle command line arguments
if ARGV.count < 2
	puts "Usage: ruby taskpaper-to-mmd.rb INPUT_FILE_PATH OUTPUT_FILE_PATH"
	puts "(Input file should be a TaskPaper file. Output will be an Open Doc.)"
	exit
end

input_file_path = File.expand_path(ARGV[0])
docx_output_file_path = File.expand_path(ARGV[1])

# Ensure we have an input file to work with
if !File.exist?(input_file_path)
	puts "Couldn't find input file \"#{input_file_path}\". ¯\\_(ツ)_/¯"
	exit
end

# Load TaskPaper file
document = TaskPaperDocument.new(input_file_path)

# Produce MMD output from document
document_mmd = document.to_mmd

// Now, here we are using the Pandoc ruby library to write a docx file from the mmd file above
File.open(docx_output_file_path, 'w') do |outfile|
  outfile.puts PandocRuby.markdown(document_mmd).to_docx
end

// That is all!

Okay. The next step is easy, simply run your script. Something like

ruby ~/location_script/taskpaper-to-mmd.rb ~/location/input_taskpaper_file.taskpaper ~/location/output_docx_file.docx

It should work. Tell me if that helps.

that i should definitely be able to do. Thanks very much Victor!

Hey, try my code and see if you can get it to work. Maybe this is something you can use.

Thanks, I will consider this for future reference. Right now I have no need for his as all my writing is in TaskPaper.

What would be helpful for TaskPaper users would be for someone to write a tutorial about using scripts with TaskPaper.