[Script for aiding] round trip with collaborators/editors

I read a post on the Reddit scrivener group about someone who had submitted a book to a publisher, in Word, as was their requirement.

The book came back with a bunch of changes marked. Now the user had to do one of two things: Transfer all the changes back to scrivener, or work in Word.

I have a similar problem. My copy editor wants to markup on paper. So I print her a PDF. But then to enter the stuff in scrivener, I have to find the passage in my document. I needed a temporary coodinate system that would help me find the same spot in my document that she had marked in the PDF.

So I, with help from Claude, came up with Scriv_marker. Scriv_marker puts a line in every 5 paragraphs in lighter grey. Each document is numbered from the start.

The idea for the workflow:

  • close the project
  • Run scriv_marker from a command line to mark the document/project.
  • print/compile the project and send to your collaborator. They do whatever, and send their copy back.
  • Now you can find that spot in your project, and work on the modifications.
  • run it with –denumber to remove any markers

I did not like using Scrivener’s built in numbering for a few reasons:

  • Numbering and denumbering hve to be done one document at a time.
  • denumbering did not return a document to the format it had before.
  • If, in my editing I added a paragraph after par 27, then all the numbering following changes.
  • With Scriv_Marker, I can number a document, and add or delete what I wish. Para 025 remains para 025

Example of after scriv_marker has run.

This is a command line tool. On mac, you run it in a terminal window.

It requires python 3
It requires the script inself.
It requires an external file scriv_common.py which has stuff used by this and other scrivener utilities.

This has been tested enough that I’m using it with my current WiP. However I have not tested it in every combination. I do not know if files have any as yet undetected sentinels for Scrivener itself.

I have not tested on windows.

I have scrivener set to make a backup on project close.

I offer this script to the community at no charge and with no warranty. If L&L wish to incorporate the functionality of this script into the next version of Scrivener, they are free to do so, however, while I did my best to shepherd Claude’s work, I am neither a professional python programmer, nor an expert on Apple’s (per)version of RTF.

Thiis is the help accessed with the --help option, or a shorter version if given a missing or bad option.
usage: scrive_marker.py [-h] [-v] [–scriv-dir SCRIV_DIR]
[–walker-path WALKER_PATH] [–renumber | --denumber |
–repair] [–regex REGEX | --folder FOLDER]
[–every EVERY] [–width WIDTH] [–color COLOR]
[–dry-run] [–no-confirm] [–ignore-lock-check]
[–title] [–binder] [–debug-dir DIR]
[project]

scriv_marker.py – insert or remove unobtrusive paragraph-count sequence
markers directly in Scrivener content.rtf files, so a compiled/printed
PDF carries a coordinate system (light-grey, centered “==>para 005 <==”
lines every N paragraphs) that maps cleanly back onto the binder when
re-entering an editor’s corrections.

Two operations (mutually exclusive, exactly one required):

--renumber   Strip any existing markers, then insert fresh ones at
             the current paragraph counts (use this any time the
             text has changed since markers were last inserted).
--denumber   Strip existing markers and leave the prose clean --
             this is what you run before your own further editing,
             or before a mirror/diff pass, so markers never get
             treated as real content.

Scope (mutually exclusive, optional – default is the whole manuscript):

(none)              Entire Manuscript/Draft subtree (--manuscript-only
                     walk, same root scriv_mirror.py uses by default).
--regex PATTERN      Match PATTERN (re.search) against each item's
                     Path, as scriv_walk.py reports it (relative to
                     the Manuscript root).
--folder REL/PATH    Restrict to this one folder, relative to the
                     Manuscript root, and everything nested under it
                     (exact path or a '/'-prefixed descendant).

Markers are matched/stripped by their own fully-deterministic literal
RTF structure (MARKER_GROUP_RE below) – see the note further down for
why this replaced an earlier hidden-sentinel design.

Paragraph counting walks the raw RTF bytes tracking brace depth.
CONFIRMED by byte-level inspection of a real Scrivener content.rtf
(Dart, 2026-09): scenes contain NO literal \par at all – every
paragraph break is written as Scrivener/Cocoa’s own shorthand, a bare
backslash immediately followed by a raw newline. Both forms are
recognized as paragraph breaks; only a top-level (brace depth == 1)
occurrence counts, so one inside a nested group (footnote text, an
embedded picture/object, etc.) is not a scene-paragraph break and is
deliberately excluded.

RTF tables (\trowd 
 \row) are NOT brace-nested – a table row’s
cells sit at the same depth as ordinary body text, so a paragraph
break inside a cell would otherwise be miscounted as a real one and
could land a marker in the middle of a row. Table ranges are detected
separately (matching \trowd starts to their \row ends) and anything
inside one is excluded from both the count and the set of valid
insertion points.

Scrivener inline annotations are ALSO confirmed (same real-file
inspection) to be excluded from the count, but for a different reason
than tables: they are not real RTF groups either. Scrivener encodes
them as literal ESCAPED text – { and \ (an escaped brace, an
escaped backslash) rather than real { } grouping – so they sit at the
same brace depth as surrounding prose and get no protection from the
depth check above. A multi-paragraph annotation was confirmed to use
the same backslash+newline shorthand internally as real prose, so
without an explicit text-pattern exclusion its internal breaks would
be miscounted, and a marker could end up spliced into the middle of
the annotation’s own literal text field.

Marker insertion restates whatever paragraph formatting (alignment,
indentation) was actually active at the insertion point, rather than
resetting to RTF’s bare spec default. This matters because RTF’s
group-scoping is asymmetric: character formatting reverts
automatically when a {
} group closes, but paragraph formatting does
not. A first real sample showed a scene relying on a single \pard
declared once, never restated per paragraph – in that case, without
this restoration, the marker’s own centering would silently bleed
forward into every subsequent paragraph. A second real sample (Dart,
2026-09) showed the OPPOSITE convention is also real: Scrivener
restates a full \pard, including a completely different \tx tab-stop
layout, on a single paragraph that had its own character style applied
by hand – interleaved with literal <$Scr
> pagination/style-state
tags (see scriv_common.py’s own OUTPUT_TEXT_FIXES, which strips these
from unrtf’s plain-text output; they’re inert here, since they contain
no backslash or brace and can’t be mistaken for RTF structure). The
lookback approach handles both conventions without needing to assume
either: it always captures whatever \pard actually governed the
PRECEDING paragraph, so when the following paragraph already restates
its own (as in the second sample), the restored text is simply a
harmless, immediately-overridden redundant \pard – never a
correctness problem, just a few wasted bytes.

Markers are matched/stripped by their own fully-deterministic literal
RTF structure (see MARKER_GROUP_RE) rather than a separate hidden-text
sentinel – an earlier draft used RTF’s standard \v (hidden text) for
that, but Scrivener choosing to invent its own escaped-text tag scheme
for its OWN internal metadata, rather than trusting \v, is a strong
signal \v may not reliably render as hidden in Scrivener’s own reader.
Since the marker’s exact bytes are already fully known and controlled,
no separate sentinel is needed.

Safety, per Dart’s spec:
–dry-run List files that would be affected – Type,
Path, and (for --denumber) how many existing
markers were found, or (for --renumber) how
many would be stripped and how many freshly
inserted. No files are touched. Read-only, so
this does NOT check Files/user.lock – safe to
run while the project is open in Scrivener.
(default) After listing the same summary, ask for
y/n confirmation before writing anything.
–no-confirm Skip that prompt and write immediately.
–ignore-lock-check Skip the Files/user.lock check (see below) on
an actual (non-dry-run) write. Off by default
– this check exists specifically because Dart
routinely has several Scrivener projects open
at once, so it checks THIS project’s own lock
file, not whether Scrivener itself is running.

Diagnostics (read-only, never touch content.rtf or check the lock
file, usable alone or alongside --renumber/–denumber):
–title Print what Scrivener would DISPLAY as each in-scope
item’s title – its binder if manually set,
else the RTF-derived fallback (the same value
scriv_walk.py uses to build Path).
–binder Print the RAW binder XML value with nothing
filled in – items with none print ‘(not set)’,
revealing which ones rely on the RTF fallback that
–title shows instead.
–debug-dir DIR
Copy each in-scope content.rtf into DIR, named by
UUID, so a flat set of sample files can be pulled and
shared without digging through Files/Data//.

Files/user.lock existing inside a .scriv package is exactly how
Scrivener itself detects “this project is already open elsewhere” (see
Literature & Latte’s own support notes) – its presence here means the
project is very likely open right now in Scrivener, actively
autosaving, which is exactly the condition under which we must not
also be rewriting content.rtf out from under it. No separate .bak
backup is written before modifying – Scrivener’s own snapshot/backup
on close already covers this.

Usage:
python3 scriv_marker.py Rebel --renumber --dry-run
python3 scriv_marker.py Rebel --renumber --folder Volume_2/Confession
python3 scriv_marker.py Rebel --denumber --regex ‘May_1967/.*’ --no-confirm
python3 scriv_marker.py Rebel --title --binder --folder Volume_2/Confession
python3 scriv_marker.py Rebel --debug-dir /tmp/scriv_samples --regex ‘Scene_(One|Two)’

Part of the scriv_* toolset (scriv_walk.py, scriv_mirror.py,
scriv_history.py, scriv_report.py, scriv_marker.py), sharing common
logic via scriv_common.py.

positional arguments:
project Project name, .scriv dir, or .scrivx path, same as the
other scriv_* tools accept.

options:
-h, --help show this help message and exit
-v, --version show program’s version number and exit
–scriv-dir SCRIV_DIR
–walker-path WALKER_PATH
Path to scriv_walk.py (default: same directory as this
script)
–renumber Strip any existing markers, then insert fresh ones.
–denumber Strip existing markers, insert nothing.
–repair One-time cleanup for files already damaged by a fixed
pre-1.4 bug (repeated redundant \pard blocks /
truncated ’ hex escapes from renumbering the same
file many times before the fix). Not needed on files
that never hit that bug – run --dry-run first to see
if anything is found.
–regex REGEX Restrict to items whose Path (relative to the
Manuscript root) matches this regex.
–folder FOLDER Restrict to this one folder and everything nested
under it, given relative to the Manuscript/Draft root
– e.g. ‘Chapter_One’ or ‘Chapter_One/Scene_One’,
WITHOUT repeating the root’s own title.
–every EVERY Insert a marker after every N paragraphs (default: 5)
–width WIDTH Zero-padded digit width for the paragraph number
(default: 3)
–color COLOR Marker text color as R,G,B (default: 100,100,100)
–dry-run List affected files and counts; write nothing.
–no-confirm Skip the confirmation prompt and write immediately.
–ignore-lock-check Skip the Files/user.lock open-project safety check
(only consulted on an actual write, never on --dry-
run).

diagnostics:
Read-only inspection – never touch content.rtf, never check the lock file, safe to run any time. Usable alone or alongside --renumber/–denumber.

–title Print what Scrivener would DISPLAY as each in-scope
item’s title – its binder if manually set,
else the RTF-derived fallback title (same value
scriv_walk.py uses to build Path).
–binder Print the RAW binder XML value for each in-
scope item, with nothing filled in – reveals which
items have no manually-set title at all (those print
‘(not set)’) as distinct from --title’s
resolved/fallback value.
–debug-dir DIR Copy each in-scope content.rtf into DIR, named by UUID
(e.g. DIR/.rtf) – makes it easy to pull a flat
set of sample files to inspect or share, without
digging through Files/Data//. Read-only: the
project’s own files are never touched.

FYI, the Compile command has an option to insert links back to the corresponding Scrivener document in each section. They’ll be working links in document formats that support them, and will also transform into live document links if imported back into Scrivener.

You’ll find the option under the “gear” icon, in the right hand pane of the main Compile screen.

4 Likes

Wow always learn something new in the Forums.

2 Likes

That doesn’t help.

A: Looks like there are two separate features: Auto-number, and link-back. Auto-number means I have to place links everywhere. Link-back seems to create one link in the compiled document for each section. This isn’t enough.

At this point I can close the project, run the command, open the project in under a minute, and I have all 240 scenes numbered or renumbered throughout my entire project. Or I can do a subset.

B: But in the scivener document it shows up as a auto-number link for the auto-number stuff. with a variable that is evaluated at compile time. It doesn’t show up AS A NUMBER.

OR From reading the pop up on this option, the granularity is the section/document. I need something at the paragraph level. E.g.

I pass a 6000 word chapter to someone. A comment could read something like, “Late in the chapter Chris repeats pretty much what he said earlier on.

Instead with some form of coordinates, this can become. Chris at P221 seems to be making the same point as at P60

It means I can export as text, run MY grammar/spelling/punctuation correction, and get an accurate list to enter my own corrections. (Apple’s built in systems seem to fix about 2/3 of them, and a good fraction of those are wrong, or are correction a legimate word, or a name to something else, and THOSE can’t be spotted later.)

C: I need a system where I can send a NON-Scriverer format that has periodic labels.

AND

Those identical labels are present in my scrivener document, at a scale no further apart than every five paragraphs.

The Scrivener answer is to make your sections smaller. You can always merge them once you’re past this phase.

1 Like

You are suggesting that I make 5 sections per page of output? I want to look at someone’s scribbles, see Para 42, and be able to find Para 42. quickly. Not scanning down even one full page. I want to be able to hit command f enter 042, and hit next and find the only 042 in the document. I want to be able to slide the scroll bar a third down, see that the para marker in that screen is 025, and know I need to go about 1/6 down to find it.

In my post the screenshot shows a screenshot with two markers showing up. That is with interval 5. That is about the maximum amount of text I want to scan for a location. The number of paragraphs for renumbers is an internal variable in my program. If I routinely worked in 10 line paragraphs, I’d change 5 to 2 or even 1.

I tried this once: I exported file as text, corrected it. then it took me longer to transfer the corrections back, and most of that longer was, “find the paragraph that starts with ‘she ate pickles’”

I make software adapt to me. I push back hard adapting to the software.

Suppose I did what you want. Then to submit, I have to follow the publisher’s mandate and make 1 word file per chapter. Then when I want to work on the project again I have to break it back apart into sections.

No. I need a coordinate system that in intersystem operable. Scriverer doesn’t have it. I’m bolting a thousand line program kludge bolt on to give me that functionality.

Over and over in these files people say, “Do your writing in sciriverner. Do your format and presentation and publisher’s editing somewhere else.

no, No, and NO.

The nature of the stuff I write is that it goes through versions. E.g. I had a outdoor program manual that went through 4 major versions and 25 minor versions over 6 years. It’s not going to word, for formatting. Havi the original changed, then REFORMAT in word. Then get a minor uipdate, then REFORMAT in word.

Word is a PITA. Any publisher who wants substantial changes in word, can look for another author.

I write in one app. I love scrivener for many reasons. I can work on 100 documents in the course of a day. I can easily find all the documents that have Aardvark and Badger or I can find documents that have Aard\* or \*vark.

Scrivener has big holes. I has crap support for presentation. It’s handling of tables sucks. It’s handling of illlustrations is awful.

There is a place for L&L to create a formatting engine that udnerstands Skrivener’s quirks. Or an add on module for it.

Meanwhile, I build tools that enable me to collaborate.

1 Like

Why not have editor reader with an issue could put xx to indicate an issue then if say repeat yourself in scene war, then search xx in project search find first instance then second. Xx can make marking text issues precisely.

Great that you’re in a position to choose publishers.

Really, you aren’t? How come?

Because Editor Reader putting XX in his word document doesn’t put XX in MY scrivener document.

Because I’m neither a best (or even good) selling author who can pick his publishers nor am I a hobbyist who has other sources of income.

If you import the document back into a fresh document then you could track the xx the reader put to mark his issues. as you fix then you replace the document in the original novel project. Again this is a workflow idea and it either makes sense to you or not.

Yeah, I could do that. That’s actually a good idea.

What I haven’t figured out: a way where I can give them a file, and I can get back a copy edited file that I can use in scrivener directly, without having to reapply all my formatting.

You have experience what scrivener does with “track changes” and “comments” from word or word produced rtf?

It does NOT seem to work with PDF, but then not much does. The manual claims it does. But perhaps this counts as media and cannot be imported into the draft folder.

I prefer paragraph numbers, as it makes phone collaborating possible, as well as lists of issues.

I’m not confident in Scrivener’s ability to import other doc formats in anything like a readable format. I’ve tried with one doc file. It pretty much deformats it. I’d like to

The manual claims that it’s best to export from the word processor doc as rtf.

Scrivener can import Word’s comments, but not Track Changes markup. It should otherwise do a reasonable job of DOCX import. If it doesn’t for you, the support team would be happy to see a test file.

Yes, that’s correct, a PDF can’t go in the Draft folder.

1 Like

That’s too bad. Makes it harder to review changes that an editor makes. Means I have to view in word.