dsh-office-kit 0.4.2 → 0.4.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/assets/skills/excel-live-control/SKILL.md +119 -152
- package/assets/skills/excel-live-control/domain_guidance/financial_models.md +42 -58
- package/assets/skills/excel-live-control/domain_guidance/healthcare.md +36 -42
- package/assets/skills/excel-live-control/domain_guidance/marketing_advertising.md +33 -34
- package/assets/skills/excel-live-control/domain_guidance/scientific_research.md +34 -36
- package/assets/skills/excel-live-control/references/office-automation.md +127 -153
- package/assets/skills/excel-live-control/references/read_only_qna.md +40 -45
- package/assets/skills/excel-live-control/style_guidelines.md +56 -64
- package/assets/skills/excel-live-control/workflows/create_workflows.md +40 -47
- package/assets/skills/excel-live-control/workflows/edit_workflows.md +37 -45
- package/assets/skills/office-documents/SKILL.md +24 -60
- package/assets/skills/office-documents/examples/end_to_end_smoke_test.md +5 -5
- package/assets/skills/office-documents/ooxml/comments.md +8 -8
- package/assets/skills/office-documents/ooxml/hyperlinks_and_fields.md +10 -10
- package/assets/skills/office-documents/ooxml/rels_and_content_types.md +7 -7
- package/assets/skills/office-documents/ooxml/tracked_changes.md +12 -12
- package/assets/skills/office-documents/references/docx-structure.md +11 -13
- package/assets/skills/office-documents/references/template-elicitation.md +11 -13
- package/assets/skills/office-documents/references/writing-quality.md +20 -20
- package/assets/skills/office-documents/tasks/accessibility_a11y.md +16 -22
- package/assets/skills/office-documents/tasks/captions_crossrefs.md +23 -26
- package/assets/skills/office-documents/tasks/clean_tracked_changes.md +17 -22
- package/assets/skills/office-documents/tasks/comments_manage.md +9 -11
- package/assets/skills/office-documents/tasks/compare_diff.md +10 -16
- package/assets/skills/office-documents/tasks/create_edit.md +14 -16
- package/assets/skills/office-documents/tasks/equations.md +17 -28
- package/assets/skills/office-documents/tasks/fields_update.md +14 -16
- package/assets/skills/office-documents/tasks/fixtures_edge_cases.md +19 -33
- package/assets/skills/office-documents/tasks/footnotes_endnotes.md +12 -14
- package/assets/skills/office-documents/tasks/forms_content_controls.md +16 -20
- package/assets/skills/office-documents/tasks/headings_numbering.md +17 -22
- package/assets/skills/office-documents/tasks/images_figures.md +15 -25
- package/assets/skills/office-documents/tasks/multi_doc_merge.md +13 -23
- package/assets/skills/office-documents/tasks/navigation_internal_links.md +18 -21
- package/assets/skills/office-documents/tasks/privacy_scrub_metadata.md +16 -22
- package/assets/skills/office-documents/tasks/protection_restrict_editing.md +19 -21
- package/assets/skills/office-documents/tasks/read_review.md +9 -20
- package/assets/skills/office-documents/tasks/redaction_anonymization.md +17 -18
- package/assets/skills/office-documents/tasks/sections_layout.md +16 -23
- package/assets/skills/office-documents/tasks/style_lint_normalize.md +18 -22
- package/assets/skills/office-documents/tasks/tables_spreadsheets.md +21 -31
- package/assets/skills/office-documents/tasks/templates_style_packs.md +17 -21
- package/assets/skills/office-documents/tasks/toc_workflow.md +10 -14
- package/assets/skills/office-documents/tasks/verify_render.md +11 -23
- package/assets/skills/office-documents/tasks/watermarks_background.md +11 -15
- package/assets/skills/office-documents/troubleshooting/libreoffice_headless.md +6 -6
- package/assets/skills/office-documents/troubleshooting/run_splitting.md +12 -12
- package/assets/skills/office-pdf/SKILL.md +36 -51
- package/assets/skills/office-pdf/references/form-fields.md +20 -20
- package/assets/skills/office-pdf/references/geometry.md +10 -10
- package/assets/skills/office-pdf/references/layout-quality.md +13 -13
- package/assets/skills/office-pdf/references/limits.md +12 -56
- package/assets/skills/office-pdf/tasks/annotations.md +5 -5
- package/assets/skills/office-pdf/tasks/create.md +6 -9
- package/assets/skills/office-pdf/tasks/edit-pages.md +14 -14
- package/assets/skills/office-pdf/tasks/forms.md +15 -15
- package/assets/skills/office-pdf/tasks/read.md +9 -9
- package/assets/skills/office-pdf/tasks/redact.md +18 -18
- package/assets/skills/office-pdf/tasks/stamp.md +10 -10
- package/assets/skills/office-presentations/SKILL.md +71 -91
- package/assets/skills/office-presentations/container_tools/render_slides.py +1 -1
- package/assets/skills/office-presentations/domain_guidance/finance.md +16 -16
- package/assets/skills/office-presentations/recipes/README.md +24 -35
- package/assets/skills/office-presentations/recipes/autolayout.md +12 -13
- package/assets/skills/office-presentations/recipes/connectors.md +11 -12
- package/assets/skills/office-presentations/recipes/images.md +11 -12
- package/assets/skills/office-presentations/recipes/master-and-layouts.md +18 -18
- package/assets/skills/office-presentations/recipes/native-charts.md +8 -8
- package/assets/skills/office-presentations/recipes/rich-text-runs.md +13 -13
- package/assets/skills/office-presentations/recipes/speaker-notes.md +7 -7
- package/assets/skills/office-presentations/recipes/tables.md +14 -14
- package/assets/skills/office-presentations/recipes/textbox-at-coordinate.md +9 -9
- package/assets/skills/office-presentations/recipes/theme-tokens.md +7 -8
- package/assets/skills/office-presentations/references/authoring-workflow.md +38 -43
- package/assets/skills/office-presentations/references/finalization.md +10 -12
- package/assets/skills/office-presentations/references/native-bullets.md +14 -18
- package/assets/skills/office-presentations/references/native-evidence.md +18 -32
- package/assets/skills/office-presentations/references/style-guidelines.md +27 -32
- package/assets/skills/office-presentations/references/templates.md +31 -60
- package/assets/skills/office-presentations/routing/google_slides.md +4 -4
- package/assets/skills/office-presentations/tasks/apply-annotations.md +17 -17
- package/assets/skills/office-presentations/tasks/charts-tables-notes.md +20 -23
- package/assets/skills/office-presentations/tasks/create-deck.md +19 -19
- package/assets/skills/office-presentations/tasks/edit-deck.md +14 -16
- package/assets/skills/office-presentations/tasks/inspect-and-verify.md +31 -34
- package/assets/skills/office-presentations/tasks/template-following.md +23 -28
- package/assets/skills/office-spreadsheets/SKILL.md +32 -39
- package/assets/skills/office-spreadsheets/recipes/charts.md +8 -26
- package/assets/skills/office-spreadsheets/recipes/conditional-formats.md +9 -26
- package/assets/skills/office-spreadsheets/recipes/data-tables.md +7 -23
- package/assets/skills/office-spreadsheets/recipes/named-ranges.md +8 -21
- package/assets/skills/office-spreadsheets/recipes/number-formats.md +7 -23
- package/assets/skills/office-spreadsheets/recipes/sparklines.md +7 -20
- package/assets/skills/office-spreadsheets/references/charts.md +15 -34
- package/assets/skills/office-spreadsheets/references/domain/financial-models.md +10 -25
- package/assets/skills/office-spreadsheets/references/domain/healthcare.md +8 -19
- package/assets/skills/office-spreadsheets/references/domain/marketing.md +8 -22
- package/assets/skills/office-spreadsheets/references/domain/research.md +8 -19
- package/assets/skills/office-spreadsheets/references/formula-patterns.md +20 -50
- package/assets/skills/office-spreadsheets/references/style-guidelines.md +19 -44
- package/assets/skills/office-spreadsheets/references/workbook-structure.md +12 -35
- package/assets/skills/office-spreadsheets/routing/google-sheets.md +11 -26
- package/assets/skills/office-spreadsheets/tasks/analyze-workbook.md +10 -25
- package/assets/skills/office-spreadsheets/tasks/annotations.md +12 -30
- package/assets/skills/office-spreadsheets/tasks/create-workbook.md +5 -5
- package/assets/skills/office-spreadsheets/tasks/diff-workbooks.md +2 -2
- package/assets/skills/office-spreadsheets/tasks/edit-workbook.md +5 -5
- package/assets/skills/office-spreadsheets/tasks/inspect-workbook.md +9 -9
- package/assets/skills/office-spreadsheets/tasks/recalc-and-check.md +10 -30
- package/assets/skills/shared/annotation-recipes.md +98 -120
- package/assets/skills/shared/common-workflow.md +70 -80
- package/dist/index.js +1 -1
- package/package.json +1 -1
|
@@ -1,26 +1,17 @@
|
|
|
1
1
|
# Read and review a file
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
## Read the content
|
|
3
|
+
## Read
|
|
6
4
|
|
|
7
5
|
```bash
|
|
8
6
|
"<python>" "<cli>" docx read --input "<file.docx>"
|
|
9
|
-
```
|
|
10
|
-
|
|
11
|
-
Returns the title, author, paragraphs with their styles, headings, tables, image and equation counts, and page dimensions. Use it as the first look, including for files you produced earlier in the session.
|
|
12
|
-
|
|
13
|
-
## Read the structure
|
|
14
|
-
|
|
15
|
-
```bash
|
|
16
7
|
"<python>" "<skill>/scripts/docx_kit.py" parts '<file.docx>' # every package part and its size
|
|
17
8
|
"<python>" "<skill>/scripts/docx_kit.py" text '<file.docx>' # paragraphs with style and outline level
|
|
18
9
|
"<python>" "<skill>/scripts/docx_kit.py" xml '<file.docx>' word/document.xml
|
|
19
10
|
```
|
|
20
11
|
|
|
21
|
-
`parts`
|
|
12
|
+
`docx read` gives the title, author, styled paragraphs, headings, table/image/equation counts and page dimensions — the first look, including for files produced earlier in the session. `parts` shows which features exist at all: no `comments.xml`, no comments, whatever the user says. `text` is faster than raw XML when you only need the words and their heading levels.
|
|
22
13
|
|
|
23
|
-
## Audit
|
|
14
|
+
## Audit
|
|
24
15
|
|
|
25
16
|
| Question | Command |
|
|
26
17
|
|---|---|
|
|
@@ -32,19 +23,17 @@ Returns the title, author, paragraphs with their styles, headings, tables, image
|
|
|
32
23
|
| Are there footnotes or endnotes? | `footnotes_report.py` |
|
|
33
24
|
| What comments exist, and on what text? | `comments_extract.py` |
|
|
34
25
|
|
|
35
|
-
|
|
26
|
+
Each audit prints JSON and exits non-zero when it found something: the exit code says whether the document needs attention, the `findings` array says why.
|
|
36
27
|
|
|
37
28
|
## Review for the user
|
|
38
29
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
1. Read the text, not just the audits. Point out the argument, not only the formatting.
|
|
30
|
+
1. Read the text, not just the audits. Address the argument, not only the formatting.
|
|
42
31
|
2. Separate defects (broken table, skipped heading level, missing alt text) from style preferences.
|
|
43
|
-
3. Cite locations by section heading and paragraph text
|
|
32
|
+
3. Cite locations by section heading and paragraph text: Word has no stable page numbers.
|
|
44
33
|
4. Offer the fix as a concrete next action, with the command that performs it.
|
|
45
34
|
|
|
46
35
|
## Pitfalls
|
|
47
36
|
|
|
48
|
-
- `docx read` shows the body only; headers, footers, footnotes and comments need `docx_kit.py xml` or the
|
|
49
|
-
- Tracked changes
|
|
50
|
-
- A field's displayed text is a cached result
|
|
37
|
+
- `docx read` shows the body only; headers, footers, footnotes and comments need `docx_kit.py xml` or the report scripts.
|
|
38
|
+
- Tracked changes read as plain text; `accept_tracked_changes.py --mode report` shows what is proposed.
|
|
39
|
+
- A field's displayed text is a cached result; `fields_report.py` shows the instruction, which is the truth.
|
|
@@ -1,26 +1,29 @@
|
|
|
1
1
|
# Redaction and anonymisation
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
Text lives in `w:t` (and `w:delText` for deletions, `w:instrText` for field instructions). A redaction that only rewrites paragraphs via `python-docx` misses headers, footers, footnotes and comments.
|
|
3
|
+
Text lives in `w:t` (plus `w:delText` for deletions and `w:instrText` for field instructions). A redaction that only rewrites paragraphs through python-docx misses headers, footers, footnotes and comments.
|
|
6
4
|
|
|
7
5
|
## Redact
|
|
8
6
|
|
|
9
7
|
```bash
|
|
10
8
|
"<python>" "<skill>/scripts/redact_docx.py" "<file.docx>" "<file-redacted.docx>" --emails --phones
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
```bash
|
|
11
12
|
"<python>" "<skill>/scripts/redact_docx.py" "<file.docx>" "<file-redacted.docx>" --pattern "ACCT-\d{6}" --replacement "<account>"
|
|
12
13
|
```
|
|
13
14
|
|
|
14
|
-
|
|
15
|
-
- `--mask_char *` keeps the shape of the value visible (`***@***.com` style), `--replacement` swaps in a token, `--no_preserve_length` stops padding to the original length.
|
|
16
|
-
- `--include_comments` also rewrites `word/comments.xml`; without it, comments keep the original text.
|
|
17
|
-
- Applied to body, every header and footer, footnotes and endnotes.
|
|
15
|
+
`--pattern` is repeatable — use it for account numbers, case ids and postcodes. It applies to the body, every header and footer, footnotes and endnotes.
|
|
18
16
|
|
|
19
|
-
|
|
17
|
+
| Flag | Effect |
|
|
18
|
+
|---|---|
|
|
19
|
+
| `--mask_char *` | masks the shape of the value instead of replacing it (`***@***.com`) |
|
|
20
|
+
| `--replacement` | swap in a token instead of masking |
|
|
21
|
+
| `--no_preserve_length` | stop padding the result to the original length |
|
|
22
|
+
| `--include_comments` | also rewrite `word/comments.xml`; without it comments keep the original text |
|
|
20
23
|
|
|
21
|
-
|
|
24
|
+
Matching happens per text run, because merging runs destroys formatting, so a value split across runs is not found. That is acceptable only if you check the result; when formatting is mixed inside the sensitive value, flatten that paragraph first (`tasks/style_lint_normalize.md`).
|
|
22
25
|
|
|
23
|
-
Redaction removes
|
|
26
|
+
Redaction removes a value; anonymisation replaces it consistently. If the same account number appears four times and the argument depends on it matching, replace all occurrences with the same token rather than `[redacted]` every time — per-pattern `--replacement "<account-A>"` does that.
|
|
24
27
|
|
|
25
28
|
## Check the whole file
|
|
26
29
|
|
|
@@ -29,14 +32,10 @@ Redaction removes the value; anonymisation replaces it consistently. If the same
|
|
|
29
32
|
"<python>" "<skill>/scripts/docx_kit.py" xml '<file-redacted.docx>' word/document.xml | grep -c "example.com"
|
|
30
33
|
```
|
|
31
34
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
## Verify
|
|
35
|
-
|
|
36
|
-
Render, then read the pages the data appeared on. A redaction that changed the line length enough to shift the layout is worth a look; a redaction that left a `<w:t>` intact shows up in the grep, not the page.
|
|
35
|
+
Grepping the dumped XML for each sensitive term across document, header, footer, footnote and comment parts is the honest check. Then render and read the pages the data appeared on: check line-length shifts, and remember an intact `<w:t>` shows in the grep, not on the page.
|
|
37
36
|
|
|
38
37
|
## Pitfalls
|
|
39
38
|
|
|
40
|
-
- Field results and field instructions can both
|
|
41
|
-
- Images are not scanned
|
|
42
|
-
- Charts store category labels in `word/charts/*.xml`; inspect
|
|
39
|
+
- Field results and field instructions can both hold data; the script rewrites `w:t` / `w:delText` only.
|
|
40
|
+
- Images are not scanned. Say so plainly instead of implying the file is clean.
|
|
41
|
+
- Charts store category labels in `word/charts/*.xml`; inspect them when a chart carried the data.
|
|
@@ -1,8 +1,6 @@
|
|
|
1
|
-
# Sections
|
|
1
|
+
# Sections, page setup, headers
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
Page setup lives in a `w:sectPr` element. The last one sits at the end of the body; earlier ones sit inside the last paragraph of the section they end (`w:pPr/w:sectPr`). That is why adding a landscape page means adding a section break before and after, not changing one global setting.
|
|
3
|
+
Page setup lives in `w:sectPr`. The last one sits at the end of the body; earlier ones sit inside the last paragraph of the section they end (`w:pPr/w:sectPr`). A landscape page therefore needs a section break before and after it, not one global setting.
|
|
6
4
|
|
|
7
5
|
## Audit
|
|
8
6
|
|
|
@@ -10,45 +8,40 @@ Page setup lives in a `w:sectPr` element. The last one sits at the end of the bo
|
|
|
10
8
|
"<python>" "<skill>/scripts/section_audit.py" "<file.docx>"
|
|
11
9
|
```
|
|
12
10
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
Findings worth fixing: `mixed_page_sizes` (sections that disagree, usually a template artefact), `narrow_text_column` (left plus right margin eats most of the width), `portrait_wider_than_letter`.
|
|
11
|
+
Reports per section: width and height in inches, orientation derived from them, every margin, column count, section start type, header and footer parts referenced, `titlePg`, explicit page-number start. Findings: `mixed_page_sizes`, `narrow_text_column` (left and right margin eat most of the width), `portrait_wider_than_letter`.
|
|
16
12
|
|
|
17
13
|
## Common changes
|
|
18
14
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
| Different first page header | set `titlePg` on the section and fill `first_page_header` |
|
|
24
|
-
| Continuous section change (e.g. narrower margins) | `w:type val="continuous"` on the new section |
|
|
15
|
+
- New-document defaults: `docx create` spec keys `page_size`, `orientation`, `top_margin` / `bottom_margin` / `left_margin` / `right_margin` (inches).
|
|
16
|
+
- A landscape page inside a portrait document: two section breaks with the landscape section between them.
|
|
17
|
+
- A different first-page header: set `titlePg` and fill `first_page_header`.
|
|
18
|
+
- A continuous change such as narrower margins: `w:type val="continuous"`.
|
|
25
19
|
|
|
26
|
-
Landscape and columns are section properties, so a snippet is often faster than a
|
|
20
|
+
Landscape and columns are section properties, so a snippet is often faster than a script:
|
|
27
21
|
|
|
28
|
-
```
|
|
29
|
-
"<python>" - <<'PY'
|
|
22
|
+
```python
|
|
30
23
|
from docx import Document
|
|
31
24
|
from docx.enum.section import WD_ORIENT, WD_SECTION
|
|
25
|
+
|
|
32
26
|
d = Document(r"<file.docx>")
|
|
33
27
|
s = d.add_section(WD_SECTION.NEW_PAGE)
|
|
34
28
|
s.orientation = WD_ORIENT.LANDSCAPE
|
|
35
29
|
s.page_width, s.page_height = s.page_height, s.page_width
|
|
36
30
|
d.save(r"<file-edited.docx>")
|
|
37
|
-
PY
|
|
38
31
|
```
|
|
39
32
|
|
|
40
|
-
|
|
33
|
+
Setting the orientation alone does not swap the two size values.
|
|
41
34
|
|
|
42
35
|
## Headers and footers
|
|
43
36
|
|
|
44
|
-
|
|
37
|
+
`word/headerN.xml` is referenced from `w:headerReference`. A header inherits from the previous section unless the section sets its own reference. Page numbers in the footer are a `PAGE` field; counts are `NUMPAGES`; `docx_ooxml_patch.py --add-page-numbers` writes one.
|
|
45
38
|
|
|
46
39
|
## Verify
|
|
47
40
|
|
|
48
|
-
Render and flip through every page, including the
|
|
41
|
+
Render and flip through every page, including the pages after a section break: header content, page numbers, orientation, margins. The classic defect is a section break that leaves an empty page — look for a stray empty paragraph carrying it.
|
|
49
42
|
|
|
50
43
|
## Pitfalls
|
|
51
44
|
|
|
52
|
-
- `w:pgSz`
|
|
53
|
-
- `titlePg` without
|
|
54
|
-
- Deleting a section break merges two sections and keeps the earlier
|
|
45
|
+
- `w:pgSz` holds width then height; a "landscape" 11×8.5 still marked portrait means only the size values changed.
|
|
46
|
+
- `titlePg` without a filled first-page header leaves page one blank instead of showing the normal header.
|
|
47
|
+
- Deleting a section break merges two sections and keeps the earlier settings.
|
|
@@ -1,44 +1,40 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Style lint and normalisation
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
A paragraph carries a `pStyle` reference; a run carries an `rStyle` plus direct formatting (`w:b`, `w:i`, `w:sz`, `w:color`). Direct formatting wins over the style, which is why a "make all body text 11pt" request fails on the runs someone bolded and resized by hand.
|
|
3
|
+
A paragraph carries `pStyle`; a run carries `rStyle` plus direct formatting (`w:b`, `w:i`, `w:sz`, `w:color`). Direct formatting wins over the style, which is why "make all body text 11pt" fails on hand-formatted runs.
|
|
6
4
|
|
|
7
5
|
## Lint
|
|
8
6
|
|
|
9
7
|
```bash
|
|
10
|
-
"<python>" "<skill>/scripts/style_lint.py" "<file.docx>" --json "<report.json
|
|
8
|
+
"<python>" "<skill>/scripts/style_lint.py" "<file.docx>" --json "<report.json"
|
|
11
9
|
```
|
|
12
10
|
|
|
13
|
-
Findings: undefined style references, headings carrying their own paragraph or run formatting, empty runs, manual bullets (`•` typed
|
|
14
|
-
|
|
15
|
-
Exit code 1 means findings; the JSON has the counts and examples, which is what you want when the number is large.
|
|
11
|
+
Findings: undefined style references, headings carrying their own paragraph or run formatting, empty runs, manual bullets (`•` typed instead of a list style), double spaces, trailing spaces, body sizes appearing once or twice against the dominant size, tables without a style. Exit 1 means findings; the JSON holds counts and examples.
|
|
16
12
|
|
|
17
|
-
##
|
|
13
|
+
## Normalise
|
|
18
14
|
|
|
19
15
|
```bash
|
|
20
|
-
"<python>" "<skill>/scripts/style_normalize.py" "<file.docx>" --out "<file-clean.docx
|
|
21
|
-
--clear_paragraph_format --enforce_heading_spacing
|
|
16
|
+
"<python>" "<skill>/scripts/style_normalize.py" "<file.docx>" --out "<file-clean.docx" --clear_paragraph_format --enforce_heading_spacing
|
|
22
17
|
```
|
|
23
18
|
|
|
24
|
-
- `--clear_paragraph_format` strips everything from each run's `rPr` except `rStyle`, `rtl` and `lang`, and drops the paragraph-mark run properties
|
|
25
|
-
- `--enforce_heading_spacing` sets space-before
|
|
19
|
+
- `--clear_paragraph_format` strips everything from each run's `rPr` except `rStyle`, `rtl` and `lang`, and drops the paragraph-mark run properties, so the style controls the look.
|
|
20
|
+
- `--enforce_heading_spacing` sets space-before and space-after per heading level plus `keepNext`.
|
|
26
21
|
|
|
27
|
-
Run
|
|
22
|
+
Run it before applying a template: a template cannot win against direct formatting (`tasks/templates_style_packs.md`).
|
|
28
23
|
|
|
29
|
-
##
|
|
24
|
+
## Correct look
|
|
30
25
|
|
|
31
|
-
- Body text 11–12pt from `Normal
|
|
32
|
-
-
|
|
33
|
-
-
|
|
26
|
+
- Body text 11–12pt from `Normal`.
|
|
27
|
+
- Headings from `Heading 1`–`Heading 3`, with no direct bold or size.
|
|
28
|
+
- Bold for in-sentence emphasis via a character style or one `w:b` run, never for whole paragraphs that are really headings.
|
|
29
|
+
- Lists from `List Bullet` / `List Number`, never typed bullets.
|
|
34
30
|
- One table style per document, usually `Table Grid`.
|
|
35
31
|
|
|
36
32
|
## Verify
|
|
37
33
|
|
|
38
|
-
Re-run `style_lint.py`
|
|
34
|
+
Re-run `style_lint.py` and watch the counts drop, then render. Clearing direct formatting can change the hierarchy when a document faked emphasis with size; compare against the original before claiming the change was neutral.
|
|
39
35
|
|
|
40
36
|
## Pitfalls
|
|
41
37
|
|
|
42
|
-
- Clearing `rPr` removes field-result formatting
|
|
43
|
-
- A style referenced but not defined falls back to `Normal` silently — the lint is the only warning
|
|
44
|
-
- `w:rsid` and proofing attributes are noise,
|
|
38
|
+
- Clearing `rPr` also removes field-result formatting; re-run the field scripts if a number must stay bold.
|
|
39
|
+
- A style that is referenced but not defined falls back to `Normal` silently — the lint is the only warning.
|
|
40
|
+
- `w:rsid` and proofing attributes are noise, removed by `privacy_scrub.py` when the file leaves (`tasks/privacy_scrub_metadata.md`).
|
|
@@ -1,64 +1,54 @@
|
|
|
1
|
-
# Tables and
|
|
1
|
+
# Tables and layout geometry
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
A table's geometry is three things that must agree: the grid (`w:tblGrid`, one `w:gridCol` per column), the table width (`w:tblW`), and each cell width (`w:tcW`). When they disagree Word shows a table wider than the text block or columns that jump on the first edit.
|
|
3
|
+
Table geometry is three things that must agree: the grid (`w:tblGrid`, one `w:gridCol` per column), the table width (`w:tblW`) and each cell width (`w:tcW`). When they disagree, Word shows a table wider than the text block, or columns that jump on the first edit.
|
|
6
4
|
|
|
7
5
|
## Create
|
|
8
6
|
|
|
9
|
-
|
|
10
|
-
"<python>" "<cli>" docx create --spec "<spec.json>" --out "<out.docx>"
|
|
11
|
-
```
|
|
7
|
+
Use `docx create --spec` with a table block:
|
|
12
8
|
|
|
13
9
|
```json
|
|
14
|
-
{
|
|
15
|
-
"type": "table",
|
|
16
|
-
"headers": ["Region", "Revenue", "Growth"],
|
|
17
|
-
"rows": [["EMEA", "4.1M", "12%"], ["APAC", "3.2M", "9%"]],
|
|
18
|
-
"style": "Table Grid"
|
|
19
|
-
}
|
|
10
|
+
{ "type": "table", "headers": ["Region", "Q1"], "rows": [["North", "10"]], "style": "Table Grid" }
|
|
20
11
|
```
|
|
21
12
|
|
|
22
|
-
Header cells are bold.
|
|
13
|
+
Header cells are bold. To repeat the header row on every page:
|
|
23
14
|
|
|
24
15
|
```bash
|
|
25
|
-
"<python>" "<skill>/scripts/a11y_audit.py" "<out.docx>" --fix_table_headers first_row --out "<out-fixed.docx
|
|
16
|
+
"<python>" "<skill>/scripts/a11y_audit.py" "<out.docx>" --fix_table_headers first_row --out "<out-fixed.docx"
|
|
26
17
|
```
|
|
27
18
|
|
|
19
|
+
That writes `w:tblHeader` on row 1.
|
|
20
|
+
|
|
28
21
|
## Inspect and fix geometry
|
|
29
22
|
|
|
30
23
|
```bash
|
|
31
24
|
"<python>" "<skill>/scripts/table_geometry.py" "<file.docx>"
|
|
32
|
-
"<python>" "<skill>/scripts/table_geometry.py" "<file.docx>" --fix --out "<file-fixed.docx
|
|
25
|
+
"<python>" "<skill>/scripts/table_geometry.py" "<file.docx>" --fix --out "<file-fixed.docx"
|
|
33
26
|
```
|
|
34
27
|
|
|
35
|
-
The audit reports `tblW`, layout mode, every grid column in twips
|
|
36
|
-
|
|
37
|
-
Text width on Letter with 1in margins is 9360 twips — that is the number a full-width table should total.
|
|
28
|
+
The audit reports `tblW`, the layout mode, every grid column in twips with their sum, and the narrowest and widest cell. `--fix` writes a fixed-layout table at the text width (page width minus margins), rescales the grid proportionally and rewrites each `tcW`, including `gridSpan`. On Letter with 1in margins the text width is 9360 twips.
|
|
38
29
|
|
|
39
|
-
##
|
|
30
|
+
## Move data between formats
|
|
40
31
|
|
|
41
32
|
```bash
|
|
42
|
-
"<python>" "<skill>/scripts/docx_table_to_csv.py" "<file.docx>" --table_index 0 --out "<table.csv
|
|
33
|
+
"<python>" "<skill>/scripts/docx_table_to_csv.py" "<file.docx>" --table_index 0 --out "<table.csv"
|
|
43
34
|
"<python>" "<skill>/scripts/xlsx_to_docx_table.py" "<book.xlsx>" --sheet Revenue --out "<table.docx>" --title "Revenue by region"
|
|
44
35
|
```
|
|
45
36
|
|
|
46
|
-
|
|
37
|
+
`docx_table_to_csv.py` flattens the table, expands horizontal spans and repeats vertically merged values. `xlsx_to_docx_table.py` writes a bold header row plus `w:tblHeader`; it is the route for "put this spreadsheet in the report", not a screenshot or an embedded object.
|
|
47
38
|
|
|
48
39
|
## Design
|
|
49
40
|
|
|
50
|
-
- Bold header row, light grey `#D9D9D9` borders, no shading
|
|
51
|
-
- Numeric
|
|
52
|
-
- Two or three columns
|
|
53
|
-
- Caption above the table (`tasks/captions_crossrefs.md`)
|
|
54
|
-
- Avoid vertical merges: they look fine in the file and confuse every downstream reader.
|
|
41
|
+
- Bold header row, light grey `#D9D9D9` borders, no shading in the body.
|
|
42
|
+
- Numeric right-aligned, text left-aligned, thousands separators inside the cells.
|
|
43
|
+
- Two or three prose columns at most. A wide table wants landscape through a section break (`tasks/sections_layout.md`), not a smaller font.
|
|
44
|
+
- Caption above the table (`tasks/captions_crossrefs.md`). Avoid vertical merges.
|
|
55
45
|
|
|
56
46
|
## Verify
|
|
57
47
|
|
|
58
|
-
Render
|
|
48
|
+
Render the page: nothing past the right margin, header row intact, no row split across pages, last column not truncated. A table that fits in XML but overflows the page is a real defect that only the render shows.
|
|
59
49
|
|
|
60
50
|
## Pitfalls
|
|
61
51
|
|
|
62
|
-
- A table inside a
|
|
63
|
-
- `autofit` is a request, not a guarantee
|
|
64
|
-
- Cell margins default to 108 twips
|
|
52
|
+
- A table inside a cell inherits the cell width, so check the outer table first.
|
|
53
|
+
- `autofit` is a request, not a guarantee.
|
|
54
|
+
- Cell margins default to 108 twips of padding.
|
|
@@ -1,53 +1,49 @@
|
|
|
1
1
|
# Templates and style packs
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
A `.dotx` is a `.docx` whose package is a template: `styles.xml` defines the look, `numbering.xml` defines lists, and the body is empty. Copying the body would import placeholder text; what you actually need is the styles part, the numbering definitions they reference, and the theme.
|
|
3
|
+
A `.dotx` is a `.docx` whose package is a template: `styles.xml` defines the look, `numbering.xml` defines lists, the body is empty. What you need is the styles part, the numbering definitions they reference, and the theme. Copying the body imports placeholder text.
|
|
6
4
|
|
|
7
5
|
## Ask before guessing
|
|
8
6
|
|
|
9
|
-
|
|
7
|
+
Never invent a house style. Get heading fonts and sizes, body font and size, accent colour, margins, table style, and any logo or cover requirement. `references/template-elicitation.md` holds the question set.
|
|
10
8
|
|
|
11
|
-
## Apply
|
|
9
|
+
## Apply
|
|
12
10
|
|
|
13
11
|
```bash
|
|
14
|
-
"<python>" "<skill>/scripts/apply_template_styles.py" --template "<house.dotx>" --target "<draft.docx>" --out "<draft-styled.docx
|
|
12
|
+
"<python>" "<skill>/scripts/apply_template_styles.py" --template "<house.dotx>" --target "<draft.docx>" --out "<draft-styled.docx"
|
|
15
13
|
```
|
|
16
14
|
|
|
17
|
-
|
|
15
|
+
Merges style definitions by `styleId` (replacing same-id, appending missing, taking the template's `docDefaults`) and copies `numbering.xml` with its relationship and content-type override. The target's body is untouched.
|
|
18
16
|
|
|
19
|
-
Then
|
|
17
|
+
Then normalise so direct formatting does not override the imported styles:
|
|
20
18
|
|
|
21
19
|
```bash
|
|
22
|
-
"<python>" "<skill>/scripts/style_normalize.py" "<draft-styled.docx>" --out "<draft-final.docx
|
|
20
|
+
"<python>" "<skill>/scripts/style_normalize.py" "<draft-styled.docx>" --out "<draft-final.docx" --clear_paragraph_format
|
|
23
21
|
```
|
|
24
22
|
|
|
25
23
|
## Restyle without a template
|
|
26
24
|
|
|
27
|
-
|
|
25
|
+
Set values on styles, not on runs:
|
|
28
26
|
|
|
29
|
-
```
|
|
30
|
-
"<python>" - <<'PY'
|
|
27
|
+
```python
|
|
31
28
|
from docx import Document
|
|
32
29
|
from docx.shared import Pt, RGBColor
|
|
30
|
+
|
|
33
31
|
d = Document(r"<draft.docx>")
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
normal.font.size = Pt(11)
|
|
32
|
+
d.styles["Normal"].font.name = "Calibri"
|
|
33
|
+
d.styles["Normal"].font.size = Pt(11)
|
|
37
34
|
for level, size in ((1, 18), (2, 14), (3, 12)):
|
|
38
35
|
style = d.styles[f"Heading {level}"]
|
|
39
36
|
style.font.size = Pt(size)
|
|
40
37
|
style.font.color.rgb = RGBColor(0, 0, 0)
|
|
41
|
-
d.save(r"<draft-
|
|
42
|
-
PY
|
|
38
|
+
d.save(r"<draft-restyled.docx>")
|
|
43
39
|
```
|
|
44
40
|
|
|
45
41
|
## Verify
|
|
46
42
|
|
|
47
|
-
Render and compare
|
|
43
|
+
Render and compare page by page with the template's own preview: heading sizes, body leading, margins, table borders. A style that looks right in the pane but wrong on the page usually has a leftover direct-formatting run — run the lint (`tasks/style_lint_normalize.md`).
|
|
48
44
|
|
|
49
45
|
## Pitfalls
|
|
50
46
|
|
|
51
|
-
- A template applied over content that references missing styles produces the fallback look, not an error; run `style_lint.py
|
|
52
|
-
- Themes (`word/theme/theme1.xml`) carry fonts and accent colours
|
|
53
|
-
- Keeping the template's `numbering.xml` without its
|
|
47
|
+
- A template applied over content that references missing styles produces the fallback look, not an error; run `style_lint.py`.
|
|
48
|
+
- Themes (`word/theme/theme1.xml`) carry fonts and accent colours, so theme-font styles change with the theme.
|
|
49
|
+
- Keeping the template's `numbering.xml` without its abstractNum ids makes lists share one counter.
|
|
@@ -1,26 +1,22 @@
|
|
|
1
1
|
# Table of contents
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
A TOC is a `TOC` field: Word fills it on recalculation and the file stores the last result. A hand-typed list looks identical, then goes wrong on the first edit.
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
## Insert
|
|
5
|
+
Insert:
|
|
8
6
|
|
|
9
7
|
```bash
|
|
10
8
|
"<python>" "<skill>/scripts/insert_toc.py" "<file.docx>" --out "<file-toc.docx>" --levels 1-3
|
|
11
9
|
```
|
|
12
10
|
|
|
13
|
-
The script
|
|
11
|
+
The script fills a placeholder paragraph containing `[[TOC]]` (override with `--placeholder`); with no placeholder it inserts the field at the start of the body. It writes `TOC \o "1-3" \h \z \u`, sets `w:updateFields`, and leaves a short cached hint so the page is not blank before recalculation.
|
|
14
12
|
|
|
15
|
-
|
|
13
|
+
Every heading you want listed must use a heading style. `heading_audit.py` finds hand-formatted headings that the TOC silently skips.
|
|
16
14
|
|
|
17
15
|
## Rebuild after edits
|
|
18
16
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
## Static contents list
|
|
17
|
+
The cached list goes stale after a structural edit. Ask the user to update the field in Word (right-click → Update Field), or delete the field paragraphs and re-run `insert_toc.py`. Do not freeze a TOC with `fields_materialize.py`.
|
|
22
18
|
|
|
23
|
-
|
|
19
|
+
For a fixed PDF export or a hand-built template contents page, generate a static list with `internal_nav.py` instead:
|
|
24
20
|
|
|
25
21
|
```bash
|
|
26
22
|
"<python>" "<skill>/scripts/internal_nav.py" "<file.docx>" --out "<file-nav.docx>" --levels 1 2
|
|
@@ -30,10 +26,10 @@ See `tasks/navigation_internal_links.md`.
|
|
|
30
26
|
|
|
31
27
|
## Verify
|
|
32
28
|
|
|
33
|
-
Render the first pages
|
|
29
|
+
Render the first pages. A line of grey hint text means the renderer never recalculated the field; old headings mean it rendered before the update. Say which happened instead of claiming the contents are correct.
|
|
34
30
|
|
|
35
31
|
## Pitfalls
|
|
36
32
|
|
|
37
|
-
- TOC entries come from outline levels, not from text that
|
|
38
|
-
- A TOC
|
|
39
|
-
- Level 4
|
|
33
|
+
- TOC entries come from outline levels, not from text that looks like a heading.
|
|
34
|
+
- A TOC in a header or text box does not work — body only.
|
|
35
|
+
- Level 4 and deeper are excluded unless you widen `--levels`.
|
|
@@ -1,50 +1,38 @@
|
|
|
1
1
|
# Visual verification
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
## Render
|
|
3
|
+
See the pages before the user does. A document that parses and a document that looks right are different claims.
|
|
6
4
|
|
|
7
5
|
```bash
|
|
8
6
|
"<python>" "<cli>" docx render --input "<file.docx>" --out-dir "<render-dir>"
|
|
9
7
|
```
|
|
10
8
|
|
|
11
|
-
Writes one PNG per page and prints
|
|
9
|
+
Writes one PNG per page and prints the paths plus the renderer it used (`libreoffice-kit`); add `--pdf` to keep a PDF as well. For a JSON report and page list use `render_docx.py`:
|
|
12
10
|
|
|
13
11
|
```bash
|
|
14
12
|
"<python>" "<skill>/render_docx.py" "<file.docx>" --output_dir "<render-dir>" --dpi 150
|
|
15
13
|
```
|
|
16
14
|
|
|
17
|
-
## Inspect
|
|
18
|
-
|
|
19
15
|
Open every PNG with `read_image` and check, in this order:
|
|
20
16
|
|
|
21
17
|
1. **Hierarchy** — does the page show the structure the user asked for, at a glance?
|
|
22
18
|
2. **Fit** — nothing past the right margin, no clipped table column, no text over a watermark.
|
|
23
19
|
3. **Breaks** — no heading alone at the bottom, no orphaned caption, no half-empty page before a section break.
|
|
24
|
-
4. **Text** — the words
|
|
20
|
+
4. **Text** — the intended words changed, and nothing else moved.
|
|
25
21
|
5. **Glyphs** — dashes, quotes, CJK, currency symbols and maths render, not as boxes.
|
|
26
|
-
6. **Header and footer** — present, correct, not repeated on a first page that should not have
|
|
22
|
+
6. **Header and footer** — present, correct, and not repeated on a first page that should not have one.
|
|
27
23
|
|
|
28
|
-
|
|
24
|
+
Re-render after any script edit. Package-valid is not page-valid: a caption placed before the paragraph it describes, a table grown past the margin, a style merge that changed heading sizes — all are invisible in the XML and obvious in the image. To compare two revisions, `render_and_diff.py` writes a heatmapped difference page; read the diff images yourself, because a ratio is not a conclusion.
|
|
29
25
|
|
|
30
|
-
|
|
26
|
+
## When no renderer exists
|
|
31
27
|
|
|
32
|
-
|
|
28
|
+
`office_status` reports `renderers.available: false` (with a `remedy`) because the DSH LibreOffice kit was not found. `render` then exits non-zero with a JSON error naming the missing renderer, and `render_docx.py` exits 3 with `reason: no_renderer`.
|
|
33
29
|
|
|
34
30
|
- Verify structure with `docx read`, `docx_kit.py parts|text|xml` and the audit scripts.
|
|
35
|
-
-
|
|
36
|
-
-
|
|
37
|
-
|
|
38
|
-
## After any script edit
|
|
39
|
-
|
|
40
|
-
Re-render. Package-valid is not page-valid: a caption inserted before the paragraph it describes, a table grown past the margin, a style merge that changed heading sizes — all are invisible to the XML and obvious in the image.
|
|
41
|
-
|
|
42
|
-
## Comparing revisions
|
|
43
|
-
|
|
44
|
-
`render_and_diff.py` renders two files and writes a heatmapped difference page. Read the diff images yourself; a ratio is not a conclusion.
|
|
31
|
+
- Say plainly that layout was not visually verified, and that the fix is a harness install shipping the kit (the plugin records its path in `config.json` when it loads), not installing Word or LibreOffice.
|
|
32
|
+
- Never claim a file "looks good" from XML evidence alone.
|
|
45
33
|
|
|
46
34
|
## Pitfalls
|
|
47
35
|
|
|
48
36
|
- Different renderers paginate differently. Compare like with like, and do not promise Word-perfect pagination from a LibreOffice render.
|
|
49
|
-
- A
|
|
50
|
-
- Render into a fresh directory
|
|
37
|
+
- A missing font turns into a blank page: that is a finding, not a rendering bug. `missing_fonts` / `glyph_fallback` in the render JSON shows substitution happened.
|
|
38
|
+
- Render into a fresh directory: stale PNGs from an earlier run are the classic false pass.
|
|
@@ -1,35 +1,31 @@
|
|
|
1
|
-
# Watermarks and background
|
|
1
|
+
# Watermarks and background marks
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
A Word watermark is not a document feature with its own element: it is a VML shape (`v:shape` with `v:textpath`) in a header part, positioned behind the text. That is why it disappears if the header is replaced, and why it can appear on some pages and not others when sections have their own headers.
|
|
3
|
+
A Word watermark is not its own element: it is a VML shape (`v:shape` with `v:textpath`) inside a header part, behind the text. That is why it disappears when the header is replaced and can differ per section.
|
|
6
4
|
|
|
7
5
|
## Add
|
|
8
6
|
|
|
9
7
|
```bash
|
|
10
|
-
"<python>" "<skill>/scripts/watermark_add.py" "<file.docx>" --out "<file-draft.docx
|
|
8
|
+
"<python>" "<skill>/scripts/watermark_add.py" "<file.docx>" --out "<file-draft.docx" --text "DRAFT"
|
|
11
9
|
```
|
|
12
10
|
|
|
13
|
-
Writes the shape into the default header
|
|
14
|
-
|
|
15
|
-
For a per-page mark across a document with several sections, each section's header needs the shape; that is a loop over sections, not one insertion.
|
|
11
|
+
Writes the shape into the default header: light grey, rotated -45°, behind the body. The script refuses to add a second watermark when it finds one, so remove the old one first. For a mark on every page across sections, each section's header needs the shape — loop over the sections.
|
|
16
12
|
|
|
17
13
|
## Find and remove
|
|
18
14
|
|
|
19
15
|
```bash
|
|
20
16
|
"<python>" "<skill>/scripts/watermark_audit_remove.py" "<file.docx>" --mode report
|
|
21
|
-
"<python>" "<skill>/scripts/watermark_audit_remove.py" "<file.docx>" --mode remove --out "<file-clean.docx
|
|
17
|
+
"<python>" "<skill>/scripts/watermark_audit_remove.py" "<file.docx>" --mode remove --out "<file-clean.docx"
|
|
22
18
|
```
|
|
23
19
|
|
|
24
|
-
The report lists
|
|
20
|
+
The report lists every VML text path and the part it lives in, and exits 1 when any watermark exists. `--contains` narrows it to a substring, for the "template says Company Confidential" case. Remove deletes the enclosing paragraph from the header.
|
|
25
21
|
|
|
26
22
|
## Verify
|
|
27
23
|
|
|
28
|
-
Render and read the pages: the mark sits behind the text
|
|
24
|
+
Render and read the pages: the mark sits behind the text, inside the page, on every expected page. A watermark missing on page two means that section has a different header.
|
|
29
25
|
|
|
30
26
|
## Pitfalls
|
|
31
27
|
|
|
32
|
-
- A watermark in a "different first page" header only
|
|
33
|
-
- Removing the header paragraph can leave an empty header that adds vertical space
|
|
34
|
-
- An image watermark is
|
|
35
|
-
-
|
|
28
|
+
- A watermark in a "different first page" header shows only on page one.
|
|
29
|
+
- Removing the header paragraph can leave an empty header that adds vertical space — check the top margin.
|
|
30
|
+
- An image watermark is `v:imagedata`, not a text path, so the report will not name it; inspect the header XML.
|
|
31
|
+
- A watermark is not security (`tasks/protection_restrict_editing.md`).
|