Writer character spacing macro: adjust the left character and check rounding
By ON Lab · Sources checked November 8, 2026
For repeated character-spacing changes at a Writer caret, change the spacing property of the single character immediately to its left. Reading the current value and adding a step is different from repeatedly setting a fixed value. Check the application's numeric readback too: our native test showed small rounding drift after opposite adjustments.
A reader's recorded macro always set the same spacing. The discussion eventually narrowed the range to the left character, because selecting both letters also affected spacing after the right one. We tested an original guarded helper with a native Writer view cursor, including paragraph starts, selected text, neighboring properties and repeated numeric updates.
What the native test actually changed
Our original body text was We To, followed by a second paragraph. We placed the caret between W and e. The helper created an independent text cursor selecting only W, read its CharKerning value and requested the next value.
After the first two decreasing calls, W's readback changed while e's CharKerning remained zero. The helper did not change the body's text. W's previously assigned bold weight and color were retained. This checks property scope, not a visual measurement of the font or every following glyph position.
We used LibreOffice 26.8.2.2 on macOS 27.2 arm64, with an isolated profile and native view-cursor calls. We did not test actual keyboard assignment, graphical dialog clicks, headers, footnotes or complex Unicode grapheme movement. The reader's environment was LibreOffice 26.2.1.2 on Windows 10.
Use requested steps, not an assumed point size
Writer's current release source maps CharKerning through a conversion between hundredths of a millimetre and integer twips. A requested step of 20 in this property is 0.20 mm, not 20 points. Do not assume that a similarly named parameter in a recorded dispatch macro has an interchangeable numeric unit.
The conversion also means a requested value and its readback need not be identical. Starting from an observed zero, our test gave these results:
| Request applied to current readback | W readback afterwards |
|---|---|
| Add −20 | −19 |
| Add −20 again | −39 |
| Add +20 | −19 |
| Add +20 again | 2 |
Thus two opposite pairs did not return the inspected property to exactly zero. Explicitly restoring zero did read back zero in our fixture. Treat incremental changes as live adjustments and inspect the result; do not promise exact numeric reversibility by pressing the opposite shortcut the same number of times.
A helper restricted to a collapsed caret
Review the code in a separate Basic module and test it on a disposable Writer copy through your normal trusted-macro workflow. The two small wrappers supply opposite requested steps. The helper deliberately rejects selected text rather than applying a single value across a mixed selection.
Sub DecreaseBeforeCaret
Dim status As String
status = AdjustLeftCharacter(ThisComponent, -20)
If status <> "changed" Then MsgBox status
End Sub
Sub IncreaseBeforeCaret
Dim status As String
status = AdjustLeftCharacter(ThisComponent, 20)
If status <> "changed" Then MsgBox status
End Sub
Function AdjustLeftCharacter(doc As Object, stepValue As Long) As String
Dim vc As Object, tc As Object, oldValue As Long, newValue As Long
AdjustLeftCharacter = "unsupported document or position"
If Not doc.supportsService("com.sun.star.text.TextDocument") Then Exit Function
vc = doc.CurrentController.getViewCursor()
If Not vc.isCollapsed() Then
AdjustLeftCharacter = "place a caret, not a selection"
Exit Function
End If
tc = vc.Text.createTextCursorByRange(vc)
If tc.isStartOfParagraph() Then
AdjustLeftCharacter = "no character before this paragraph position"
Exit Function
End If
If Not tc.goLeft(1, True) Then Exit Function
If tc.String = "" Then Exit Function
If Not tc.getPropertySetInfo().hasPropertyByName("CharKerning") Then Exit Function
If tc.getPropertyState("CharKerning") = 2 Then
AdjustLeftCharacter = "ambiguous character spacing"
Exit Function
End If
oldValue = CLng(tc.CharKerning)
newValue = oldValue + stepValue
If newValue < -32768 Or newValue > 32767 Then
AdjustLeftCharacter = "spacing is outside the supported short range"
Exit Function
End If
tc.CharKerning = CInt(newValue)
AdjustLeftCharacter = "changed"
End Function
The numeric property-state value 2 means an ambiguous value. The helper also checks that the optional property exists and that the requested value fits the property's signed-short range. Those guards do not establish that extreme spacing is useful or visually acceptable; choose a modest step for the actual text.
Why the paragraph-start check matters
A blind move left can fail at the start of a document or reach a preceding paragraph boundary. The helper checks the paragraph position before selecting a character. In our native controls, it refused both the start of the first paragraph and the start of the second, and the second paragraph's first character retained its original spacing.
A nonempty selection was also rejected. In the range-boundary control, the helper refused an out-of-range request and retained the existing readback. These are bounded tests of the original ASCII body fixture, not proof for every script, field, embedded object or text container.
Assign the wrappers only after checking the action
Writer's official keyboard customization guide describes selecting a function or macro, a shortcut and the assignment scope. After the two wrapper actions work as intended on a copy, assign them through that interface and verify the actual keys in your version.
The native test exercised the helper directly. It did not establish that a particular key is free on your operating system, that an assignment was saved, or that a shortcut works in a different document context. Check the resulting character's spacing and the character to its right after using the key.
Keep the caret at the intended gap for repeated adjustments. A collapsed caret at a different location selects a different preceding character. A selected range is outside this helper's contract and returns a message instead of silently flattening its spacing values.
Keep a before-and-after check
- Start with a short copy containing the intended letter pair and a nearby pair that should retain its own spacing property.
- Record the initial value and the caret position.
- Run one decreasing action, inspect the range, and repeat once to confirm it accumulates.
- Try the opposite action and check the actual numeric readback.
- Test the paragraph start and a selection to confirm the refusal behavior.
- Save the working copy, reopen it and inspect the visible result before applying the method to a larger document.
This is manual character spacing through a character property. It does not edit a font's automatic pair-kerning table, and the native property test does not judge whether a particular pair looks right. Preserve a source copy when exact restoration matters, especially because the observed conversions can produce small numeric drift.