Encourage Later Line Break

Get help using and writing Nisus Writer Pro macros.
Post Reply
adryan
Posts: 561
Joined: 2014-02-08 12:57:03
Location: Australia

Encourage Later Line Break

Post by adryan »

G'day, all

First, the aim: I want to tighten the kerning at each space in a line of fully justified text until either a fragment of the first word in the following line jumps up to the end of the initial line or there is no point in tightening it further. The word to be split would separate either because of automatic hyphenation or because of a manually inserted soft hyphen in its midst. The reason for doing this is that it often happens that only a small amount of tightening will make for a more pleasing appearance than that resulting from automatic text manipulation.

Next, the problem: Encouraged by the success of Philip's "Paragraph Straddling Section" Macro, I thought I would have a go at wrestling it into something that would accomplish my aim. A tentative name for the Macro might be "Encourage Later Line Break". However, after lots of experimentation with Macro Language constructions, I could find no way to determine what character terminated a line. (Here I want it to be either a hard or a soft hyphen, or possibly an en dash or a slash.) I ended up with either a Range Object that had no text property or a Unicode code point whose relationship to any text I was dealing with was obscure, to say the least. If I can resolve these syntactic issues, I might be able to complete the Macro myself.

Now, some comments about usage: I don't need the Macro to decide the aesthetic issue. It should be enough to set a maximum number of loop iterations, to ensure it does in fact stop. This number will probably depend on the kern setting and (I guess) font size. I can experiment with this. When the Macro stops, either a split will have occurred or it won't. If a split has occurred, I can judge whether I want to retain it or revert to the initial situation.

And finally, the plea: Help!

Cheers,
Adrian
MacBook Pro (M1 Pro, 2021)
macOS Ventura
Nisus Writer user since 1996
User avatar
phspaelti
Posts: 1313
Joined: 2007-02-07 00:58:12
Location: Japan

Re: Encourage Later Line Break

Post by phspaelti »

Hello Adrian,
If I understand correctly you are trying to do the opposite of what you (we) were doing last time, i.e., successively tighten until a word from the following line pops up.
The simplest idea to my mind is to simply check what the bound of the current line is until it changes. Like this:

Code: Select all

$doc = Document.active
$loc = $doc.textSelection.location
$lineRange = $doc.text.rangeOfLineAtIndex $loc
$spaces = $doc.text.find ' ', 'Ea', $lineRange
$bound = $lineRange.bound
$i = $spaces.count - 1
while $doc.text.rangeOfLineAtIndex($loc).bound <= $bound
  $doc.setSelection $spaces[$i]
  Kern:Tighten
  $i -= 1
  if $i < 0
    $i = $spaces.count - 1
  end
end
Of course you may need to be careful that it is actually possible. For example this will fail if you try this on a line where the last character is a return, in which case the bound of the line will never change. You can get the last character of the line with code like this:

Code: Select all

$doc = Document.active
$loc = $doc.textSelection.location
$lineRange = $doc.text.rangeOfLineAtIndex $loc
$lastChar = $doc.text.characterAtIndex $lineRange.bound - 1
prompt $lastChar
Note that this will give you the code of the character. For example a return will give you "10". A soft-hyphen does not register as a character at all. So this would simply give you the code of the letter preceding the soft-hyphen.
philip
adryan
Posts: 561
Joined: 2014-02-08 12:57:03
Location: Australia

Re: Encourage Later Line Break

Post by adryan »

G'day, Philip et al

First, thanks for the prompt reply, Philip.

Your idea of checking whether or not the bound of the line Range has changed, without having to worry about what character terminates the line, is absolutely brilliant! So this Macro does exactly what I want.

I have named your second Macro here as "Last Character in Line". It also works well. It does in fact treat a soft hyphen as a character, returning a (decimal) value of 173 which translates to hexadecimal AD corresponding to Unicode U+00AD which represents a soft hyphen. So that's great. [Other readers may wish to note that most lines will probably end with a space which will not be apparent in fully justified text. If you particularly want to identify the terminal (non-space) character in that situation, you would need to change the bound offset from 1 to 2 in the code.]

Looking at this second slab of code, I can see how it all works, but there are two things I don't quite understand.

One relates to argument syntax. When does one enclose an argument in parentheses and when does one separate it from the command by a space?

My second query relates to text in a Range. You went all the way back to $doc.text to determine the character you wanted, whereas I had been trying (unsuccessfully) to get it from $lineRange somehow. Is there any way to get there using this second approach?

Thanks heaps, Philip, for your help with all this. The "Paragraph Straddling Section" and "Encourage Later Line Break" Macros are going to help enormously in removing a lot of the drudgery associated with my current project.

Cheers,
Adrian
MacBook Pro (M1 Pro, 2021)
macOS Ventura
Nisus Writer user since 1996
User avatar
phspaelti
Posts: 1313
Joined: 2007-02-07 00:58:12
Location: Japan

Re: Encourage Later Line Break

Post by phspaelti »

Hello Adrian,
I'm glad it works for you.
adryan wrote: 2023-01-21 14:35:11 One relates to argument syntax. When does one enclose an argument in parentheses and when does one separate it from the command by a space?
Probably a smart way to think about it would be that you always need parentheses. So any command should always be followed by a parentheses containing all the arguments with the arguments separated by commas. Then, if it it's clear which command the arguments belong to, you can omit them. I think NIsus chose to make the no parentheses look like the default, because they thought it looks more userfriendly. Also historically the no parentheses syntax may indeed have been the default. But programmers are probably more used to the parentheses being the default.

In practice that will mean that if the line of code has only a single command which takes arguments, you can leave the parentheses off. The typical place where you will absolutely need the parentheses is when you want to do something further with the output of the command.
In my code:

Code: Select all

$doc.text.rangeOfLineAtIndex $loc
returns a range object. But since I want to compare the bound of the range, the code will need to apply a property to this range. Basically the code is doing this:

Code: Select all

$range = $doc.text.rangeOfLineAtIndex $loc. # get the range of the line
$boundOfLine = $range.bound. # get the bound of the line
$comparison = $boundOfLine <= $bound
The problem is that since I want to pack all of this into the loop condition, I need to put it all in one line. So instead of storing the range in a variable, I just continue using it to find the bound. And that's why the parentheses are required.
adryan wrote: 2023-01-21 14:35:11 My second query relates to text in a Range. You went all the way back to $doc.text to determine the character you wanted, whereas I had been trying (unsuccessfully) to get it from $lineRange somehow. Is there any way to get there using this second approach?
Range objects are funny things. The truth is they are just a pair of numbers wrapped in an object for convenience. The two numbers are (1) a number representing an index (= the "location") and (2) a number representing the length of a string. These two numbers can be understood as a range only if we relate them to some text string. Otherwise they are just two meaningless numbers. You can create a range without any text, and then apply it to some text later, but unless you have some such text the range will not refer to any characters.

An analogy might be: think of cardboard with a hole punched in it. You can lay the cardboard on top of a page of text, and then read the characters you can see through the hole. But if you have only the cardboard there will be no characters associated with it.

Nisus has another type of object: the Text Selection. A text selection is the combination of a range and a text object. Unlike the range by itself, the selection has an associated text, so you always know which characters are in the relevant range. There will also be a guarantee that the range is "valid" for the associated text. A range all by itself might be "too big" for a given text, and not refer to anything in the given text.

So the short answer to your question is that there is no second approach :wink:
philip
adryan
Posts: 561
Joined: 2014-02-08 12:57:03
Location: Australia

Re: Encourage Later Line Break

Post by adryan »

G'day, Philip et al

Thanks for the clear and detailed explanations, Philip. Just what I needed.

I know I speak for many on these forums in expressing my appreciation for your generous contributions of time, knowledge and code, especially when it comes to the more arcane aspects of this powerful software.

All the best for 2023. (That goes for the rest of you, too!)

Cheers,
Adrian
MacBook Pro (M1 Pro, 2021)
macOS Ventura
Nisus Writer user since 1996
NisusUser
Posts: 317
Joined: 2011-01-12 05:32:38

Re: Encourage Later Line Break

Post by NisusUser »

adryan wrote: 2023-01-22 01:17:59 Thanks for the clear and detailed explanations, Philip. Just what I needed.

I know I speak for many on these forums in expressing my appreciation for your generous contributions of time, knowledge and code, especially when it comes to the more arcane aspects of this powerful software.
Indeed! Philip has helped me many times.
Post Reply