@richscripts/richtexteditor 2.6.4 → 2.7.0
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/README.md +21 -1
- package/package.json +2 -2
- package/richtexteditor/all_plugins.js +355 -5
- package/richtexteditor/all_plugins.min.js +56 -56
- package/richtexteditor/plugins/all_plugins.js +355 -5
- package/richtexteditor/plugins/all_plugins.min.js +56 -56
- package/richtexteditor/plugins/documentimport.js +25 -2
- package/richtexteditor/plugins/docxexport.js +31 -0
- package/richtexteditor/plugins/inlinecode.js +270 -0
- package/richtexteditor/plugins/pdfexport.js +25 -2
- package/richtexteditor/plugins/wordexport.js +3 -1
- package/richtexteditor/rte.js +15 -3
|
@@ -1138,6 +1138,19 @@ function RTE_Plugin_DocumentImport() {
|
|
|
1138
1138
|
// matches the old behaviour rather than inventing <ol>s.
|
|
1139
1139
|
info.ordered = !!fmt && fmt !== "bullet" && fmt !== "none";
|
|
1140
1140
|
}
|
|
1141
|
+
// Word paragraph indentation. Nothing read this before 2026-09-04,
|
|
1142
|
+
// so every indented paragraph in every imported .docx arrived
|
|
1143
|
+
// flush left and the indentation was lost silently.
|
|
1144
|
+
// w:left is in twips; 15 twips = 1px.
|
|
1145
|
+
var ind = firstChildEl(ppr, "ind");
|
|
1146
|
+
if (ind) {
|
|
1147
|
+
var lft = ind.getAttributeNS(W, "left") || ind.getAttribute("w:left") || "";
|
|
1148
|
+
var tw = parseInt(lft, 10);
|
|
1149
|
+
// 720 twips alongside the Quote style is the blockquote's own
|
|
1150
|
+
// indent, already carried by the blockquote element itself.
|
|
1151
|
+
if (tw > 0 && !(info.quote && tw === 720))
|
|
1152
|
+
info.indentPx = Math.round(tw / 15);
|
|
1153
|
+
}
|
|
1141
1154
|
var jc = firstChildEl(ppr, "jc");
|
|
1142
1155
|
if (jc) {
|
|
1143
1156
|
var a = jc.getAttributeNS(W, "val") || jc.getAttribute("w:val") || "";
|
|
@@ -1204,10 +1217,20 @@ function RTE_Plugin_DocumentImport() {
|
|
|
1204
1217
|
continue;
|
|
1205
1218
|
}
|
|
1206
1219
|
closeListsTo(0);
|
|
1207
|
-
if (st.heading)
|
|
1220
|
+
if (st.heading) {
|
|
1221
|
+
// Headings carry w:ind too; without this an indented
|
|
1222
|
+
// heading imported flush left.
|
|
1223
|
+
var hs = st.indentPx ? ' style="margin-left:' + st.indentPx + 'px"' : "";
|
|
1224
|
+
out.push("<h" + st.heading + hs + ">" + (inner || "") + "</h" + st.heading + ">");
|
|
1225
|
+
}
|
|
1208
1226
|
else if (st.quote) out.push("<blockquote>" + (inner || "<br>") + "</blockquote>");
|
|
1209
1227
|
else {
|
|
1210
|
-
var
|
|
1228
|
+
var decl = [];
|
|
1229
|
+
if (st.align) decl.push("text-align:" + st.align);
|
|
1230
|
+
// indentUseMargin defaults to margin, and margin is what
|
|
1231
|
+
// w:ind means, so import writes margin-left to match.
|
|
1232
|
+
if (st.indentPx) decl.push("margin-left:" + st.indentPx + "px");
|
|
1233
|
+
var style = decl.length ? ' style="' + decl.join(";") + '"' : "";
|
|
1211
1234
|
// A paragraph that contained ONLY a text box has no text of
|
|
1212
1235
|
// its own; emitting an empty <p> before the box adds a blank
|
|
1213
1236
|
// line that was never in the document.
|
|
@@ -612,6 +612,31 @@ function RTE_Plugin_DocxExport() {
|
|
|
612
612
|
var revSeq = { n: 1 };
|
|
613
613
|
var base = { bold: false, italic: false, underline: false, strike: false, sizeHalfPoints: 0, color: null, highlight: null, vertAlign: null, mono: false };
|
|
614
614
|
|
|
615
|
+
|
|
616
|
+
// A block indented in the editor carries margin-left (or padding-left,
|
|
617
|
+
// per indentUseMargin). Word expresses the same thing as w:ind, in
|
|
618
|
+
// TWIPS. Before 2026-09-04 indentation reached Word only when it was
|
|
619
|
+
// spelled <blockquote>, which dragged the Quote style along with it -
|
|
620
|
+
// so indented body text arrived italic and accent-coloured.
|
|
621
|
+
// 1px = 0.75pt and 1pt = 20 twips, hence px * 15.
|
|
622
|
+
function indentTwips(node) {
|
|
623
|
+
if (!node || !node.style) return 0;
|
|
624
|
+
var raw = node.style.marginLeft || node.style.paddingLeft || "";
|
|
625
|
+
var m = /^\s*(-?[\d.]+)\s*([a-z%]*)\s*$/i.exec(raw);
|
|
626
|
+
if (!m) return 0;
|
|
627
|
+
var n = parseFloat(m[1]);
|
|
628
|
+
if (!n || n <= 0) return 0;
|
|
629
|
+
var unit = (m[2] || "px").toLowerCase();
|
|
630
|
+
var px = unit === "px" ? n
|
|
631
|
+
: unit === "pt" ? n * (4 / 3)
|
|
632
|
+
: unit === "in" ? n * 96
|
|
633
|
+
: unit === "cm" ? n * 37.795
|
|
634
|
+
: unit === "mm" ? n * 3.7795
|
|
635
|
+
: (unit === "em" || unit === "rem") ? n * 16
|
|
636
|
+
: 0;
|
|
637
|
+
return Math.round(px * 15);
|
|
638
|
+
}
|
|
639
|
+
|
|
615
640
|
function para(node, style, opts) {
|
|
616
641
|
opts = opts || {};
|
|
617
642
|
var pr = "";
|
|
@@ -620,6 +645,12 @@ function RTE_Plugin_DocxExport() {
|
|
|
620
645
|
if (opts.pre) pr += '<w:pStyle w:val="Code"/>';
|
|
621
646
|
if (opts.numId) pr += "<w:numPr><w:ilvl w:val=\"" + (opts.level || 0) + "\"/><w:numId w:val=\"" + opts.numId + "\"/></w:numPr>";
|
|
622
647
|
if (opts.pageBreakBefore) pr += "<w:pageBreakBefore/>";
|
|
648
|
+
// Independent of the Quote style: a real blockquote already got
|
|
649
|
+
// its w:ind above, so only emit this for everything else.
|
|
650
|
+
if (!opts.quote) {
|
|
651
|
+
var indTw = indentTwips(node);
|
|
652
|
+
if (indTw > 0) pr += '<w:ind w:left="' + indTw + '"/>';
|
|
653
|
+
}
|
|
623
654
|
var align = node && node.style ? node.style.textAlign : "";
|
|
624
655
|
if (align === "center" || align === "right") pr += '<w:jc w:val="' + align + '"/>';
|
|
625
656
|
else if (align === "justify") pr += '<w:jc w:val="both"/>';
|
|
@@ -0,0 +1,270 @@
|
|
|
1
|
+
if (!window.RTE_DefaultConfig) window.RTE_DefaultConfig = {};
|
|
2
|
+
|
|
3
|
+
// 2026-09-04 Inline code — CKEditor 5's "Code" feature (`<code>`), the one
|
|
4
|
+
// basic-style it ships that this editor had no command for. Markdown `x` was
|
|
5
|
+
// the only way to produce inline code here, which meant there was no way to
|
|
6
|
+
// REMOVE it and no toolbar affordance at all.
|
|
7
|
+
//
|
|
8
|
+
// The command is "inlinecode", NOT "code". "code" is already taken by the
|
|
9
|
+
// HTML-source-view toggle in the core (__Is_Cmd_Active case "code" ->
|
|
10
|
+
// ___Is_CodeMode()), so registering "code" here would have collided with source
|
|
11
|
+
// view. keyboarda11y.js has carried "inlinecode" in its TOGGLE_CMD list all
|
|
12
|
+
// along, with a comment that it matched nothing in this build; a core
|
|
13
|
+
// __Is_Cmd_Active case added alongside this plugin is what makes it match, so
|
|
14
|
+
// the button reports aria-pressed and not just a highlight.
|
|
15
|
+
//
|
|
16
|
+
// Design notes:
|
|
17
|
+
// - Toggling is done by SPLITTING the <code> element around the selection
|
|
18
|
+
// rather than by lifting text nodes out of it. Splitting preserves nested
|
|
19
|
+
// inline markup: un-coding the middle of <code>a <em>b</em> c</code> keeps
|
|
20
|
+
// the <em>. Lifting text nodes would have dropped it.
|
|
21
|
+
// - A collapsed selection acts on the word under the caret. That is the same
|
|
22
|
+
// choice changecase.js makes, and for the same reason: it makes the command
|
|
23
|
+
// usable from a keyboard shortcut without selecting first. The alternative -
|
|
24
|
+
// inserting an empty <code> and putting the caret inside - is the caret trap
|
|
25
|
+
// documented in the footnotes work, so it is deliberately not done.
|
|
26
|
+
// - Never nests inside <pre> or an existing <code>: both already mean
|
|
27
|
+
// "preformatted", and nesting produces markup no serializer round-trips.
|
|
28
|
+
// - Emits a bare <code> element and no rte-* class. That keeps the styling in
|
|
29
|
+
// the content stylesheet where authors can override it, and keeps the plugin
|
|
30
|
+
// out of check-css-contract.mjs, which only scans the four theme sheets.
|
|
31
|
+
RTE_DefaultConfig.plugin_inlinecode = RTE_Plugin_InlineCode;
|
|
32
|
+
|
|
33
|
+
if (!RTE_DefaultConfig.text_inlinecode) RTE_DefaultConfig.text_inlinecode = "Inline code";
|
|
34
|
+
|
|
35
|
+
if (!RTE_DefaultConfig.svgCode_inlinecode) {
|
|
36
|
+
RTE_DefaultConfig.svgCode_inlinecode = '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="7 8 3 12 7 16"/><polyline points="17 8 21 12 17 16"/></svg>';
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function RTE_Plugin_InlineCode() {
|
|
40
|
+
var obj = this;
|
|
41
|
+
var config, editor;
|
|
42
|
+
|
|
43
|
+
obj.PluginName = "InlineCode";
|
|
44
|
+
|
|
45
|
+
obj.InitConfig = function (argconfig) { config = argconfig; };
|
|
46
|
+
|
|
47
|
+
obj.InitEditor = function (argeditor) {
|
|
48
|
+
editor = argeditor;
|
|
49
|
+
|
|
50
|
+
editor.attachEvent("exec_command_inlinecode", function (state) {
|
|
51
|
+
state.returnValue = true;
|
|
52
|
+
obj.Toggle();
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
// Public API, matching the shape the other toggle plugins expose.
|
|
56
|
+
editor.toggleInlineCode = function () { return obj.Toggle(); };
|
|
57
|
+
editor.isInlineCode = function () { return obj.IsActive(); };
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
function getDoc() { try { return editor.getDocument(); } catch (e) { return null; } }
|
|
61
|
+
function getEditable() { try { return editor.getEditable(); } catch (e) { return null; } }
|
|
62
|
+
|
|
63
|
+
function getRange(doc) {
|
|
64
|
+
var sel = doc && doc.getSelection ? doc.getSelection() : null;
|
|
65
|
+
if (!sel || !sel.rangeCount) return null;
|
|
66
|
+
return sel.getRangeAt(0);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// Nearest <code> ancestor that is still inside the editable area.
|
|
70
|
+
function codeAncestor(node, editable) {
|
|
71
|
+
while (node && node !== editable) {
|
|
72
|
+
if (node.nodeType === 1 && node.nodeName === "CODE") return node;
|
|
73
|
+
node = node.parentNode;
|
|
74
|
+
}
|
|
75
|
+
return null;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function inPre(node, editable) {
|
|
79
|
+
while (node && node !== editable) {
|
|
80
|
+
if (node.nodeType === 1 && node.nodeName === "PRE") return true;
|
|
81
|
+
node = node.parentNode;
|
|
82
|
+
}
|
|
83
|
+
return false;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
obj.IsActive = function () {
|
|
87
|
+
var doc = getDoc(), editable = getEditable();
|
|
88
|
+
if (!doc || !editable) return false;
|
|
89
|
+
var range = getRange(doc);
|
|
90
|
+
if (!range) return false;
|
|
91
|
+
return !!codeAncestor(range.startContainer, editable);
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
// ---- selection helpers -----------------------------------------------
|
|
95
|
+
|
|
96
|
+
// Expand a collapsed caret to the word under it. Returns false when the
|
|
97
|
+
// caret is not sitting in a word, in which case the command does nothing
|
|
98
|
+
// rather than creating an empty element.
|
|
99
|
+
function expandToWord(range) {
|
|
100
|
+
var node = range.startContainer;
|
|
101
|
+
if (!node || node.nodeType !== 3) return false;
|
|
102
|
+
var text = node.data || "";
|
|
103
|
+
var offset = range.startOffset;
|
|
104
|
+
var start = offset, end = offset;
|
|
105
|
+
while (start > 0 && /\S/.test(text.charAt(start - 1))) start--;
|
|
106
|
+
while (end < text.length && /\S/.test(text.charAt(end))) end++;
|
|
107
|
+
if (start === end) return false;
|
|
108
|
+
range.setStart(node, start);
|
|
109
|
+
range.setEnd(node, end);
|
|
110
|
+
return true;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// Every text node the range actually covers. Boundary text nodes are split
|
|
114
|
+
// first so a partially-selected node is not treated as fully selected.
|
|
115
|
+
function selectedTextNodes(doc, range, editable) {
|
|
116
|
+
if (range.startContainer.nodeType === 3 && range.startOffset > 0) {
|
|
117
|
+
var s = range.startContainer;
|
|
118
|
+
if (range.startOffset < s.data.length) {
|
|
119
|
+
var rest = s.splitText(range.startOffset);
|
|
120
|
+
var endSame = (range.endContainer === s);
|
|
121
|
+
var endOff = range.endOffset;
|
|
122
|
+
range.setStart(rest, 0);
|
|
123
|
+
if (endSame) range.setEnd(rest, endOff - s.data.length);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
if (range.endContainer.nodeType === 3 && range.endOffset < range.endContainer.data.length && range.endOffset > 0) {
|
|
127
|
+
range.endContainer.splitText(range.endOffset);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
var out = [];
|
|
131
|
+
var walker = doc.createTreeWalker(editable, 4 /* SHOW_TEXT */, null, false);
|
|
132
|
+
var n;
|
|
133
|
+
while ((n = walker.nextNode())) {
|
|
134
|
+
if (!n.data) continue;
|
|
135
|
+
if (range.intersectsNode ? range.intersectsNode(n) : true) {
|
|
136
|
+
// intersectsNode is true for merely-touching nodes, so confirm
|
|
137
|
+
// the node is genuinely within the boundaries.
|
|
138
|
+
var r = doc.createRange();
|
|
139
|
+
r.selectNodeContents(n);
|
|
140
|
+
if (range.compareBoundaryPoints(Range.END_TO_START, r) < 0 &&
|
|
141
|
+
range.compareBoundaryPoints(Range.START_TO_END, r) > 0) {
|
|
142
|
+
out.push(n);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
return out;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// ---- apply / remove ---------------------------------------------------
|
|
150
|
+
|
|
151
|
+
// Split `code` around the selection and unwrap only the overlapping part,
|
|
152
|
+
// so partial removal keeps the untouched halves coded.
|
|
153
|
+
function unwrapPart(doc, code, range) {
|
|
154
|
+
var parent = code.parentNode;
|
|
155
|
+
if (!parent) return;
|
|
156
|
+
|
|
157
|
+
var startsInside = code.contains(range.startContainer);
|
|
158
|
+
var endsInside = code.contains(range.endContainer);
|
|
159
|
+
|
|
160
|
+
// Tail first: extracting it cannot disturb the head's boundaries.
|
|
161
|
+
var after = doc.createRange();
|
|
162
|
+
if (endsInside) after.setStart(range.endContainer, range.endOffset);
|
|
163
|
+
else after.setStart(code, code.childNodes.length);
|
|
164
|
+
after.setEnd(code, code.childNodes.length);
|
|
165
|
+
var afterFrag = after.extractContents();
|
|
166
|
+
|
|
167
|
+
var before = doc.createRange();
|
|
168
|
+
before.setStart(code, 0);
|
|
169
|
+
if (startsInside) before.setEnd(range.startContainer, range.startOffset);
|
|
170
|
+
else before.setEnd(code, 0);
|
|
171
|
+
var beforeFrag = before.extractContents();
|
|
172
|
+
|
|
173
|
+
if (beforeFrag && beforeFrag.textContent !== "") {
|
|
174
|
+
var head = doc.createElement("code");
|
|
175
|
+
head.appendChild(beforeFrag);
|
|
176
|
+
parent.insertBefore(head, code);
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
// What is left in `code` is exactly the selected part: unwrap it.
|
|
180
|
+
while (code.firstChild) parent.insertBefore(code.firstChild, code);
|
|
181
|
+
|
|
182
|
+
if (afterFrag && afterFrag.textContent !== "") {
|
|
183
|
+
var tail = doc.createElement("code");
|
|
184
|
+
tail.appendChild(afterFrag);
|
|
185
|
+
parent.insertBefore(tail, code);
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
parent.removeChild(code);
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
function wrapNodes(doc, nodes, editable) {
|
|
192
|
+
// Group contiguous siblings so a run of text under one parent becomes a
|
|
193
|
+
// single <code>, not one per text node.
|
|
194
|
+
var group = [], groups = [];
|
|
195
|
+
for (var i = 0; i < nodes.length; i++) {
|
|
196
|
+
var n = nodes[i];
|
|
197
|
+
if (inPre(n, editable) || codeAncestor(n, editable)) continue;
|
|
198
|
+
if (group.length && group[group.length - 1].parentNode === n.parentNode &&
|
|
199
|
+
group[group.length - 1].nextSibling === n) {
|
|
200
|
+
group.push(n);
|
|
201
|
+
} else {
|
|
202
|
+
if (group.length) groups.push(group);
|
|
203
|
+
group = [n];
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
if (group.length) groups.push(group);
|
|
207
|
+
|
|
208
|
+
for (var g = 0; g < groups.length; g++) {
|
|
209
|
+
var run = groups[g];
|
|
210
|
+
// Whitespace-only runs would produce a <code> containing nothing but
|
|
211
|
+
// a space, which is invisible formatting debris.
|
|
212
|
+
var joined = "";
|
|
213
|
+
for (var k = 0; k < run.length; k++) joined += run[k].data;
|
|
214
|
+
if (!/\S/.test(joined)) continue;
|
|
215
|
+
|
|
216
|
+
var code = doc.createElement("code");
|
|
217
|
+
run[0].parentNode.insertBefore(code, run[0]);
|
|
218
|
+
for (var j = 0; j < run.length; j++) code.appendChild(run[j]);
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
// Adjacent <code><code> pairs read as one to the user but serialize as two.
|
|
223
|
+
function mergeAdjacent(editable) {
|
|
224
|
+
var all = editable.querySelectorAll("code");
|
|
225
|
+
for (var i = 0; i < all.length; i++) {
|
|
226
|
+
var code = all[i];
|
|
227
|
+
var next = code.nextSibling;
|
|
228
|
+
while (next && next.nodeType === 1 && next.nodeName === "CODE") {
|
|
229
|
+
while (next.firstChild) code.appendChild(next.firstChild);
|
|
230
|
+
next.parentNode.removeChild(next);
|
|
231
|
+
next = code.nextSibling;
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
obj.Toggle = function () {
|
|
237
|
+
var doc = getDoc(), editable = getEditable();
|
|
238
|
+
if (!doc || !editable) return false;
|
|
239
|
+
var range = getRange(doc);
|
|
240
|
+
if (!range) return false;
|
|
241
|
+
if (!editable.contains(range.startContainer)) return false;
|
|
242
|
+
|
|
243
|
+
if (range.collapsed && !expandToWord(range)) return false;
|
|
244
|
+
|
|
245
|
+
var nodes = selectedTextNodes(doc, range, editable);
|
|
246
|
+
if (!nodes.length) return false;
|
|
247
|
+
|
|
248
|
+
// Remove when every selected text node is already coded; otherwise apply.
|
|
249
|
+
var allCoded = true;
|
|
250
|
+
for (var i = 0; i < nodes.length; i++) {
|
|
251
|
+
if (!/\S/.test(nodes[i].data)) continue;
|
|
252
|
+
if (!codeAncestor(nodes[i], editable)) { allCoded = false; break; }
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
if (allCoded) {
|
|
256
|
+
var codes = [], seen = [];
|
|
257
|
+
for (var n = 0; n < nodes.length; n++) {
|
|
258
|
+
var c = codeAncestor(nodes[n], editable);
|
|
259
|
+
if (c && seen.indexOf(c) < 0) { seen.push(c); codes.push(c); }
|
|
260
|
+
}
|
|
261
|
+
for (var x = 0; x < codes.length; x++) unwrapPart(doc, codes[x], range);
|
|
262
|
+
} else {
|
|
263
|
+
wrapNodes(doc, nodes, editable);
|
|
264
|
+
mergeAdjacent(editable);
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
try { editor.updateToolbarStatus && editor.updateToolbarStatus(); } catch (e) { }
|
|
268
|
+
return true;
|
|
269
|
+
};
|
|
270
|
+
}
|
|
@@ -393,6 +393,28 @@ function RTE_Plugin_PdfExport() {
|
|
|
393
393
|
var self = this;
|
|
394
394
|
var base = { bold: false, italic: false, underline: false, strike: false, size: this.baseSize, color: [0, 0, 0], mono: false, link: null };
|
|
395
395
|
|
|
396
|
+
// A block indented in the editor carries margin-left (or padding-left, per
|
|
397
|
+
// indentUseMargin). Before 2026-09-04 the PDF honoured an indent only when
|
|
398
|
+
// it was spelled <blockquote>, because that was the only way the editor
|
|
399
|
+
// could express one. Points, since that is the PDF's unit: 1px = 0.75pt.
|
|
400
|
+
function cssIndentPt(node) {
|
|
401
|
+
if (!node || !node.style) return 0;
|
|
402
|
+
var raw = node.style.marginLeft || node.style.paddingLeft || "";
|
|
403
|
+
var m = /^\s*(-?[\d.]+)\s*([a-z%]*)\s*$/i.exec(raw);
|
|
404
|
+
if (!m) return 0;
|
|
405
|
+
var n = parseFloat(m[1]);
|
|
406
|
+
if (!n || n <= 0) return 0;
|
|
407
|
+
var unit = (m[2] || "px").toLowerCase();
|
|
408
|
+
var px = unit === "px" ? n
|
|
409
|
+
: unit === "pt" ? n * (4 / 3)
|
|
410
|
+
: unit === "in" ? n * 96
|
|
411
|
+
: unit === "cm" ? n * 37.795
|
|
412
|
+
: unit === "mm" ? n * 3.7795
|
|
413
|
+
: (unit === "em" || unit === "rem") ? n * 16
|
|
414
|
+
: 0;
|
|
415
|
+
return px * 0.75;
|
|
416
|
+
}
|
|
417
|
+
|
|
396
418
|
function block(node, style, indent, listMarker, listId, listOrdered) {
|
|
397
419
|
var tag = node.tagName.toLowerCase();
|
|
398
420
|
var s = styleOf(node, style);
|
|
@@ -465,8 +487,9 @@ function RTE_Plugin_PdfExport() {
|
|
|
465
487
|
continue;
|
|
466
488
|
}
|
|
467
489
|
if (isBlockTag(tag)) {
|
|
468
|
-
|
|
469
|
-
|
|
490
|
+
var own = (tag === "blockquote" ? self.baseSize * 1.5 : 0) + cssIndentPt(c);
|
|
491
|
+
if (hasBlockChildren(c)) { walk(c, styleOf(c, style), indent + own); continue; }
|
|
492
|
+
block(c, style, indent + own, null);
|
|
470
493
|
continue;
|
|
471
494
|
}
|
|
472
495
|
// Inline content sitting directly under a container.
|
|
@@ -146,7 +146,9 @@ function RTE_Plugin_WordExport() {
|
|
|
146
146
|
"table { border-collapse: collapse; }\r\n" +
|
|
147
147
|
"td, th { border: 1px solid #999; padding: 4px 8px; }\r\n" +
|
|
148
148
|
"img { max-width: 100%; height: auto; }\r\n" +
|
|
149
|
-
|
|
149
|
+
// margin-left was zeroed here, which also flattened any indentation
|
|
150
|
+
// expressed as a margin. Keep the quote bar, keep the offset.
|
|
151
|
+
"blockquote { border-left: 3px solid #ccc; padding-left: 12px; color: #444; }\r\n" +
|
|
150
152
|
"pre { font-family: Consolas, 'Courier New', monospace; background: #f4f4f4; padding: 8px; }\r\n" +
|
|
151
153
|
"</style>\r\n" +
|
|
152
154
|
"</head>\r\n" +
|