uniword 1.7.2 → 1.7.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.
- checksums.yaml +4 -4
- data/lib/uniword/converter/plain_text.rb +33 -34
- data/lib/uniword/omml_integration.rb +62 -0
- data/lib/uniword/version.rb +1 -1
- data/lib/uniword.rb +10 -0
- metadata +7 -6
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: a100b8d47c3610f0e1443e481b3baf95697bf7da3275c3157b8e148faa01fbab
|
|
4
|
+
data.tar.gz: 88054dad6bf458eccfce74a58350d6afe839515847cbed377e762669dde0bbfb
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 6fd9d522d692c981deb2cde0aa56e654f19a086ce4e8a359f379bbb102243c90464998ffd6e5917a0a3f246cba8a595b65f937547a1a274f3ad1dca6e6a269d2
|
|
7
|
+
data.tar.gz: ae2d601a492a258b692726330a9f7b7d3e67142571e74377d3ae5c383e51919d2d45c03273dd90aef9af12d3f5f46ddcb84d1cd768f3066e3ee4717b7c2a10d9
|
|
@@ -27,6 +27,8 @@ module Uniword
|
|
|
27
27
|
# * tables with a header row
|
|
28
28
|
# * links (bridge resolves relationship ids to target URLs)
|
|
29
29
|
# * bookmarks (heading/paragraph anchor id propagation)
|
|
30
|
+
# * footnotes (coradoc-docx 0.1.6 carries them into the core
|
|
31
|
+
# model; to md/adoc emits GFM [^id] + definitions)
|
|
30
32
|
#
|
|
31
33
|
# Dropped or degraded (recorded on PlainTextResult, warned about;
|
|
32
34
|
# +strict: true+ raises FidelityError):
|
|
@@ -36,18 +38,18 @@ module Uniword
|
|
|
36
38
|
# * tracked changes (w:ins/w:del)
|
|
37
39
|
# * structured document tags (content survives, wrapper unwrapped)
|
|
38
40
|
# * math (OMML)
|
|
39
|
-
# * images (docx extractor does not carry drawings)
|
|
40
|
-
# * footnotes (coradoc-docx 0.1.4 cannot read the current Footnotes
|
|
41
|
-
# collection; the bridge sets it aside for the conversion)
|
|
41
|
+
# * images (docx extractor does not carry drawings; coradoc#322)
|
|
42
42
|
#
|
|
43
43
|
# Never carried by any plain-text format (not detected per document):
|
|
44
44
|
# themes, settings, font table, and other package-level parts.
|
|
45
45
|
#
|
|
46
46
|
# From the plain-text side, links written to DOCX resolve to real
|
|
47
|
-
# External relationships on save.
|
|
47
|
+
# External relationships on save. Upstream flattenings that remain:
|
|
48
48
|
# coradoc-markdown's CoreModel transform emits inline Markdown links
|
|
49
49
|
# as literal text, so from_markdown keeps links as text while
|
|
50
|
-
# from_asciidoc builds real hyperlinks
|
|
50
|
+
# from_asciidoc builds real hyperlinks (coradoc#320); the same
|
|
51
|
+
# inline parser gap leaves from_markdown footnote REFERENCES as
|
|
52
|
+
# literal text (definitions do convert, coradoc#320).
|
|
51
53
|
#
|
|
52
54
|
# @example Document to Markdown
|
|
53
55
|
# md = Uniword::Converter::PlainText.new.to_markdown(document)
|
|
@@ -136,9 +138,28 @@ module Uniword
|
|
|
136
138
|
core = with_conversion_guards(document, drops) do
|
|
137
139
|
::Coradoc::Docx.parse_to_core(document)
|
|
138
140
|
end
|
|
141
|
+
adoc_model = ::Coradoc::AsciiDoc::Transform::FromCoreModel
|
|
142
|
+
.transform(core)
|
|
143
|
+
normalize_emphasis_constraint!(adoc_model)
|
|
139
144
|
build_result(format: :asciidoc, direction: :docx_to_asciidoc,
|
|
140
|
-
core: core, drops: drops, text:
|
|
141
|
-
|
|
145
|
+
core: core, drops: drops, text: adoc_model.to_adoc)
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
# CoreModel carries no emphasis constraint, and the framework's
|
|
149
|
+
# flank guess can downgrade boundary-flanked runs to the
|
|
150
|
+
# constrained spelling, which does not reparse mid-word. The
|
|
151
|
+
# DOCX contract emits the unconstrained forms (`**bold**`,
|
|
152
|
+
# `__italic__`) — they parse unconditionally — so the
|
|
153
|
+
# converter restores the emphasis models' own unconstrained
|
|
154
|
+
# default after the transform.
|
|
155
|
+
def normalize_emphasis_constraint!(model)
|
|
156
|
+
model.visit do |element, _phase|
|
|
157
|
+
element.unconstrained = true if element.is_a?(
|
|
158
|
+
::Coradoc::AsciiDoc::Model::Inline::Bold,
|
|
159
|
+
) || element.is_a?(::Coradoc::AsciiDoc::Model::Inline::Italic)
|
|
160
|
+
element
|
|
161
|
+
end
|
|
162
|
+
model
|
|
142
163
|
end
|
|
143
164
|
|
|
144
165
|
# Parse AsciiDoc into a document model.
|
|
@@ -194,7 +215,6 @@ module Uniword
|
|
|
194
215
|
end
|
|
195
216
|
features << "math (OMML)" if any_paragraph?(document) { |p| math?(p) }
|
|
196
217
|
features << "images" if any_paragraph?(document) { |p| drawing?(p) }
|
|
197
|
-
features << "footnotes" if user_footnotes?(document)
|
|
198
218
|
features
|
|
199
219
|
end
|
|
200
220
|
|
|
@@ -204,23 +224,12 @@ module Uniword
|
|
|
204
224
|
raise FidelityError.new(format, drops)
|
|
205
225
|
end
|
|
206
226
|
|
|
207
|
-
#
|
|
208
|
-
#
|
|
209
|
-
#
|
|
210
|
-
|
|
211
|
-
def with_conversion_guards(document, drops)
|
|
212
|
-
stash = {}
|
|
213
|
-
if drops.include?("footnotes")
|
|
214
|
-
stash[:footnotes] = document.footnotes
|
|
215
|
-
document.footnotes = nil
|
|
216
|
-
end
|
|
227
|
+
# Rewrite hyperlink rIds to their target URLs so coradoc's
|
|
228
|
+
# writers emit real links; every mutation is restored after the
|
|
229
|
+
# conversion.
|
|
230
|
+
def with_conversion_guards(document, _drops)
|
|
217
231
|
rewrites = resolve_link_targets(document)
|
|
218
|
-
|
|
219
|
-
yield
|
|
220
|
-
ensure
|
|
221
|
-
restore_link_targets(rewrites)
|
|
222
|
-
document.footnotes = stash[:footnotes] if stash.key?(:footnotes)
|
|
223
|
-
end
|
|
232
|
+
yield.tap { restore_link_targets(rewrites) }
|
|
224
233
|
end
|
|
225
234
|
|
|
226
235
|
# Rewrite w:hyperlink r:id attributes to their relationship
|
|
@@ -411,16 +420,6 @@ module Uniword
|
|
|
411
420
|
end
|
|
412
421
|
end
|
|
413
422
|
|
|
414
|
-
def user_footnotes?(document)
|
|
415
|
-
footnotes = document.footnotes
|
|
416
|
-
unless footnotes.is_a?(Uniword::Wordprocessingml::Footnotes)
|
|
417
|
-
return false
|
|
418
|
-
end
|
|
419
|
-
|
|
420
|
-
footnotes.footnote_entries.any? do |entry|
|
|
421
|
-
entry.type != "separator" && entry.type != "continuationSeparator"
|
|
422
|
-
end
|
|
423
|
-
end
|
|
424
423
|
end
|
|
425
424
|
end
|
|
426
425
|
end
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "omml"
|
|
4
|
+
|
|
5
|
+
module Uniword
|
|
6
|
+
# Registers Uniword's WordprocessingML classes as substitutions for
|
|
7
|
+
# Omml::Models' minimal WordprocessingML stubs. When an Omml model
|
|
8
|
+
# (e.g. CTOMath) resolves a WordprocessingML type symbol (e.g.
|
|
9
|
+
# +:ct_br+), the Register's substitution table redirects resolution
|
|
10
|
+
# to the corresponding Uniword class.
|
|
11
|
+
#
|
|
12
|
+
# This keeps a single source of truth for WordprocessingML types:
|
|
13
|
+
# Uniword owns the rich, builder-friendly classes; omml owns the math
|
|
14
|
+
# schema. The two meet through register substitution, not through
|
|
15
|
+
# duplicate model definitions.
|
|
16
|
+
module OmmlIntegration
|
|
17
|
+
SUBSTITUTIONS = {
|
|
18
|
+
"Omml::Models::CTBr" => "Uniword::Wordprocessingml::Break",
|
|
19
|
+
}.freeze
|
|
20
|
+
|
|
21
|
+
class << self
|
|
22
|
+
def register
|
|
23
|
+
ensure_omml_context
|
|
24
|
+
register_substitutions
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
private
|
|
28
|
+
|
|
29
|
+
def ensure_omml_context
|
|
30
|
+
return if Lutaml::Model::GlobalContext.context(:uniword)
|
|
31
|
+
|
|
32
|
+
Omml::Configuration.register_in(:uniword)
|
|
33
|
+
Lutaml::Model::Config.default_register = :uniword
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def register_substitutions
|
|
37
|
+
register_handle = Lutaml::Model::GlobalRegister.lookup(:uniword)
|
|
38
|
+
return unless register_handle
|
|
39
|
+
|
|
40
|
+
SUBSTITUTIONS.each do |omml_class_name, uniword_class_name|
|
|
41
|
+
omml_class = constantize(omml_class_name)
|
|
42
|
+
uniword_class = constantize(uniword_class_name)
|
|
43
|
+
next unless omml_class && uniword_class
|
|
44
|
+
|
|
45
|
+
register_handle.register_global_type_substitution(
|
|
46
|
+
from_type: omml_class,
|
|
47
|
+
to_type: uniword_class,
|
|
48
|
+
)
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def constantize(class_name)
|
|
53
|
+
parts = class_name.split("::")
|
|
54
|
+
parts.reduce(Object) do |scope, part|
|
|
55
|
+
scope.const_get(part)
|
|
56
|
+
end
|
|
57
|
+
rescue NameError
|
|
58
|
+
nil
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
data/lib/uniword/version.rb
CHANGED
data/lib/uniword.rb
CHANGED
|
@@ -47,10 +47,16 @@ module Uniword
|
|
|
47
47
|
# directly, falling back to :default for Uniword's own types. Setting
|
|
48
48
|
# Config.default_register means from_xml/to_xml calls without an explicit
|
|
49
49
|
# register: argument use :uniword automatically.
|
|
50
|
+
#
|
|
51
|
+
# OmmlIntegration (autoloaded below) then substitutes omml's minimal
|
|
52
|
+
# WordprocessingML stubs with Uniword's richer classes via the Register's
|
|
53
|
+
# type substitution mechanism — single source of truth for WordprocessingML.
|
|
50
54
|
require "omml"
|
|
51
55
|
Omml::Configuration.register_in(:uniword)
|
|
52
56
|
Lutaml::Model::Config.default_register = :uniword
|
|
53
57
|
|
|
58
|
+
autoload :OmmlIntegration, "uniword/omml_integration"
|
|
59
|
+
|
|
54
60
|
# Version constant
|
|
55
61
|
autoload :VERSION, "uniword/version"
|
|
56
62
|
autoload :ModelAttributeAccess, "uniword/model_attribute_access"
|
|
@@ -396,4 +402,8 @@ module Uniword
|
|
|
396
402
|
from_html(html).to_file(path)
|
|
397
403
|
end
|
|
398
404
|
end
|
|
405
|
+
|
|
406
|
+
# Register WordprocessingML substitutions after autoloads are declared
|
|
407
|
+
# so OmmlIntegration can resolve Uniword class constants via const_get.
|
|
408
|
+
OmmlIntegration.register
|
|
399
409
|
end
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: uniword
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.7.
|
|
4
|
+
version: 1.7.3
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Ribose Inc.
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: exe
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-11-
|
|
11
|
+
date: 2026-11-06 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: logger
|
|
@@ -30,28 +30,28 @@ dependencies:
|
|
|
30
30
|
requirements:
|
|
31
31
|
- - "~>"
|
|
32
32
|
- !ruby/object:Gem::Version
|
|
33
|
-
version: 1.9.
|
|
33
|
+
version: 1.9.407
|
|
34
34
|
type: :runtime
|
|
35
35
|
prerelease: false
|
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
|
37
37
|
requirements:
|
|
38
38
|
- - "~>"
|
|
39
39
|
- !ruby/object:Gem::Version
|
|
40
|
-
version: 1.9.
|
|
40
|
+
version: 1.9.407
|
|
41
41
|
- !ruby/object:Gem::Dependency
|
|
42
42
|
name: lutaml-model
|
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
|
44
44
|
requirements:
|
|
45
45
|
- - "~>"
|
|
46
46
|
- !ruby/object:Gem::Version
|
|
47
|
-
version: 0.8.
|
|
47
|
+
version: 0.8.169
|
|
48
48
|
type: :runtime
|
|
49
49
|
prerelease: false
|
|
50
50
|
version_requirements: !ruby/object:Gem::Requirement
|
|
51
51
|
requirements:
|
|
52
52
|
- - "~>"
|
|
53
53
|
- !ruby/object:Gem::Version
|
|
54
|
-
version: 0.8.
|
|
54
|
+
version: 0.8.169
|
|
55
55
|
- !ruby/object:Gem::Dependency
|
|
56
56
|
name: omml
|
|
57
57
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -1164,6 +1164,7 @@ files:
|
|
|
1164
1164
|
- lib/uniword/office/top.rb
|
|
1165
1165
|
- lib/uniword/office/writing_style.rb
|
|
1166
1166
|
- lib/uniword/office/zoom.rb
|
|
1167
|
+
- lib/uniword/omml_integration.rb
|
|
1167
1168
|
- lib/uniword/ooxml.rb
|
|
1168
1169
|
- lib/uniword/ooxml/additional_characteristics.rb
|
|
1169
1170
|
- lib/uniword/ooxml/app_properties.rb
|