Convert TXT to RTF

Is there a way to use KM to convert a TXT file to RTF with predetermined font and size?

I'm importing a number of TXT files into an app that requires RTF, and I want to avoid tediously reformatting each one by hand.

Your suggestions appreciated.

Hey @anamorph,

Sure.

Put all your files in a directory.

Open the Terminal.

cd "<path-to-directory>`

To move the working directory to the correct place.

ls

To make certain you are in the correct place.

textutil -format txt -convert rtf -encoding UTF-8 -font Times -fontsize 18 *

To convert the files. (Change font family and size to suit.)

The originals are left intact, so you're okay if you make a mistake.

-Chris

3 Likes

Thanks very much for this, @ccstone . It looks like just what I need.

Chris, this is very cool! :+1:

Could you please show us how to convert a specific file?

I think you just put in the filename; add full path if it's in another directory.

[EDIT] As I learned from posts elsewhere on the forum, you need to enclose path varables in double quotes so that textutil will properly recognize spaces in the path.

textutil -convert html foo.rtf

textutil -convert html "foo foo.rtf"

textutil -convert html "$KMVAR_myFilePathVariable"

Here is the textutil manual.

In case anyone needs to do this kind of thing with CSS formatting from JavaScript (could also be done from AppleScript, see below), a snippet writing RTF to the desktop might look something like:

(() => {
    'use strict';

    ObjC.import('AppKit');

    // main :: IO ()
    const main = () =>
        bindLR(
            rtfFromTextLR('font: italic 24px Courier;')(
                'Certainty and confidence - twin forms of foolishness.'
            ),
            strRTF => writeFile('~/Desktop/test.rtf', strRTF)
        );

    // rtfFromTextLR :: CSS String -> String -> Either String RTF String
    const rtfFromTextLR = strCSS => strText =>
        rtfFromHTML(
            "<span style=\"" + strCSS + "\">" + strText + "</span>"
        )

    // rtfFromHTML :: String -> Either String String
    const rtfFromHTML = strHTML => {
        const
            as = $.NSAttributedString.alloc
            .initWithHTMLDocumentAttributes($(strHTML)
                .dataUsingEncoding($.NSUTF8StringEncoding), 0
            );
        return bindLR(
            typeof as
            .dataFromRangeDocumentAttributesError !== 'function' ? (
                Left('String could not be parsed as HTML')
            ) : Right(as),

            // Function bound if Right value obtained above:
            htmlAS => {
                let error = $();
                const rtfData = htmlAS
                    .dataFromRangeDocumentAttributesError({
                            'location': 0,
                            'length': htmlAS.length
                        }, {
                            DocumentType: 'NSRTF'
                        },
                        error
                    );
                return Boolean(ObjC.unwrap(rtfData) && !error.code) ? Right(
                    ObjC.unwrap($.NSString.alloc.initWithDataEncoding(
                        rtfData,
                        $.NSUTF8StringEncoding
                    ))
                ) : Left(ObjC.unwrap(error.localizedDescription));
            }
        );
    };

    // GENERIC FUNCTIONS ----------------------------
    // https://github.com/RobTrew/prelude-jxa

    // Left :: a -> Either a b
    const Left = x => ({
        type: 'Either',
        Left: x
    });

    // Right :: b -> Either a b
    const Right = x => ({
        type: 'Either',
        Right: x
    });

    // bindLR (>>=) :: Either a -> (a -> Either b) -> Either b
    const bindLR = (m, mf) =>
        undefined !== m.Left ? (
            m
        ) : mf(m.Right);


    // writeFile :: FilePath -> String -> IO ()
    const writeFile = (strPath, strText) =>
        $.NSString.alloc.initWithUTF8String(strText)
        .writeToFileAtomicallyEncodingError(
            $(strPath)
            .stringByStandardizingPath, false,
            $.NSUTF8StringEncoding, null
        );

    // MAIN ---
    return main();
})();

Applescript version of rtfFromHTML:

use AppleScript version "2.4"
use framework "Foundation"
use framework "AppKit"
use scripting additions


-- rtfFromHTML :: String -> Either String String
on rtfFromHTML(strHTML)
    set ca to current application
    set s to ca's NSString's stringWithString:strHTML
    set d to (s)'s dataUsingEncoding:(ca's NSUTF8StringEncoding)
    
    set attStr to ca's NSAttributedString's alloc()'s initWithHTML:d documentAttributes:(missing value)
    if attStr is missing value then
        |Left|("String could not be parsed as HTML")
    else
        set {rtfData, err} to attStr's ¬
            dataFromRange:{location:0, |length|:attStr's |length|()} ¬
                documentAttributes:{DocumentType:"NSRTF"} ¬
                |error|:(reference)
        
        if (missing value = rtfData) or (missing value is not err) then
            |Left|(err's localizedDescription() as text)
        else
            |Right|((ca's NSString's alloc()'s ¬
                initWithData:rtfData encoding:(ca's NSUTF8StringEncoding)) as text)
        end if
    end if
end rtfFromHTML

-- GENERIC FUNCTIONS from
-- https://github.com/RobTrew/prelude-applescript
1 Like

In my previous example the asterisk is a placeholder for any file.

textutil -format txt -convert rtf -encoding UTF-8 -font Times -fontsize 18 *

Actually, since I'm only converting text files I really should have written it like this:

textutil -format txt -convert rtf -encoding UTF-8 -font Times -fontsize 18 *.txt

To manage a single file you can do this:

cd ~/'Downloads/test_textutil/'
textutil -format txt -convert rtf -encoding UTF-8 -font Times -fontsize 18 'test_textutil.txt'

** NOTE that I'm unnecessarily single-quoting the source directory path and the file name, because they don't contain any spaces. Nevertheless it's a good habit to get into -- because if you don't you'll get bitten at some point.

-Chris

1 Like