caracal 1.0.9 → 1.0.10

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 0d2efc2b19a3ae0483703f88497fb42bbf876f9e
4
- data.tar.gz: 2dcd3fb0074ddc334878eb00339dc27ad7ec30aa
3
+ metadata.gz: 8ba1b2ffe0fd04fdee4a28a8697b6862ab972630
4
+ data.tar.gz: 8a1c979775800f0eddef47ea163c5e6baf8b2116
5
5
  SHA512:
6
- metadata.gz: 5539dbe580138147f1c3c036d2147790085178a4fd2b60dfb54e0b545fab050f52a1c10bf5ff591354c6d14712ce8ff9f8f5a31254adee2a60abf4b781be59c5
7
- data.tar.gz: 39f1c998d281aaa798f52310c4346721e1e39e81d1c4821ad99c6d7026603a27a0c3bba8d9804a45f6977404907de3badd9b1d57c674781e458ca659405411b2
6
+ metadata.gz: 0575246a9d772fb51aa053d1a4539b3e28bc74b52f279fb99a2eab242a044d5422996a6ad1ff8068ecafc7ec340b16a0f3b71985599c1bfda026a40b7bb31815
7
+ data.tar.gz: 7e3f5804fad3bdd679079c01986a39b24a23e84a3b2e81a426be38b0b80837a2c3da94743dab2e9f04adbe49ad1e7aed0c2f7b1da272a57baea8e3c0d3a1b045
data/.gitignore CHANGED
@@ -9,6 +9,7 @@ _yardoc
9
9
  coverage
10
10
  doc/
11
11
  lib/bundler/man
12
+ notes/
12
13
  pkg
13
14
  rdoc
14
15
  spec/reports
data/README.md CHANGED
@@ -678,6 +678,23 @@ end
678
678
  ```
679
679
 
680
680
 
681
+ ## Experimental Features
682
+
683
+ ### IFrames
684
+
685
+ You can include an external Word document into your working Caracal document by specifying a URL or by supplying the data directly.
686
+
687
+ *It should be noted that the metaphor here is imperfect. Caracal fully includes the external file at the time of insertion. Further changes to the external file will not be reflected in your Caracal output.*
688
+
689
+ ```ruby
690
+ # this example loads the file from the internet
691
+ docx.iframe url: 'http://www.some-website.org/snippet.docx'
692
+
693
+ # this example loads the data directly
694
+ docx.iframe data: File.read('my/path/to/snippet.docx')
695
+
696
+ ```
697
+
681
698
  ## Template Rendering
682
699
 
683
700
  Caracal includes [Tilt](https://github.com/rtomayko/tilt) integration to facilitate its inclusion in other frameworks.
@@ -690,11 +707,13 @@ Rails integration can be added via the [Caracal-Rails](https://github.com/trade-
690
707
  Caracal was written for and tested against Word 2010, 2013, and Office365. It should also open in LibreOffice
691
708
  with high fidelity.
692
709
 
710
+
693
711
  ### Older Versions
694
712
  If you are using a version of Word that predates 2010, Caracal may or may not work for you. (Probably it won't.)
695
713
  We don't ever plan to support versions before 2010, but if you choose to embark on that endeavor, we'd be
696
714
  happy to answer questions and provide what guidance we can. We just won't write any code in that direction.
697
715
 
716
+
698
717
  ### Newer Versions
699
718
 
700
719
  For those using reasonably current versions of Word, please consider the following:
@@ -709,6 +728,7 @@ made a mistake in your document's syntax or have an environment-specific, non-ca
709
728
  - If you do see the same behavior in the example project, you've probably uncovered a variance in the way
710
729
  your particular permutation of Windows/Word interprets the OOXML.
711
730
 
731
+
712
732
  ### How to Work on a Problem
713
733
 
714
734
  Caracal is essentially an exercise in reverse engineering OOXML output. When developing features, we
data/lib/caracal.rb CHANGED
@@ -18,8 +18,17 @@ require 'caracal/document'
18
18
  # Extra Setup
19
19
  #------------------------------------------------
20
20
 
21
- # Add functions to table cell model. we do this here to
22
- # avoid a circular require between Caracal::Core::Tables
21
+ # Convenience method for finding root directory.
22
+ #
23
+ module Caracal
24
+ def self.root
25
+ File.dirname __dir__
26
+ end
27
+ end
28
+
29
+
30
+ # Add functions to table cell model. we do this here to
31
+ # avoid a circular require between Caracal::Core::Tables
23
32
  # and Caracal::Core::Models::TableCellModel.
24
33
  #
25
34
  Caracal::Core::Models::TableCellModel.class_eval do
@@ -4,8 +4,8 @@ require 'caracal/errors'
4
4
 
5
5
  module Caracal
6
6
  module Core
7
-
8
- # This module encapsulates all the functionality related to setting the
7
+
8
+ # This module encapsulates all the functionality related to setting the
9
9
  # document's custom properties.
10
10
  #
11
11
  module CustomProperties
@@ -26,14 +26,16 @@ module Caracal
26
26
  model
27
27
  end
28
28
 
29
+
29
30
  #============== GETTERS =============================
30
-
31
+
31
32
  def custom_props
32
33
  @custom_props ||= []
33
34
  end
34
35
 
36
+
35
37
  #============== REGISTRATION ========================
36
-
38
+
37
39
  def register_property(model)
38
40
  custom_props << model
39
41
  model
@@ -42,6 +44,6 @@ module Caracal
42
44
  end
43
45
  end
44
46
  end
45
-
47
+
46
48
  end
47
- end
49
+ end
@@ -0,0 +1,42 @@
1
+ require 'caracal/core/models/iframe_model'
2
+ require 'caracal/errors'
3
+
4
+
5
+ module Caracal
6
+ module Core
7
+
8
+ # This module encapsulates all the functionality related to inserting
9
+ # word document snippets into the document.
10
+ #
11
+ module IFrames
12
+ def self.included(base)
13
+ base.class_eval do
14
+
15
+ #-------------------------------------------------------------
16
+ # Public Methods
17
+ #-------------------------------------------------------------
18
+
19
+ def iframe(options={}, &block)
20
+ model = Caracal::Core::Models::IFrameModel.new(options, &block)
21
+ if model.valid?
22
+ model.preprocess!
23
+ model.namespaces.each do |(prefix, href)|
24
+ namespace({ prefix: prefix, href: href })
25
+ end
26
+ model.ignorables.each do |prefix|
27
+ ignorable(prefix)
28
+ end
29
+
30
+ contents << model
31
+ else
32
+ raise Caracal::Errors::InvalidModelError, 'IFrameModel requires either the :url or :data argument.'
33
+ end
34
+ model
35
+ end
36
+
37
+ end
38
+ end
39
+ end
40
+
41
+ end
42
+ end
@@ -0,0 +1,47 @@
1
+ module Caracal
2
+ module Core
3
+
4
+ # This module encapsulates all the functionality related to registering and
5
+ # retrieving ignorable namespaces.
6
+ #
7
+ module Ignorables
8
+ def self.included(base)
9
+ base.class_eval do
10
+
11
+ #-------------------------------------------------------------
12
+ # Public Methods
13
+ #-------------------------------------------------------------
14
+
15
+ #============== ATTRIBUTES ==========================
16
+
17
+ def ignorable(prefix)
18
+ register_ignorable(prefix)
19
+ end
20
+
21
+
22
+ #============== GETTERS =============================
23
+
24
+ def ignorables
25
+ @ignorables ||= []
26
+ end
27
+
28
+
29
+ #============== REGISTRATION ========================
30
+
31
+ def register_ignorable(prefix)
32
+ unless ignorables.include?(prefix)
33
+ ignorables << prefix
34
+ prefix
35
+ end
36
+ end
37
+
38
+ def unregister_ignorable(prefix)
39
+ ignorables.delete(prefix)
40
+ end
41
+
42
+ end
43
+ end
44
+ end
45
+
46
+ end
47
+ end
@@ -0,0 +1,148 @@
1
+ require 'caracal/core/models/base_model'
2
+
3
+ module Caracal
4
+ module Core
5
+ module Models
6
+
7
+ # This class handles block options passed to the img method.
8
+ #
9
+ class IFrameModel < BaseModel
10
+
11
+ #--------------------------------------------------
12
+ # Configuration
13
+ #--------------------------------------------------
14
+
15
+ # accessors
16
+ attr_reader :iframe_url
17
+ attr_reader :iframe_data
18
+ attr_reader :iframe_ignorables
19
+ attr_reader :iframe_namespaces
20
+ attr_reader :iframe_relationships
21
+
22
+ # initialization
23
+ def initialize(options={}, &block)
24
+ super options, &block
25
+ end
26
+
27
+
28
+ #--------------------------------------------------
29
+ # Public Methods
30
+ #--------------------------------------------------
31
+
32
+ #=============== PROCESSING =======================
33
+
34
+ def preprocess!
35
+ ::Zip::File.open(file) do |zip|
36
+ # locate relationships xml
37
+ entry = zip.glob('word/_rels/document.xml.rels').first
38
+ content = entry.get_input_stream.read
39
+ rel_xml = Nokogiri::XML(content)
40
+
41
+ # locate document xml
42
+ entry = zip.glob('word/document.xml').first
43
+ content = entry.get_input_stream.read
44
+ doc_xml = Nokogiri::XML(content)
45
+
46
+ # master nodesets
47
+ rel_nodes = rel_xml.children.first.children
48
+ doc_root = doc_xml.at_xpath('//w:document')
49
+ pic_nodes = doc_xml.xpath('//pic:pic', { pic: 'http://schemas.openxmlformats.org/drawingml/2006/picture' })
50
+
51
+ # namespaces
52
+ @iframe_namespaces = doc_root.namespaces
53
+
54
+ # ignorable namespaces
55
+ if a = doc_root.attributes['Ignorable']
56
+ @iframe_ignorables = a.value.split(/\s+/)
57
+ end
58
+
59
+ # relationships
60
+ media_map = rel_nodes.reduce({}) do |hash, node|
61
+ type = node.at_xpath('@Type').value
62
+ if type.slice(-5, 5) == 'image'
63
+ id = node.at_xpath('@Id').value
64
+ path = "word/#{ node.at_xpath('@Target').value }"
65
+ hash[id] = path
66
+ end
67
+ hash
68
+ end
69
+ @iframe_relationships = pic_nodes.reduce([]) do |array, node|
70
+ r_node = node.children[1].children[0]
71
+ r_id = r_node.attributes['embed'].value.to_s
72
+ r_media = media_map[r_id]
73
+
74
+ p_node = node.children[0].children[0]
75
+ p_id = p_node.attributes['id'].to_s.to_i
76
+ p_name = p_node.attributes['name'].to_s
77
+ p_data = zip.glob(r_media).first.get_input_stream.read
78
+
79
+ # register relationship
80
+ array << { id: r_id, type: 'image', target: p_name, data: p_data }
81
+ array
82
+ end
83
+ end
84
+ end
85
+
86
+
87
+ #=============== GETTERS ==========================
88
+
89
+ def file
90
+ @file ||= begin
91
+ if iframe_url.nil?
92
+ file = File.new('tmp_caracal', 'w+')
93
+ file.print iframe_data
94
+ file.rewind
95
+ else
96
+ file = open(iframe_url)
97
+ end
98
+ file
99
+ end
100
+ end
101
+
102
+ def ignorables
103
+ @iframe_ignorables || []
104
+ end
105
+
106
+ def namespaces
107
+ @iframe_namespaces || {}
108
+ end
109
+
110
+ def relationships
111
+ @iframe_relationships || []
112
+ end
113
+
114
+
115
+ #=============== SETTERS ==========================
116
+
117
+ # strings
118
+ [:data, :url].each do |m|
119
+ define_method "#{ m }" do |value|
120
+ instance_variable_set("@iframe_#{ m }", value.to_s)
121
+ end
122
+ end
123
+
124
+
125
+ #=============== VALIDATION =======================
126
+
127
+ def valid?
128
+ vals = option_keys.map { |m| send("iframe_#{ m }") }.compact
129
+ vals = vals.reject { |v| v.size == 0 }
130
+ vals.size > 0
131
+ end
132
+
133
+
134
+
135
+ #--------------------------------------------------
136
+ # Private Methods
137
+ #--------------------------------------------------
138
+ private
139
+
140
+ def option_keys
141
+ [:url, :data]
142
+ end
143
+
144
+ end
145
+
146
+ end
147
+ end
148
+ end
@@ -0,0 +1,65 @@
1
+ require 'caracal/core/models/base_model'
2
+
3
+
4
+ module Caracal
5
+ module Core
6
+ module Models
7
+
8
+ # This class encapsulates the logic needed to store and manipulate
9
+ # namespace data.
10
+ #
11
+ class NamespaceModel < BaseModel
12
+
13
+ #-------------------------------------------------------------
14
+ # Configuration
15
+ #-------------------------------------------------------------
16
+
17
+ # accessors
18
+ attr_reader :namespace_prefix
19
+ attr_reader :namespace_href
20
+
21
+
22
+
23
+ #-------------------------------------------------------------
24
+ # Public Instance Methods
25
+ #-------------------------------------------------------------
26
+
27
+ #=================== SETTERS =============================
28
+
29
+ # strings
30
+ [:href, :prefix].each do |m|
31
+ define_method "#{ m }" do |value|
32
+ instance_variable_set("@namespace_#{ m }", value.to_s)
33
+ end
34
+ end
35
+
36
+
37
+ #=================== STATE ===============================
38
+
39
+ def matches?(str)
40
+ namespace_prefix == str.to_s
41
+ end
42
+
43
+
44
+ #=============== VALIDATION ===========================
45
+
46
+ def valid?
47
+ required = [:href, :prefix]
48
+ required.all? { |m| !send("namespace_#{ m }").nil? }
49
+ end
50
+
51
+
52
+ #-------------------------------------------------------------
53
+ # Private Instance Methods
54
+ #-------------------------------------------------------------
55
+ private
56
+
57
+ def option_keys
58
+ [:prefix, :href]
59
+ end
60
+
61
+ end
62
+
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,89 @@
1
+ require 'caracal/core/models/namespace_model'
2
+ require 'caracal/errors'
3
+
4
+
5
+ module Caracal
6
+ module Core
7
+
8
+ # This module encapsulates all the functionality related to registering and
9
+ # retrieving namespaces.
10
+ #
11
+ module Namespaces
12
+ def self.included(base)
13
+ base.class_eval do
14
+
15
+ #-------------------------------------------------------------
16
+ # Class Methods
17
+ #-------------------------------------------------------------
18
+
19
+ def self.default_namespaces
20
+ [
21
+ { prefix: 'xmlns:mc', href: 'http://schemas.openxmlformats.org/markup-compatibility/2006' },
22
+ { prefix: 'xmlns:o', href: 'urn:schemas-microsoft-com:office:office' },
23
+ { prefix: 'xmlns:r', href: 'http://schemas.openxmlformats.org/officeDocument/2006/relationships' },
24
+ { prefix: 'xmlns:m', href: 'http://schemas.openxmlformats.org/officeDocument/2006/math' },
25
+ { prefix: 'xmlns:v', href: 'urn:schemas-microsoft-com:vml' },
26
+ { prefix: 'xmlns:wp', href: 'http://schemas.openxmlformats.org/drawingml/2006/wordprocessingDrawing' },
27
+ { prefix: 'xmlns:w10', href: 'urn:schemas-microsoft-com:office:word' },
28
+ { prefix: 'xmlns:w', href: 'http://schemas.openxmlformats.org/wordprocessingml/2006/main' },
29
+ { prefix: 'xmlns:wne', href: 'http://schemas.microsoft.com/office/word/2006/wordml' },
30
+ { prefix: 'xmlns:sl', href: 'http://schemas.openxmlformats.org/schemaLibrary/2006/main' },
31
+ { prefix: 'xmlns:a', href: 'http://schemas.openxmlformats.org/drawingml/2006/main' },
32
+ { prefix: 'xmlns:pic', href: 'http://schemas.openxmlformats.org/drawingml/2006/picture' },
33
+ { prefix: 'xmlns:c', href: 'http://schemas.openxmlformats.org/drawingml/2006/chart' },
34
+ { prefix: 'xmlns:lc', href: 'http://schemas.openxmlformats.org/drawingml/2006/lockedCanvas' },
35
+ { prefix: 'xmlns:dgm', href: 'http://schemas.openxmlformats.org/drawingml/2006/diagram' }
36
+ ]
37
+ end
38
+
39
+
40
+ #-------------------------------------------------------------
41
+ # Public Methods
42
+ #-------------------------------------------------------------
43
+
44
+ #============== ATTRIBUTES ==========================
45
+
46
+ def namespace(options={}, &block)
47
+ model = Caracal::Core::Models::NamespaceModel.new(options, &block)
48
+ if model.valid?
49
+ ns = register_namespace(model)
50
+ else
51
+ raise Caracal::Errors::InvalidModelError, 'namespace must specify the :prefix and :href attributes.'
52
+ end
53
+ ns
54
+ end
55
+
56
+
57
+ #============== GETTERS =============================
58
+
59
+ def namespaces
60
+ @namespaces ||= []
61
+ end
62
+
63
+ def find_namespace(prefix)
64
+ namespaces.find { |ns| ns.matches?(prefix) }
65
+ end
66
+
67
+
68
+ #============== REGISTRATION ========================
69
+
70
+ def register_namespace(model)
71
+ unless ns = find_namespace(model.namespace_prefix)
72
+ namespaces << model
73
+ ns = model
74
+ end
75
+ ns
76
+ end
77
+
78
+ def unregister_namespace(prefix)
79
+ if ns = find_namespace(prefix)
80
+ namespaces.delete(ns)
81
+ end
82
+ end
83
+
84
+ end
85
+ end
86
+ end
87
+
88
+ end
89
+ end