docrev 0.9.13 → 0.9.15
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/.claude/settings.local.json +9 -9
- package/.gitattributes +1 -1
- package/CHANGELOG.md +149 -149
- package/PLAN-tables-and-postprocess.md +850 -850
- package/README.md +411 -391
- package/bin/rev.js +11 -11
- package/bin/rev.ts +145 -145
- package/completions/rev.bash +127 -127
- package/completions/rev.ps1 +210 -210
- package/completions/rev.zsh +207 -207
- package/dev_notes/stress2/build_adversarial.ts +186 -186
- package/dev_notes/stress2/drift_matcher.ts +62 -62
- package/dev_notes/stress2/probe_anchors.ts +35 -35
- package/dev_notes/stress2/project/discussion.before.md +3 -3
- package/dev_notes/stress2/project/discussion.md +3 -3
- package/dev_notes/stress2/project/methods.before.md +20 -20
- package/dev_notes/stress2/project/methods.md +20 -20
- package/dev_notes/stress2/project/rev.yaml +5 -5
- package/dev_notes/stress2/project/sections.yaml +4 -4
- package/dev_notes/stress2/sections.yaml +5 -5
- package/dev_notes/stress2/trace_placement.ts +50 -50
- package/dev_notes/stresstest_boundaries.ts +27 -27
- package/dev_notes/stresstest_drift_apply.ts +43 -43
- package/dev_notes/stresstest_drift_compare.ts +43 -43
- package/dev_notes/stresstest_drift_v2.ts +54 -54
- package/dev_notes/stresstest_inspect.ts +54 -54
- package/dev_notes/stresstest_pstyle.ts +55 -55
- package/dev_notes/stresstest_section_debug.ts +23 -23
- package/dev_notes/stresstest_split.ts +70 -70
- package/dev_notes/stresstest_trace.ts +19 -19
- package/dev_notes/stresstest_verify_no_overwrite.ts +40 -40
- package/dist/lib/build.d.ts +38 -1
- package/dist/lib/build.d.ts.map +1 -1
- package/dist/lib/build.js +68 -30
- package/dist/lib/build.js.map +1 -1
- package/dist/lib/commands/build.d.ts.map +1 -1
- package/dist/lib/commands/build.js +38 -5
- package/dist/lib/commands/build.js.map +1 -1
- package/dist/lib/commands/utilities.js +164 -164
- package/dist/lib/commands/word-tools.js +8 -8
- package/dist/lib/grammar.js +3 -3
- package/dist/lib/pdf-comments.js +44 -44
- package/dist/lib/plugins.js +57 -57
- package/dist/lib/pptx-themes.js +115 -115
- package/dist/lib/spelling.js +2 -2
- package/dist/lib/templates.js +387 -387
- package/dist/lib/themes.js +51 -51
- package/eslint.config.js +27 -27
- package/lib/anchor-match.ts +276 -276
- package/lib/annotations.ts +644 -644
- package/lib/build.ts +1300 -1251
- package/lib/citations.ts +160 -160
- package/lib/commands/build.ts +833 -801
- package/lib/commands/citations.ts +515 -515
- package/lib/commands/comments.ts +1050 -1050
- package/lib/commands/context.ts +174 -174
- package/lib/commands/core.ts +309 -309
- package/lib/commands/doi.ts +435 -435
- package/lib/commands/file-ops.ts +372 -372
- package/lib/commands/history.ts +320 -320
- package/lib/commands/index.ts +87 -87
- package/lib/commands/init.ts +259 -259
- package/lib/commands/merge-resolve.ts +378 -378
- package/lib/commands/preview.ts +178 -178
- package/lib/commands/project-info.ts +244 -244
- package/lib/commands/quality.ts +517 -517
- package/lib/commands/response.ts +454 -454
- package/lib/commands/section-boundaries.ts +82 -82
- package/lib/commands/sections.ts +451 -451
- package/lib/commands/sync.ts +706 -706
- package/lib/commands/text-ops.ts +449 -449
- package/lib/commands/utilities.ts +448 -448
- package/lib/commands/verify-anchors.ts +272 -272
- package/lib/commands/word-tools.ts +340 -340
- package/lib/comment-realign.ts +517 -517
- package/lib/config.ts +84 -84
- package/lib/crossref.ts +781 -781
- package/lib/csl.ts +191 -191
- package/lib/dependencies.ts +98 -98
- package/lib/diff-engine.ts +465 -465
- package/lib/doi-cache.ts +115 -115
- package/lib/doi.ts +897 -897
- package/lib/equations.ts +506 -506
- package/lib/errors.ts +346 -346
- package/lib/format.ts +541 -541
- package/lib/git.ts +326 -326
- package/lib/grammar.ts +303 -303
- package/lib/image-registry.ts +180 -180
- package/lib/import.ts +911 -911
- package/lib/journals.ts +543 -543
- package/lib/merge.ts +633 -633
- package/lib/orcid.ts +144 -144
- package/lib/pdf-comments.ts +263 -263
- package/lib/pdf-import.ts +524 -524
- package/lib/plugins.ts +362 -362
- package/lib/postprocess.ts +188 -188
- package/lib/pptx-color-filter.lua +37 -37
- package/lib/pptx-template.ts +469 -469
- package/lib/pptx-themes.ts +483 -483
- package/lib/protect-restore.ts +520 -520
- package/lib/rate-limiter.ts +94 -94
- package/lib/response.ts +197 -197
- package/lib/restore-references.ts +240 -240
- package/lib/review.ts +327 -327
- package/lib/schema.ts +417 -417
- package/lib/scientific-words.ts +73 -73
- package/lib/sections.ts +335 -335
- package/lib/slides.ts +756 -756
- package/lib/spelling.ts +334 -334
- package/lib/templates.ts +526 -526
- package/lib/themes.ts +742 -742
- package/lib/trackchanges.ts +247 -247
- package/lib/tui.ts +450 -450
- package/lib/types.ts +550 -550
- package/lib/undo.ts +250 -250
- package/lib/utils.ts +69 -69
- package/lib/variables.ts +179 -179
- package/lib/word-extraction.ts +806 -806
- package/lib/word.ts +643 -643
- package/lib/wordcomments.ts +817 -817
- package/package.json +137 -137
- package/scripts/postbuild.js +28 -28
- package/skill/REFERENCE.md +473 -431
- package/skill/SKILL.md +274 -258
- package/tsconfig.json +26 -26
- package/types/index.d.ts +525 -525
package/lib/postprocess.ts
CHANGED
|
@@ -1,188 +1,188 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Postprocess scripting for docrev
|
|
3
|
-
*
|
|
4
|
-
* Allows users to run custom scripts after output generation.
|
|
5
|
-
* Supports shell scripts, PowerShell, Python, and Node.js.
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
import * as fs from 'fs';
|
|
9
|
-
import * as path from 'path';
|
|
10
|
-
import { spawn } from 'child_process';
|
|
11
|
-
|
|
12
|
-
/**
|
|
13
|
-
* Script execution result
|
|
14
|
-
*/
|
|
15
|
-
interface ExecutionResult {
|
|
16
|
-
success: boolean;
|
|
17
|
-
stdout: string;
|
|
18
|
-
stderr: string;
|
|
19
|
-
error?: string;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
/**
|
|
23
|
-
* Execution options
|
|
24
|
-
*/
|
|
25
|
-
interface ExecutionOptions {
|
|
26
|
-
verbose?: boolean;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
/**
|
|
30
|
-
* Postprocess result
|
|
31
|
-
*/
|
|
32
|
-
interface PostprocessResult {
|
|
33
|
-
success: boolean;
|
|
34
|
-
error?: string;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
/**
|
|
38
|
-
* Execute a script with environment variables
|
|
39
|
-
*/
|
|
40
|
-
export async function executeScript(
|
|
41
|
-
scriptPath: string,
|
|
42
|
-
env: Record<string, string>,
|
|
43
|
-
options: ExecutionOptions = {}
|
|
44
|
-
): Promise<ExecutionResult> {
|
|
45
|
-
return new Promise((resolve) => {
|
|
46
|
-
const ext = path.extname(scriptPath).toLowerCase();
|
|
47
|
-
const isWindows = process.platform === 'win32';
|
|
48
|
-
let command: string;
|
|
49
|
-
let args: string[];
|
|
50
|
-
let useShell: boolean;
|
|
51
|
-
|
|
52
|
-
// Determine how to run based on extension
|
|
53
|
-
if (ext === '.ps1') {
|
|
54
|
-
command = 'powershell';
|
|
55
|
-
args = ['-ExecutionPolicy', 'Bypass', '-File', scriptPath];
|
|
56
|
-
useShell = false;
|
|
57
|
-
} else if (ext === '.py') {
|
|
58
|
-
// Python needs shell on Windows for PATH resolution
|
|
59
|
-
command = isWindows ? 'python' : 'python3';
|
|
60
|
-
// On Windows, wrap path in quotes to handle spaces
|
|
61
|
-
args = [isWindows ? `"${scriptPath}"` : scriptPath];
|
|
62
|
-
useShell = isWindows;
|
|
63
|
-
} else if (ext === '.js' || ext === '.mjs') {
|
|
64
|
-
// Node.js is typically in PATH and works without shell
|
|
65
|
-
command = process.execPath; // Use the same Node that's running this script
|
|
66
|
-
args = [scriptPath];
|
|
67
|
-
useShell = false;
|
|
68
|
-
} else {
|
|
69
|
-
// Assume shell script (.sh or no extension)
|
|
70
|
-
command = isWindows ? 'bash' : '/bin/bash';
|
|
71
|
-
args = [scriptPath];
|
|
72
|
-
useShell = false;
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
const proc = spawn(command, args, {
|
|
76
|
-
env: { ...process.env, ...env },
|
|
77
|
-
cwd: path.dirname(scriptPath),
|
|
78
|
-
stdio: ['ignore', 'pipe', 'pipe'],
|
|
79
|
-
shell: useShell,
|
|
80
|
-
// On Windows with shell, use windowsVerbatimArguments to preserve paths with spaces
|
|
81
|
-
windowsVerbatimArguments: isWindows && useShell,
|
|
82
|
-
});
|
|
83
|
-
|
|
84
|
-
let stdout = '';
|
|
85
|
-
let stderr = '';
|
|
86
|
-
|
|
87
|
-
proc.stdout.on('data', (data) => {
|
|
88
|
-
stdout += data.toString();
|
|
89
|
-
if (options.verbose) {
|
|
90
|
-
process.stdout.write(data);
|
|
91
|
-
}
|
|
92
|
-
});
|
|
93
|
-
|
|
94
|
-
proc.stderr.on('data', (data) => {
|
|
95
|
-
stderr += data.toString();
|
|
96
|
-
if (options.verbose) {
|
|
97
|
-
process.stderr.write(data);
|
|
98
|
-
}
|
|
99
|
-
});
|
|
100
|
-
|
|
101
|
-
proc.on('error', (err) => {
|
|
102
|
-
resolve({ success: false, stdout, stderr, error: err.message });
|
|
103
|
-
});
|
|
104
|
-
|
|
105
|
-
proc.on('close', (code) => {
|
|
106
|
-
if (code === 0) {
|
|
107
|
-
resolve({ success: true, stdout, stderr });
|
|
108
|
-
} else {
|
|
109
|
-
resolve({
|
|
110
|
-
success: false,
|
|
111
|
-
stdout,
|
|
112
|
-
stderr,
|
|
113
|
-
error: `Exit code ${code}: ${stderr.trim() || 'Unknown error'}`,
|
|
114
|
-
});
|
|
115
|
-
}
|
|
116
|
-
});
|
|
117
|
-
});
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
/**
|
|
121
|
-
* Run postprocess scripts for a given format
|
|
122
|
-
*/
|
|
123
|
-
export async function runPostprocess(
|
|
124
|
-
outputPath: string,
|
|
125
|
-
format: string,
|
|
126
|
-
config: { postprocess?: Record<string, string>; _configPath?: string; [key: string]: unknown },
|
|
127
|
-
options: ExecutionOptions = {}
|
|
128
|
-
): Promise<PostprocessResult> {
|
|
129
|
-
const postprocessConfig = config.postprocess || {};
|
|
130
|
-
|
|
131
|
-
// Collect scripts to run (format-specific + all)
|
|
132
|
-
const scripts: string[] = [];
|
|
133
|
-
if (postprocessConfig[format]) {
|
|
134
|
-
scripts.push(postprocessConfig[format]);
|
|
135
|
-
}
|
|
136
|
-
if (postprocessConfig.all) {
|
|
137
|
-
scripts.push(postprocessConfig.all);
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
if (scripts.length === 0) {
|
|
141
|
-
return { success: true };
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
const directory = path.dirname(outputPath);
|
|
145
|
-
const errors: string[] = [];
|
|
146
|
-
|
|
147
|
-
for (const scriptPath of scripts) {
|
|
148
|
-
const absoluteScript = path.isAbsolute(scriptPath)
|
|
149
|
-
? scriptPath
|
|
150
|
-
: path.join(directory, scriptPath);
|
|
151
|
-
|
|
152
|
-
if (!fs.existsSync(absoluteScript)) {
|
|
153
|
-
errors.push(`Postprocess script not found: ${scriptPath}`);
|
|
154
|
-
continue;
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
try {
|
|
158
|
-
if (options.verbose) {
|
|
159
|
-
console.log(`Running postprocess script: ${scriptPath}`);
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
const result = await executeScript(
|
|
163
|
-
absoluteScript,
|
|
164
|
-
{
|
|
165
|
-
OUTPUT_FILE: outputPath,
|
|
166
|
-
OUTPUT_FORMAT: format,
|
|
167
|
-
PROJECT_DIR: directory,
|
|
168
|
-
CONFIG_PATH: config._configPath || '',
|
|
169
|
-
},
|
|
170
|
-
options
|
|
171
|
-
);
|
|
172
|
-
|
|
173
|
-
if (!result.success) {
|
|
174
|
-
errors.push(`Script ${scriptPath} failed: ${result.error}`);
|
|
175
|
-
} else if (options.verbose) {
|
|
176
|
-
console.log(`Postprocess script completed: ${scriptPath}`);
|
|
177
|
-
}
|
|
178
|
-
} catch (err: unknown) {
|
|
179
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
180
|
-
errors.push(`Script ${scriptPath} error: ${message}`);
|
|
181
|
-
}
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
return {
|
|
185
|
-
success: errors.length === 0,
|
|
186
|
-
error: errors.length > 0 ? errors.join('\n') : undefined,
|
|
187
|
-
};
|
|
188
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* Postprocess scripting for docrev
|
|
3
|
+
*
|
|
4
|
+
* Allows users to run custom scripts after output generation.
|
|
5
|
+
* Supports shell scripts, PowerShell, Python, and Node.js.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import * as fs from 'fs';
|
|
9
|
+
import * as path from 'path';
|
|
10
|
+
import { spawn } from 'child_process';
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Script execution result
|
|
14
|
+
*/
|
|
15
|
+
interface ExecutionResult {
|
|
16
|
+
success: boolean;
|
|
17
|
+
stdout: string;
|
|
18
|
+
stderr: string;
|
|
19
|
+
error?: string;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Execution options
|
|
24
|
+
*/
|
|
25
|
+
interface ExecutionOptions {
|
|
26
|
+
verbose?: boolean;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Postprocess result
|
|
31
|
+
*/
|
|
32
|
+
interface PostprocessResult {
|
|
33
|
+
success: boolean;
|
|
34
|
+
error?: string;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Execute a script with environment variables
|
|
39
|
+
*/
|
|
40
|
+
export async function executeScript(
|
|
41
|
+
scriptPath: string,
|
|
42
|
+
env: Record<string, string>,
|
|
43
|
+
options: ExecutionOptions = {}
|
|
44
|
+
): Promise<ExecutionResult> {
|
|
45
|
+
return new Promise((resolve) => {
|
|
46
|
+
const ext = path.extname(scriptPath).toLowerCase();
|
|
47
|
+
const isWindows = process.platform === 'win32';
|
|
48
|
+
let command: string;
|
|
49
|
+
let args: string[];
|
|
50
|
+
let useShell: boolean;
|
|
51
|
+
|
|
52
|
+
// Determine how to run based on extension
|
|
53
|
+
if (ext === '.ps1') {
|
|
54
|
+
command = 'powershell';
|
|
55
|
+
args = ['-ExecutionPolicy', 'Bypass', '-File', scriptPath];
|
|
56
|
+
useShell = false;
|
|
57
|
+
} else if (ext === '.py') {
|
|
58
|
+
// Python needs shell on Windows for PATH resolution
|
|
59
|
+
command = isWindows ? 'python' : 'python3';
|
|
60
|
+
// On Windows, wrap path in quotes to handle spaces
|
|
61
|
+
args = [isWindows ? `"${scriptPath}"` : scriptPath];
|
|
62
|
+
useShell = isWindows;
|
|
63
|
+
} else if (ext === '.js' || ext === '.mjs') {
|
|
64
|
+
// Node.js is typically in PATH and works without shell
|
|
65
|
+
command = process.execPath; // Use the same Node that's running this script
|
|
66
|
+
args = [scriptPath];
|
|
67
|
+
useShell = false;
|
|
68
|
+
} else {
|
|
69
|
+
// Assume shell script (.sh or no extension)
|
|
70
|
+
command = isWindows ? 'bash' : '/bin/bash';
|
|
71
|
+
args = [scriptPath];
|
|
72
|
+
useShell = false;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const proc = spawn(command, args, {
|
|
76
|
+
env: { ...process.env, ...env },
|
|
77
|
+
cwd: path.dirname(scriptPath),
|
|
78
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
79
|
+
shell: useShell,
|
|
80
|
+
// On Windows with shell, use windowsVerbatimArguments to preserve paths with spaces
|
|
81
|
+
windowsVerbatimArguments: isWindows && useShell,
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
let stdout = '';
|
|
85
|
+
let stderr = '';
|
|
86
|
+
|
|
87
|
+
proc.stdout.on('data', (data) => {
|
|
88
|
+
stdout += data.toString();
|
|
89
|
+
if (options.verbose) {
|
|
90
|
+
process.stdout.write(data);
|
|
91
|
+
}
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
proc.stderr.on('data', (data) => {
|
|
95
|
+
stderr += data.toString();
|
|
96
|
+
if (options.verbose) {
|
|
97
|
+
process.stderr.write(data);
|
|
98
|
+
}
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
proc.on('error', (err) => {
|
|
102
|
+
resolve({ success: false, stdout, stderr, error: err.message });
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
proc.on('close', (code) => {
|
|
106
|
+
if (code === 0) {
|
|
107
|
+
resolve({ success: true, stdout, stderr });
|
|
108
|
+
} else {
|
|
109
|
+
resolve({
|
|
110
|
+
success: false,
|
|
111
|
+
stdout,
|
|
112
|
+
stderr,
|
|
113
|
+
error: `Exit code ${code}: ${stderr.trim() || 'Unknown error'}`,
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
});
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Run postprocess scripts for a given format
|
|
122
|
+
*/
|
|
123
|
+
export async function runPostprocess(
|
|
124
|
+
outputPath: string,
|
|
125
|
+
format: string,
|
|
126
|
+
config: { postprocess?: Record<string, string>; _configPath?: string; [key: string]: unknown },
|
|
127
|
+
options: ExecutionOptions = {}
|
|
128
|
+
): Promise<PostprocessResult> {
|
|
129
|
+
const postprocessConfig = config.postprocess || {};
|
|
130
|
+
|
|
131
|
+
// Collect scripts to run (format-specific + all)
|
|
132
|
+
const scripts: string[] = [];
|
|
133
|
+
if (postprocessConfig[format]) {
|
|
134
|
+
scripts.push(postprocessConfig[format]);
|
|
135
|
+
}
|
|
136
|
+
if (postprocessConfig.all) {
|
|
137
|
+
scripts.push(postprocessConfig.all);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
if (scripts.length === 0) {
|
|
141
|
+
return { success: true };
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
const directory = path.dirname(outputPath);
|
|
145
|
+
const errors: string[] = [];
|
|
146
|
+
|
|
147
|
+
for (const scriptPath of scripts) {
|
|
148
|
+
const absoluteScript = path.isAbsolute(scriptPath)
|
|
149
|
+
? scriptPath
|
|
150
|
+
: path.join(directory, scriptPath);
|
|
151
|
+
|
|
152
|
+
if (!fs.existsSync(absoluteScript)) {
|
|
153
|
+
errors.push(`Postprocess script not found: ${scriptPath}`);
|
|
154
|
+
continue;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
try {
|
|
158
|
+
if (options.verbose) {
|
|
159
|
+
console.log(`Running postprocess script: ${scriptPath}`);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
const result = await executeScript(
|
|
163
|
+
absoluteScript,
|
|
164
|
+
{
|
|
165
|
+
OUTPUT_FILE: outputPath,
|
|
166
|
+
OUTPUT_FORMAT: format,
|
|
167
|
+
PROJECT_DIR: directory,
|
|
168
|
+
CONFIG_PATH: config._configPath || '',
|
|
169
|
+
},
|
|
170
|
+
options
|
|
171
|
+
);
|
|
172
|
+
|
|
173
|
+
if (!result.success) {
|
|
174
|
+
errors.push(`Script ${scriptPath} failed: ${result.error}`);
|
|
175
|
+
} else if (options.verbose) {
|
|
176
|
+
console.log(`Postprocess script completed: ${scriptPath}`);
|
|
177
|
+
}
|
|
178
|
+
} catch (err: unknown) {
|
|
179
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
180
|
+
errors.push(`Script ${scriptPath} error: ${message}`);
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
return {
|
|
185
|
+
success: errors.length === 0,
|
|
186
|
+
error: errors.length > 0 ? errors.join('\n') : undefined,
|
|
187
|
+
};
|
|
188
|
+
}
|
|
@@ -1,37 +1,37 @@
|
|
|
1
|
-
-- Pandoc Lua filter to add color support for PPTX
|
|
2
|
-
-- Handles [text]{color=#RRGGBB} syntax
|
|
3
|
-
|
|
4
|
-
function Span(elem)
|
|
5
|
-
local color = elem.attributes['color']
|
|
6
|
-
if color then
|
|
7
|
-
-- Remove # if present
|
|
8
|
-
color = color:gsub('^#', '')
|
|
9
|
-
|
|
10
|
-
-- Create raw OpenXML for colored text
|
|
11
|
-
local content_text = pandoc.utils.stringify(elem.content)
|
|
12
|
-
|
|
13
|
-
-- Check if content has bold
|
|
14
|
-
local is_bold = false
|
|
15
|
-
for _, item in ipairs(elem.content) do
|
|
16
|
-
if item.t == 'Strong' then
|
|
17
|
-
is_bold = true
|
|
18
|
-
content_text = pandoc.utils.stringify(item.content)
|
|
19
|
-
break
|
|
20
|
-
end
|
|
21
|
-
end
|
|
22
|
-
|
|
23
|
-
local bold_attr = ''
|
|
24
|
-
if is_bold then
|
|
25
|
-
bold_attr = ' b="1"'
|
|
26
|
-
end
|
|
27
|
-
|
|
28
|
-
-- Return raw OOXML span with color
|
|
29
|
-
local ooxml = string.format(
|
|
30
|
-
'<a:r><a:rPr%s><a:solidFill><a:srgbClr val="%s"/></a:solidFill></a:rPr><a:t>%s</a:t></a:r>',
|
|
31
|
-
bold_attr, color, content_text
|
|
32
|
-
)
|
|
33
|
-
|
|
34
|
-
return pandoc.RawInline('openxml', ooxml)
|
|
35
|
-
end
|
|
36
|
-
return elem
|
|
37
|
-
end
|
|
1
|
+
-- Pandoc Lua filter to add color support for PPTX
|
|
2
|
+
-- Handles [text]{color=#RRGGBB} syntax
|
|
3
|
+
|
|
4
|
+
function Span(elem)
|
|
5
|
+
local color = elem.attributes['color']
|
|
6
|
+
if color then
|
|
7
|
+
-- Remove # if present
|
|
8
|
+
color = color:gsub('^#', '')
|
|
9
|
+
|
|
10
|
+
-- Create raw OpenXML for colored text
|
|
11
|
+
local content_text = pandoc.utils.stringify(elem.content)
|
|
12
|
+
|
|
13
|
+
-- Check if content has bold
|
|
14
|
+
local is_bold = false
|
|
15
|
+
for _, item in ipairs(elem.content) do
|
|
16
|
+
if item.t == 'Strong' then
|
|
17
|
+
is_bold = true
|
|
18
|
+
content_text = pandoc.utils.stringify(item.content)
|
|
19
|
+
break
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
local bold_attr = ''
|
|
24
|
+
if is_bold then
|
|
25
|
+
bold_attr = ' b="1"'
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
-- Return raw OOXML span with color
|
|
29
|
+
local ooxml = string.format(
|
|
30
|
+
'<a:r><a:rPr%s><a:solidFill><a:srgbClr val="%s"/></a:solidFill></a:rPr><a:t>%s</a:t></a:r>',
|
|
31
|
+
bold_attr, color, content_text
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
return pandoc.RawInline('openxml', ooxml)
|
|
35
|
+
end
|
|
36
|
+
return elem
|
|
37
|
+
end
|