Tab-Aligned Text

I want to be able to write tab-aligned text to a file. This macro takes a sample text input, splits it into columns at a user-defined delimiter, and aligns each column using tabs.

It then displays the result in a KM Display Text window and also writes it to `Tab Align Test.rtf' on the desktop.

Tab-Align Text Test.kmmacros (24 KB)

Macro screenshot

The displayed text is properly aligned:

The file-written text is not:

I realise that for some reason the fonts are different, but if you copy/paste the KM window text to the TextEdit window, and set the fonts to be the same, it's still aligned:

CleanShot 2023-09-10 at 11.38.55

If you use the Write Styled Text option to set the font when writing the file, you get a similar result, except that the written text is grey while the pasted text is white.

Tab-Align Styled Text Test.kmmacros (25 KB)

Macro screenshot

I've also noticed that the tab markers are different:

CleanShot 2023-09-10 at 11.48.06

So yeah, I've got myself a bit confused. Any help would be great!

I see a few of issues with what you're doing.

  1. You'll need different approaches for aligning columns of text that use a monospaced font like Courier and a proportional font like Helvetica (TextEdit).

  2. TextEdit apparently won't let you set Tabs on the ruler via AppleScript but for the proportional approach, you will need to set tabs for the document.

  3. While the monospaced version can (and probably should) use multiple spaces to pad out the columns so each line is the same number of characters (which will always work), the proportional approach should separate columns by a single tab.

The key, then, is to have control over specifiy font and tabs. You have to determine which kind of font you are working with and then to simulate tabs with multiple spaces in a monospaced font or set them in your document for a proportional font using just one tab between columns.

Why not use tabs with a monospaced font? Well, you could, but you'd have to have control over the tab settings of the document. Programs like BBEdit have a global tab setting (specified as a number of spaces, say, 3 or 4) that you wouldn't want to change for a specific document so padding out the columns is safer is less efficient.

Why not use multiple tabs with proportional fonts? Well, you could, but it's more work if you have to edit them (in the ruler). Having just one to get to the next column makes the inevitable layout refinements more efficient.

Hope that helps. A bit anyway.

Assuming a monospaced font, of course, you could switch to plain text mode in TextEdit.

Thanks for the reply!

I'm aware of this, which is why I've been using Menlo in both TextEdit and KM. :+1:t3:

You can set the tab width for plain text, but not rich text, annoyingly.

My first port of call was a spaced version. Maybe it's good enough.

For the one-tab approach, you'd still have to manually set the tab length, which rules out writing to files in the background.

Agreed, but it's kind of moot unless you can set the tab width for .rtf files.


The thing I still don't understand is why copying the text from the Display Text window and pasting it into TextEdit does adjust the tab-stop width automatically. What's going on there?

Pasteboard items of different types in the clipboard.

Your script populates a public.utf8-plain-text pasteboard item, but no public.rtf pasteboard item is created.

In the case of copying manually from the display window, however, we do get a public.rtf pasteboard, which TextEdit will use in preference.

Textually represented, its contents is:

public.rtf as string:

{\rtf1\ansi\ansicpg1252\cocoartf2709
\cocoatextscaling0\cocoaplatform0{\fonttbl\f0\fnil\fcharset0 Menlo-Regular;}
{\colortbl;\red255\green255\blue255;\red0\green0\blue0;}
{\*\expandedcolortbl;;\cssrgb\c0\c0\c0\cname textColor;}
\pard\tx0\tx1120\tx2240\tx3360\tx4480\tx5600\tx6720\tx7840\tx8960\tx10080\tx11200\tx12320\tx13440\tx14560\tx15680\tx16800\tx17920\tx19040\tx20160\tx21280\tx22400\tx23520\tx24640\tx25760\tx26880\tx28000\tx29120\tx30240\tx31360\tx32480\tx33600\tx34720\pardirnatural

\f0\fs28\fsmilli14182 \cf2 Mamma            just killed a man    that's terrible!    \
Put a gun against    his head        why?!            \
Pulled            my trigger        makes sense I suppose...        \
Now            he's dead        hello officer, I'd like to report a murder}

To achieve the same in your script, you may be able to apply /usr/bin/textutil to your plain text to obtain rtf, rather than leaving that stage to TextEdit, which seems to do something a bit complex – overriden perhaps by the editor's default font setting in the GUI.

1 Like

When I run your macro I see Helvetica in TextEdit.

There are a variable number of tabs in the pasted text that pad out the columns in TextEdit. The line that begins with 'Put' has one to get to the second column while the one that begins with 'Now' has three. That seems to be what the variable Local__Text has.

I don't really know what I'm doing here, but...

Tab-Align Styled Text Test.kmmacros (23 KB)

Macro screenshot

This creates a temporary .txt file, processes it (I think!) using textutil and outputs a .rtf to the Desktop.

Unfortunately, it's still unaligned, and changing font/size doesn't seem to rectify that. Maybe I've misunderstood how to use textutil.

Where ?

Does your shell script specify a font ? (I may be missing something)

textutil uses Helvetica Light by default, and that's what is specified by the RTF header it is generating there.

One way to generate RTF with a specific font, is to construct HTML markup, and use textutil for an HTML -> RTF rewrite.


Another approach is to create your public.rtf pasteboard item directly, using the kind of RTF wrapping (heading and closing brace) obtained by manual copying from a KM display window:

The pasteboard created like this seems to paste in the way you hope for:

Expand disclosure triangle to view JS source
(() => {
    "use strict";

    ObjC.import("AppKit");

    const main = () => {

        const rtf = `{\\rtf1\\ansi\\ansicpg1252\\cocoartf2709
\\cocoatextscaling0\\cocoaplatform0{\\fonttbl\\f0\\fnil\\fcharset0 Menlo-Regular;}
{\\colortbl;\\red255\\green255\\blue255;\\red0\\green0\\blue0;}
{\\*\\expandedcolortbl;;\\cssrgb\\c0\\c0\\c0\\cname textColor;}
\\pard\\tx0\\tx1120\\tx2240\\tx3360\\tx4480\\tx5600\\tx6720\\tx7840\\tx8960\\tx10080\\tx11200\\tx12320\\tx13440\\tx14560\\tx15680\\tx16800\\tx17920\\tx19040\\tx20160\\tx21280\\tx22400\\tx23520\\tx24640\\tx25760\\tx26880\\tx28000\\tx29120\\tx30240\\tx31360\\tx32480\\tx33600\\tx34720\\pardirnatural

\\f0\\fs28\\fsmilli14182 \\cf2 Mamma			just killed a man	that's terrible!	\\
Put a gun against	his head		why?!			\\
Pulled			my trigger		makes sense I suppose...		\\
Now			he's dead		hello officer, I'd like to report a murder}`;


        return setClipOfTextType("public.rtf")(
            rtf
        );
    };

    // --------------------- GENERIC ---------------------

    // setClipOfTextType :: String -> String -> IO String
    const setClipOfTextType = utiOrBundleID =>
        txt => {
            const pb = $.NSPasteboard.generalPasteboard;

            return (
                pb.clearContents,
                pb.setStringForType(
                    $(txt),
                    utiOrBundleID
                ),
                txt
            );
        };

    return main();
})();

Sorry, I wasn't very articulate.

I meant I opened the resultant file and tried changing the font and size in TextEdit to see if that impacted the alignment.

I'm not sure why I thought font/size would make any difference when the tab markers aren't being changed. That seems to be the difference when you paste the KM displayed text into TextEdit.

Ok this is new to me. I tried running it in a Javascript for Automation action, which gave me:

{\rtf1\ansi\ansicpg1252\cocoartf2709
\cocoatextscaling0\cocoaplatform0{\fonttbl\f0\fnil\fcharset0 Menlo-Regular;}
{\colortbl;\red255\green255\blue255;\red0\green0\blue0;}
{\*\expandedcolortbl;;\cssrgb\c0\c0\c0\cname textColor;}
\pard\tx0\tx1120\tx2240\tx3360\tx4480\tx5600\tx6720\tx7840\tx8960\tx10080\tx11200\tx12320\tx13440\tx14560\tx15680\tx16800\tx17920\tx19040\tx20160\tx21280\tx22400\tx23520\tx24640\tx25760\tx26880\tx28000\tx29120\tx30240\tx31360\tx32480\tx33600\tx34720\pardirnatural

\f0\fs28\fsmilli14182 \cf2 Mamma			just killed a man	that's terrible!	\
Put a gun against	his head		why?!			\
Pulled			my trigger		makes sense I suppose...		\
Now			he's dead		hello officer, I'd like to report a murder}

You can set the output of the action to Ignore.

The more significant effect is that it places something in a public.rtf pasteBoard item in the clipboard, so you can try pasting that into TextEdit

Here's the RTF header of the Tab Align Test.rtf your macro creates on my system:

{\rtf1\ansi\ansicpg1252\cocoartf2709
\cocoatextscaling0\cocoaplatform0{\fonttbl\f0\fswiss\fcharset0 Helvetica;}
{\colortbl;\red255\green255\blue255;}
{\*\expandedcolortbl;;}
\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\partightenfactor0

You can see Helvetica there. So that's one issue.

As for the tab in a proportional font treatment, you can just replace the colon with a tab in your sample text.

Whatever applications you are importing this proportional text into (say, InDesign), should let you script setting its tabs.

Yes, that works!

So, unless I've missed the point... I'd have to find a way to format the input similarly (arbitrary spaces and // pairs at line ends) and insert it into the javascript at the appropriate point. Is that right?

(Sorry this is all a bit over my head.)

1 Like

I had a stab at it, but I'm not having much luck, as nothing pastes. Feel like I'm getting closer though:

Tab-Align Text and Set as .rtf Pasteboard.kmmacros (26 KB)

Macro screenshot

Away from a laptop, but glancing from a phone, I'm not sure whether you need that duplication of backslashes in that context.

(Whatever you use should resolve to single slashes in the RTF source)

Here's my attempt to resolve that issue:

Tab-Align Text and Set as .rtf Pasteboard.kmmacros (29 KB)

Macro screenshot

Still no joy with pasting.

Here's the output .rtf header text:

Text
{{
tf1ansiansicpg1252\cocoartf2709
\cocoatextscaling0\cocoaplatform0{{onttbl0nilcharset0 Menlo-Regular;}}
{{\colortbl;
ed255\green255lue255;
ed0\green0lue0;}}
{{\*\expandedcolortbl;;\cssrgb\c0\c0\c0\cname textColor;}}
\pard	x0	x1120	x2240	x3360	x4480	x5600	x6720	x7840	x8960	x10080	x11200	x12320	x13440	x14560	x15680	x16800	x17920	x19040	x20160	x21280	x22400	x23520	x24640	x25760	x26880	x28000	x29120	x30240	x31360	x32480	x33600\pardirnatural

0s28smilli14182 \cf2 Mamma			just killed a man	that's terrible!	\
Put a gun against	his head		why?!			\
Pulled			my trigger		makes sense I suppose...		\
Now			he's dead		hello officer, I'd like to report a murder}

There seem to be some strange characters there in the range of fonttbl, for example.

(And it also appears, for example, to start with the wrong characters - {{tf in lieu of {rtf)

Had another go, but to be honest I'm a bit out of my depth here.

Tab-Align Text and Set as .rtf Pasteboard.kmmacros (28 KB)

Macro screenshot

Ok, I've identified the point of failure.

  • If you run the following macro with the Display Local__Text and Cancel Macro group enabled, you'll get the text input correctly formatted for use in the Javascript:

  • If you copy and paste this into the green Set public.rtf Pasteboard (Paste Displayed Text) action at <REPLACE THIS>, the pasteboard is correctly set and the tab-aligned text can be pasted. Great!


Ok, so let's try and use Local__Text in the Javascript:

  • Disable the red and green actions and enable the purple Set public.rtf Pasteboard Using Local__Text action.

The macro will now use the correctly formatted Local__Text variable in the Javascript, but it fails to interpret the \\ characters as line breaks.

My suspicion was that each instance of \ in Local__Text might need to be escaped, so I tried adjusting this in the text formatting shell script, but the result was identical. I also tried changing it to single \ characters and \n, but that didn't help either.

So near yet so far!

Tab-Align Text and Set as .rtf Pasteboard.kmmacros (35 KB)

Macro screenshot

2 Likes

@noisneil - You are fighting the good fight on this. When you figure this out, your Jedi level will rise and make Yoda proud.

1 Like

After much jiggerypokery...

Tab-Align Text and Set as .rtf Pasteboard.kmmacros (23 KB)

Macro screenshot

For some reason, the clipboard needs to be trimmed for whitespace, as the first line contains 4 leading spaces. I suspect this may be something to do with the \\par function. Nonentheless, I can't envision too many scenarios where trimming whitespace would cause a problem.

The problem now is that writing the clipboard to a file results in completely misaligned result. :weary:

Why is this so hard?!