caracal 0.1.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.
Files changed (95) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +23 -0
  3. data/Gemfile +3 -0
  4. data/LICENSE.txt +22 -0
  5. data/README.md +941 -0
  6. data/Rakefile +2 -0
  7. data/caracal.gemspec +27 -0
  8. data/lib/caracal.rb +31 -0
  9. data/lib/caracal/core/file_name.rb +39 -0
  10. data/lib/caracal/core/fonts.rb +75 -0
  11. data/lib/caracal/core/images.rb +37 -0
  12. data/lib/caracal/core/line_breaks.rb +29 -0
  13. data/lib/caracal/core/list_styles.rb +92 -0
  14. data/lib/caracal/core/lists.rb +57 -0
  15. data/lib/caracal/core/models/base_model.rb +51 -0
  16. data/lib/caracal/core/models/border_model.rb +120 -0
  17. data/lib/caracal/core/models/font_model.rb +64 -0
  18. data/lib/caracal/core/models/image_model.rb +118 -0
  19. data/lib/caracal/core/models/line_break_model.rb +15 -0
  20. data/lib/caracal/core/models/link_model.rb +65 -0
  21. data/lib/caracal/core/models/list_item_model.rb +105 -0
  22. data/lib/caracal/core/models/list_model.rb +130 -0
  23. data/lib/caracal/core/models/list_style_model.rb +129 -0
  24. data/lib/caracal/core/models/margin_model.rb +76 -0
  25. data/lib/caracal/core/models/page_break_model.rb +15 -0
  26. data/lib/caracal/core/models/page_number_model.rb +69 -0
  27. data/lib/caracal/core/models/page_size_model.rb +70 -0
  28. data/lib/caracal/core/models/paragraph_model.rb +141 -0
  29. data/lib/caracal/core/models/relationship_model.rb +108 -0
  30. data/lib/caracal/core/models/rule_model.rb +27 -0
  31. data/lib/caracal/core/models/style_model.rb +134 -0
  32. data/lib/caracal/core/models/table_cell_model.rb +155 -0
  33. data/lib/caracal/core/models/table_model.rb +206 -0
  34. data/lib/caracal/core/models/text_model.rb +92 -0
  35. data/lib/caracal/core/page_breaks.rb +29 -0
  36. data/lib/caracal/core/page_numbers.rb +51 -0
  37. data/lib/caracal/core/page_settings.rb +72 -0
  38. data/lib/caracal/core/relationships.rb +90 -0
  39. data/lib/caracal/core/rules.rb +35 -0
  40. data/lib/caracal/core/styles.rb +86 -0
  41. data/lib/caracal/core/tables.rb +41 -0
  42. data/lib/caracal/core/text.rb +73 -0
  43. data/lib/caracal/document.rb +242 -0
  44. data/lib/caracal/errors.rb +23 -0
  45. data/lib/caracal/renderers/app_renderer.rb +41 -0
  46. data/lib/caracal/renderers/content_types_renderer.rb +53 -0
  47. data/lib/caracal/renderers/core_renderer.rb +44 -0
  48. data/lib/caracal/renderers/document_renderer.rb +349 -0
  49. data/lib/caracal/renderers/fonts_renderer.rb +56 -0
  50. data/lib/caracal/renderers/footer_renderer.rb +69 -0
  51. data/lib/caracal/renderers/numbering_renderer.rb +87 -0
  52. data/lib/caracal/renderers/package_relationships_renderer.rb +50 -0
  53. data/lib/caracal/renderers/relationships_renderer.rb +48 -0
  54. data/lib/caracal/renderers/settings_renderer.rb +58 -0
  55. data/lib/caracal/renderers/styles_renderer.rb +163 -0
  56. data/lib/caracal/renderers/xml_renderer.rb +83 -0
  57. data/lib/caracal/version.rb +3 -0
  58. data/lib/tilt/caracal.rb +21 -0
  59. data/spec/lib/caracal/core/file_name_spec.rb +54 -0
  60. data/spec/lib/caracal/core/fonts_spec.rb +119 -0
  61. data/spec/lib/caracal/core/images_spec.rb +25 -0
  62. data/spec/lib/caracal/core/line_breaks_spec.rb +25 -0
  63. data/spec/lib/caracal/core/list_styles_spec.rb +121 -0
  64. data/spec/lib/caracal/core/lists_spec.rb +43 -0
  65. data/spec/lib/caracal/core/models/base_model_spec.rb +38 -0
  66. data/spec/lib/caracal/core/models/border_model_spec.rb +159 -0
  67. data/spec/lib/caracal/core/models/font_model_spec.rb +92 -0
  68. data/spec/lib/caracal/core/models/image_model_spec.rb +192 -0
  69. data/spec/lib/caracal/core/models/line_break_model_spec.rb +21 -0
  70. data/spec/lib/caracal/core/models/link_model_spec.rb +139 -0
  71. data/spec/lib/caracal/core/models/list_item_model_spec.rb +190 -0
  72. data/spec/lib/caracal/core/models/list_model_spec.rb +178 -0
  73. data/spec/lib/caracal/core/models/list_style_model_spec.rb +212 -0
  74. data/spec/lib/caracal/core/models/margin_model_spec.rb +111 -0
  75. data/spec/lib/caracal/core/models/page_break_model_spec.rb +21 -0
  76. data/spec/lib/caracal/core/models/page_number_model_spec.rb +101 -0
  77. data/spec/lib/caracal/core/models/page_size_model_spec.rb +91 -0
  78. data/spec/lib/caracal/core/models/paragraph_model_spec.rb +162 -0
  79. data/spec/lib/caracal/core/models/relationship_model_spec.rb +183 -0
  80. data/spec/lib/caracal/core/models/rule_model_spec.rb +108 -0
  81. data/spec/lib/caracal/core/models/style_model_spec.rb +187 -0
  82. data/spec/lib/caracal/core/models/table_cell_model_spec.rb +221 -0
  83. data/spec/lib/caracal/core/models/table_model_spec.rb +222 -0
  84. data/spec/lib/caracal/core/models/text_model_spec.rb +132 -0
  85. data/spec/lib/caracal/core/page_breaks_spec.rb +25 -0
  86. data/spec/lib/caracal/core/page_numbers_spec.rb +80 -0
  87. data/spec/lib/caracal/core/page_settings_spec.rb +143 -0
  88. data/spec/lib/caracal/core/relationships_spec.rb +119 -0
  89. data/spec/lib/caracal/core/rules_spec.rb +25 -0
  90. data/spec/lib/caracal/core/styles_spec.rb +129 -0
  91. data/spec/lib/caracal/core/tables_spec.rb +25 -0
  92. data/spec/lib/caracal/core/text_spec.rb +52 -0
  93. data/spec/lib/caracal/errors_spec.rb +10 -0
  94. data/spec/spec_helper.rb +8 -0
  95. metadata +245 -0
@@ -0,0 +1,141 @@
1
+ require 'caracal/core/models/base_model'
2
+ require 'caracal/core/models/link_model'
3
+ require 'caracal/core/models/text_model'
4
+ require 'caracal/errors'
5
+
6
+
7
+ module Caracal
8
+ module Core
9
+ module Models
10
+
11
+ # This class encapsulates the logic needed to store and manipulate
12
+ # paragraph data.
13
+ #
14
+ class ParagraphModel < BaseModel
15
+
16
+ #-------------------------------------------------------------
17
+ # Configuration
18
+ #-------------------------------------------------------------
19
+
20
+ # readers
21
+ attr_reader :paragraph_style
22
+ attr_reader :paragraph_align
23
+ attr_reader :paragraph_color
24
+ attr_reader :paragraph_size
25
+ attr_reader :paragraph_bold
26
+ attr_reader :paragraph_italic
27
+ attr_reader :paragraph_underline
28
+
29
+ # initialization
30
+ def initialize(**options, &block)
31
+ if content = options.delete(:content)
32
+ text content, options.dup
33
+ end
34
+
35
+ super options, &block
36
+ end
37
+
38
+
39
+ #-------------------------------------------------------------
40
+ # Public Instance Methods
41
+ #-------------------------------------------------------------
42
+
43
+ #=============== GETTERS ==============================
44
+
45
+ # .runs
46
+ def runs
47
+ @runs ||= []
48
+ end
49
+
50
+ # .run_attributes
51
+ def run_attributes
52
+ {
53
+ color: paragraph_color,
54
+ size: paragraph_size,
55
+ bold: paragraph_bold,
56
+ italic: paragraph_italic,
57
+ underline: paragraph_underline,
58
+ }
59
+ end
60
+
61
+
62
+ #=============== SETTERS ==============================
63
+
64
+ # booleans
65
+ [:bold, :italic, :underline].each do |m|
66
+ define_method "#{ m }" do |value|
67
+ instance_variable_set("@paragraph_#{ m }", !!value)
68
+ end
69
+ end
70
+
71
+ # integers
72
+ [:size].each do |m|
73
+ define_method "#{ m }" do |value|
74
+ instance_variable_set("@paragraph_#{ m }", value.to_i)
75
+ end
76
+ end
77
+
78
+ # strings
79
+ [:color, :style].each do |m|
80
+ define_method "#{ m }" do |value|
81
+ instance_variable_set("@paragraph_#{ m }", value.to_s)
82
+ end
83
+ end
84
+
85
+ # symbols
86
+ [:align].each do |m|
87
+ define_method "#{ m }" do |value|
88
+ instance_variable_set("@paragraph_#{ m }", value.to_s.to_sym)
89
+ end
90
+ end
91
+
92
+
93
+ #=============== SUB-METHODS ===========================
94
+
95
+ # .link
96
+ def link(*args, **options, &block)
97
+ options.merge!({ content: args[0] }) unless args[0].nil?
98
+ options.merge!({ href: args[1] }) unless args[1].nil?
99
+
100
+ model = Caracal::Core::Models::LinkModel.new(options, &block)
101
+ if model.valid?
102
+ runs << model
103
+ else
104
+ raise Caracal::Errors::InvalidModelError, ':link method must receive strings for the display text and the external href.'
105
+ end
106
+ end
107
+
108
+ # .text
109
+ def text(*args, **options, &block)
110
+ options.merge!({ content: args[0] }) unless args[0].nil?
111
+
112
+ model = Caracal::Core::Models::TextModel.new(options, &block)
113
+ if model.valid?
114
+ runs << model
115
+ else
116
+ raise Caracal::Errors::InvalidModelError, ':text method must receive a string for the display text.'
117
+ end
118
+ end
119
+
120
+
121
+ #=============== VALIDATION ===========================
122
+
123
+ def valid?
124
+ runs.size > 0
125
+ end
126
+
127
+
128
+ #-------------------------------------------------------------
129
+ # Private Instance Methods
130
+ #-------------------------------------------------------------
131
+ private
132
+
133
+ def option_keys
134
+ [:content, :style, :align, :color, :size, :bold, :italic, :underline]
135
+ end
136
+
137
+ end
138
+
139
+ end
140
+ end
141
+ end
@@ -0,0 +1,108 @@
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
+ # relationship data.
10
+ #
11
+ class RelationshipModel < BaseModel
12
+
13
+ #-------------------------------------------------------------
14
+ # Configuration
15
+ #-------------------------------------------------------------
16
+
17
+ # constants
18
+ TYPE_MAP = {
19
+ font: 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/fontTable',
20
+ footer: 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/footer',
21
+ image: 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/image',
22
+ link: 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink',
23
+ numbering: 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/numbering',
24
+ setting: 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/settings',
25
+ style: 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles'
26
+ }
27
+
28
+ # accessors
29
+ attr_reader :relationship_id
30
+ attr_reader :relationship_type
31
+ attr_reader :relationship_target
32
+ attr_reader :relationship_key
33
+
34
+
35
+
36
+ #-------------------------------------------------------------
37
+ # Public Instance Methods
38
+ #-------------------------------------------------------------
39
+
40
+ #=================== GETTERS =============================
41
+
42
+ def formatted_id
43
+ "rId#{ relationship_id }"
44
+ end
45
+
46
+ def formatted_target
47
+ if relationship_type == :image
48
+ ext = relationship_target.to_s.split('.').last
49
+ "media/image#{ relationship_id }.#{ ext }"
50
+ else
51
+ relationship_target
52
+ end
53
+ end
54
+
55
+ def formatted_type
56
+ TYPE_MAP.fetch(relationship_type)
57
+ end
58
+
59
+
60
+ #=================== SETTERS =============================
61
+
62
+ def id(value)
63
+ @relationship_id = value.to_i
64
+ end
65
+
66
+ def target(value)
67
+ @relationship_target = value.to_s
68
+ @relationship_key = value.to_s.downcase
69
+ end
70
+
71
+ def type(value)
72
+ @relationship_type = value.to_s.downcase.to_sym
73
+ end
74
+
75
+
76
+ #=================== STATE ===============================
77
+
78
+ def matches?(str)
79
+ relationship_key.downcase == str.to_s.downcase
80
+ end
81
+
82
+ def target_mode?
83
+ relationship_type == :link
84
+ end
85
+
86
+
87
+ #=============== VALIDATION ===========================
88
+
89
+ def valid?
90
+ required = [:id, :target, :type]
91
+ required.all? { |m| !send("relationship_#{ m }").nil? }
92
+ end
93
+
94
+
95
+ #-------------------------------------------------------------
96
+ # Private Instance Methods
97
+ #-------------------------------------------------------------
98
+ private
99
+
100
+ def option_keys
101
+ [:id, :type, :target]
102
+ end
103
+
104
+ end
105
+
106
+ end
107
+ end
108
+ end
@@ -0,0 +1,27 @@
1
+ require 'caracal/core/models/border_model'
2
+
3
+
4
+ module Caracal
5
+ module Core
6
+ module Models
7
+
8
+ # This class handles block options passed to the page margins
9
+ # method.
10
+ #
11
+ class RuleModel < BorderModel
12
+
13
+ #-------------------------------------------------------------
14
+ # Configuration
15
+ #-------------------------------------------------------------
16
+
17
+ # aliases
18
+ alias_method :rule_color, :border_color
19
+ alias_method :rule_size, :border_size
20
+ alias_method :rule_spacing, :border_spacing
21
+ alias_method :rule_line, :border_line
22
+
23
+ end
24
+
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,134 @@
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
+ # paragraph style data.
10
+ #
11
+ class StyleModel < BaseModel
12
+
13
+ #-------------------------------------------------------------
14
+ # Configuration
15
+ #-------------------------------------------------------------
16
+
17
+ # constants
18
+ const_set(:DEFAULT_STYLE_COLOR, '333333')
19
+ const_set(:DEFAULT_STYLE_SIZE, 20)
20
+ const_set(:DEFAULT_STYLE_BOLD, false)
21
+ const_set(:DEFAULT_STYLE_ITALIC, false)
22
+ const_set(:DEFAULT_STYLE_UNDERLINE, false)
23
+ const_set(:DEFAULT_STYLE_ALIGN, :left)
24
+ const_set(:DEFAULT_STYLE_LINE, 360) # 0.25in in twips
25
+ const_set(:DEFAULT_STYLE_TOP, 0) # 0.0in in twips
26
+ const_set(:DEFAULT_STYLE_BOTTOM, 0) # 0.0in in twips
27
+ const_set(:DEFAULT_STYLE_BASE, 'Normal')
28
+ const_set(:DEFAULT_STYLE_NEXT, 'Normal')
29
+
30
+ # accessors
31
+ attr_reader :style_default
32
+ attr_reader :style_id
33
+ attr_reader :style_name
34
+ attr_reader :style_color
35
+ attr_reader :style_font
36
+ attr_reader :style_size
37
+ attr_reader :style_bold
38
+ attr_reader :style_italic
39
+ attr_reader :style_underline
40
+ attr_reader :style_align
41
+ attr_reader :style_top
42
+ attr_reader :style_bottom
43
+ attr_reader :style_line
44
+ attr_reader :style_base
45
+ attr_reader :style_next
46
+
47
+
48
+ # initialization
49
+ def initialize(**options, &block)
50
+ @style_default = false
51
+ @style_base = DEFAULT_STYLE_BASE
52
+ @style_next = DEFAULT_STYLE_NEXT
53
+
54
+ super options, &block
55
+
56
+ if (style_id == DEFAULT_STYLE_BASE)
57
+ @style_default ||= true
58
+ @style_color ||= DEFAULT_STYLE_COLOR
59
+ @style_size ||= DEFAULT_STYLE_SIZE
60
+ @style_bold ||= DEFAULT_STYLE_BOLD
61
+ @style_italic ||= DEFAULT_STYLE_ITALIC
62
+ @style_underline ||= DEFAULT_STYLE_UNDERLINE
63
+ @style_align ||= DEFAULT_STYLE_ALIGN
64
+ @style_top ||= DEFAULT_STYLE_TOP
65
+ @style_bottom ||= DEFAULT_STYLE_BOTTOM
66
+ @style_line ||= DEFAULT_STYLE_LINE
67
+ end
68
+ end
69
+
70
+
71
+ #-------------------------------------------------------------
72
+ # Public Instance Methods
73
+ #-------------------------------------------------------------
74
+
75
+ #=============== SETTERS ==============================
76
+
77
+ # booleans
78
+ [:bold, :italic, :underline].each do |m|
79
+ define_method "#{ m }" do |value|
80
+ instance_variable_set("@style_#{ m }", !!value)
81
+ end
82
+ end
83
+
84
+ # integers
85
+ [:bottom, :size, :line, :top].each do |m|
86
+ define_method "#{ m }" do |value|
87
+ instance_variable_set("@style_#{ m }", value.to_i)
88
+ end
89
+ end
90
+
91
+ # strings
92
+ [:id, :name, :color, :font].each do |m|
93
+ define_method "#{ m }" do |value|
94
+ instance_variable_set("@style_#{ m }", value.to_s)
95
+ end
96
+ end
97
+
98
+ # symbols
99
+ [:align].each do |m|
100
+ define_method "#{ m }" do |value|
101
+ instance_variable_set("@style_#{ m }", value.to_s.to_sym)
102
+ end
103
+ end
104
+
105
+
106
+ #=============== STATE ================================
107
+
108
+ def matches?(str)
109
+ style_id.downcase == str.to_s.downcase
110
+ end
111
+
112
+
113
+ #=============== VALIDATION ===========================
114
+
115
+ def valid?
116
+ a = [:id, :name]
117
+ a.map { |m| send("style_#{ m }") }.compact.size == a.size
118
+ end
119
+
120
+
121
+ #-------------------------------------------------------------
122
+ # Private Instance Methods
123
+ #-------------------------------------------------------------
124
+ private
125
+
126
+ def option_keys
127
+ [:bold, :italic, :underline, :top, :bottom, :size, :line, :id, :name, :color, :font, :align]
128
+ end
129
+
130
+ end
131
+
132
+ end
133
+ end
134
+ end
@@ -0,0 +1,155 @@
1
+ require 'caracal/core/models/base_model'
2
+ require 'caracal/core/models/margin_model'
3
+
4
+
5
+ module Caracal
6
+ module Core
7
+ module Models
8
+
9
+ # This class handles block options passed to tables via their data
10
+ # collections.
11
+ #
12
+ class TableCellModel < BaseModel
13
+
14
+ #-------------------------------------------------------------
15
+ # Configuration
16
+ #-------------------------------------------------------------
17
+
18
+ # constants
19
+ const_set(:DEFAULT_CELL_BACKGROUND, 'ffffff')
20
+ const_set(:DEFAULT_CELL_MARGINS, Caracal::Core::Models::MarginModel.new({ top: 100, bottom: 100, left: 100, right: 100 }))
21
+
22
+ # accessors
23
+ attr_reader :cell_background
24
+ attr_reader :cell_width
25
+ attr_reader :cell_margins
26
+
27
+ # initialization
28
+ def initialize(**options, &block)
29
+ @cell_background = DEFAULT_CELL_BACKGROUND
30
+ @cell_margins = DEFAULT_CELL_MARGINS
31
+
32
+ if content = options.delete(:content)
33
+ p content, options.dup
34
+ end
35
+
36
+ super options, &block
37
+ end
38
+
39
+
40
+ #-------------------------------------------------------------
41
+ # Public Methods
42
+ #-------------------------------------------------------------
43
+
44
+ #=============== DATA ACCESSORS =======================
45
+
46
+ def contents
47
+ @contents ||= []
48
+ end
49
+
50
+
51
+ #=============== STYLES ===============================
52
+
53
+ # This method allows styles to be applied to this cell
54
+ # from the table level. It attempts to add the style
55
+ # first to the instance, and then to any sub-models that
56
+ # respond to the method.
57
+ #
58
+ # In all cases, invalid options will simply be ignored.
59
+ #
60
+ def apply_styles(**options)
61
+ # first, try apply to self
62
+ options.each do |(k,v)|
63
+ send(k, v) if respond_to?(k)
64
+ end
65
+
66
+ # prevent top-level attrs from trickling down
67
+ options.delete_if { |(k,v)| option_keys.include?(k) }
68
+
69
+ # then, try apply to contents
70
+ contents.each do |model|
71
+ options.each do |k,v|
72
+ model.send(k, v) if model.respond_to?(k)
73
+ end
74
+
75
+ # finally, apply to runs. options do trickle down
76
+ # because paragraph-level styles don't seem to
77
+ # affect runs within tables. weirdsies.
78
+ if model.respond_to?(:runs)
79
+ model.runs.each do |run|
80
+ options.each do |k,v|
81
+ run.send(k, v) if run.respond_to?(k)
82
+ end
83
+ end
84
+ end
85
+ end
86
+ end
87
+
88
+ def calculate_width(default_width)
89
+ width(default_width) unless cell_width.to_i > 0
90
+
91
+ container_width = cell_width - cell_margin_left - cell_margin_right
92
+
93
+ contents.each do |model|
94
+ if model.respond_to?(:calculate_width)
95
+ model.calculate_width(container_width) # will always be a TableModel
96
+ end
97
+ end
98
+ end
99
+
100
+
101
+ #=============== GETTERS ==============================
102
+
103
+ # margin attrs
104
+ [:top, :bottom, :left, :right].each do |m|
105
+ define_method "cell_margin_#{ m }" do
106
+ v = cell_margins ? cell_margins.send("margin_#{ m }") : 0
107
+ end
108
+ end
109
+
110
+
111
+ #=============== SETTERS ==============================
112
+
113
+ # integers
114
+ [:width].each do |m|
115
+ define_method "#{ m }" do |value|
116
+ instance_variable_set("@cell_#{ m }", value.to_i)
117
+ end
118
+ end
119
+
120
+ # models
121
+ [:margins].each do |m|
122
+ define_method "#{ m }" do |**options, &block|
123
+ instance_variable_set("@cell_#{ m }", Caracal::Core::Models::MarginModel.new(options, &block))
124
+ end
125
+ end
126
+
127
+ # strings
128
+ [:background].each do |m|
129
+ define_method "#{ m }" do |value|
130
+ instance_variable_set("@cell_#{ m }", value.to_s)
131
+ end
132
+ end
133
+
134
+
135
+ #=============== VALIDATION ===========================
136
+
137
+ def valid?
138
+ contents.size > 0
139
+ end
140
+
141
+
142
+ #-------------------------------------------------------------
143
+ # Private Instance Methods
144
+ #-------------------------------------------------------------
145
+ private
146
+
147
+ def option_keys
148
+ [:background, :margins, :width]
149
+ end
150
+
151
+ end
152
+
153
+ end
154
+ end
155
+ end