caracal_the_curve 1.4.1

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 (116) hide show
  1. checksums.yaml +7 -0
  2. data/.github/workflows/main.yml +22 -0
  3. data/.github/workflows/publish_gem.yml +40 -0
  4. data/.gitignore +25 -0
  5. data/.travis.yml +20 -0
  6. data/CHANGELOG.md +141 -0
  7. data/Gemfile +3 -0
  8. data/LICENSE.txt +22 -0
  9. data/README.md +842 -0
  10. data/Rakefile +7 -0
  11. data/caracal.gemspec +28 -0
  12. data/lib/caracal/core/bookmarks.rb +52 -0
  13. data/lib/caracal/core/custom_properties.rb +49 -0
  14. data/lib/caracal/core/file_name.rb +43 -0
  15. data/lib/caracal/core/fonts.rb +75 -0
  16. data/lib/caracal/core/iframes.rb +42 -0
  17. data/lib/caracal/core/ignorables.rb +47 -0
  18. data/lib/caracal/core/images.rb +37 -0
  19. data/lib/caracal/core/list_styles.rb +93 -0
  20. data/lib/caracal/core/lists.rb +57 -0
  21. data/lib/caracal/core/models/base_model.rb +51 -0
  22. data/lib/caracal/core/models/bookmark_model.rb +86 -0
  23. data/lib/caracal/core/models/border_model.rb +120 -0
  24. data/lib/caracal/core/models/custom_property_model.rb +60 -0
  25. data/lib/caracal/core/models/font_model.rb +64 -0
  26. data/lib/caracal/core/models/iframe_model.rb +148 -0
  27. data/lib/caracal/core/models/image_model.rb +133 -0
  28. data/lib/caracal/core/models/line_break_model.rb +15 -0
  29. data/lib/caracal/core/models/link_model.rb +94 -0
  30. data/lib/caracal/core/models/list_item_model.rb +108 -0
  31. data/lib/caracal/core/models/list_model.rb +132 -0
  32. data/lib/caracal/core/models/list_style_model.rb +118 -0
  33. data/lib/caracal/core/models/margin_model.rb +76 -0
  34. data/lib/caracal/core/models/namespace_model.rb +65 -0
  35. data/lib/caracal/core/models/page_break_model.rb +61 -0
  36. data/lib/caracal/core/models/page_number_model.rb +95 -0
  37. data/lib/caracal/core/models/page_size_model.rb +79 -0
  38. data/lib/caracal/core/models/paragraph_model.rb +186 -0
  39. data/lib/caracal/core/models/relationship_model.rb +114 -0
  40. data/lib/caracal/core/models/rule_model.rb +27 -0
  41. data/lib/caracal/core/models/style_model.rb +165 -0
  42. data/lib/caracal/core/models/table_cell_model.rb +176 -0
  43. data/lib/caracal/core/models/table_model.rb +206 -0
  44. data/lib/caracal/core/models/text_model.rb +118 -0
  45. data/lib/caracal/core/namespaces.rb +89 -0
  46. data/lib/caracal/core/page_breaks.rb +29 -0
  47. data/lib/caracal/core/page_numbers.rb +58 -0
  48. data/lib/caracal/core/page_settings.rb +74 -0
  49. data/lib/caracal/core/relationships.rb +90 -0
  50. data/lib/caracal/core/rules.rb +35 -0
  51. data/lib/caracal/core/styles.rb +86 -0
  52. data/lib/caracal/core/tables.rb +42 -0
  53. data/lib/caracal/core/text.rb +75 -0
  54. data/lib/caracal/document.rb +272 -0
  55. data/lib/caracal/errors.rb +23 -0
  56. data/lib/caracal/renderers/app_renderer.rb +41 -0
  57. data/lib/caracal/renderers/content_types_renderer.rb +54 -0
  58. data/lib/caracal/renderers/core_renderer.rb +44 -0
  59. data/lib/caracal/renderers/custom_renderer.rb +64 -0
  60. data/lib/caracal/renderers/document_renderer.rb +427 -0
  61. data/lib/caracal/renderers/fonts_renderer.rb +56 -0
  62. data/lib/caracal/renderers/footer_renderer.rb +90 -0
  63. data/lib/caracal/renderers/numbering_renderer.rb +88 -0
  64. data/lib/caracal/renderers/package_relationships_renderer.rb +51 -0
  65. data/lib/caracal/renderers/relationships_renderer.rb +48 -0
  66. data/lib/caracal/renderers/settings_renderer.rb +58 -0
  67. data/lib/caracal/renderers/styles_renderer.rb +181 -0
  68. data/lib/caracal/renderers/xml_renderer.rb +83 -0
  69. data/lib/caracal/utilities.rb +23 -0
  70. data/lib/caracal/version.rb +3 -0
  71. data/lib/caracal.rb +40 -0
  72. data/lib/tilt/caracal.rb +21 -0
  73. data/spec/lib/caracal/core/bookmarks_spec.rb +35 -0
  74. data/spec/lib/caracal/core/file_name_spec.rb +54 -0
  75. data/spec/lib/caracal/core/fonts_spec.rb +119 -0
  76. data/spec/lib/caracal/core/iframes_spec.rb +29 -0
  77. data/spec/lib/caracal/core/ignorables_spec.rb +79 -0
  78. data/spec/lib/caracal/core/images_spec.rb +25 -0
  79. data/spec/lib/caracal/core/list_styles_spec.rb +121 -0
  80. data/spec/lib/caracal/core/lists_spec.rb +43 -0
  81. data/spec/lib/caracal/core/models/base_model_spec.rb +38 -0
  82. data/spec/lib/caracal/core/models/bookmark_model_spec.rb +135 -0
  83. data/spec/lib/caracal/core/models/border_model_spec.rb +159 -0
  84. data/spec/lib/caracal/core/models/font_model_spec.rb +92 -0
  85. data/spec/lib/caracal/core/models/iframe_model_spec.rb +83 -0
  86. data/spec/lib/caracal/core/models/image_model_spec.rb +225 -0
  87. data/spec/lib/caracal/core/models/line_break_model_spec.rb +21 -0
  88. data/spec/lib/caracal/core/models/link_model_spec.rb +179 -0
  89. data/spec/lib/caracal/core/models/list_item_model_spec.rb +197 -0
  90. data/spec/lib/caracal/core/models/list_model_spec.rb +178 -0
  91. data/spec/lib/caracal/core/models/list_style_model_spec.rb +198 -0
  92. data/spec/lib/caracal/core/models/margin_model_spec.rb +111 -0
  93. data/spec/lib/caracal/core/models/namespace_model_spec.rb +107 -0
  94. data/spec/lib/caracal/core/models/page_break_model_spec.rb +21 -0
  95. data/spec/lib/caracal/core/models/page_number_model_spec.rb +136 -0
  96. data/spec/lib/caracal/core/models/page_size_model_spec.rb +101 -0
  97. data/spec/lib/caracal/core/models/paragraph_model_spec.rb +196 -0
  98. data/spec/lib/caracal/core/models/relationship_model_spec.rb +193 -0
  99. data/spec/lib/caracal/core/models/rule_model_spec.rb +108 -0
  100. data/spec/lib/caracal/core/models/style_model_spec.rb +225 -0
  101. data/spec/lib/caracal/core/models/table_cell_model_spec.rb +230 -0
  102. data/spec/lib/caracal/core/models/table_model_spec.rb +222 -0
  103. data/spec/lib/caracal/core/models/text_model_spec.rb +154 -0
  104. data/spec/lib/caracal/core/namespaces_spec.rb +116 -0
  105. data/spec/lib/caracal/core/page_breaks_spec.rb +25 -0
  106. data/spec/lib/caracal/core/page_numbers_spec.rb +89 -0
  107. data/spec/lib/caracal/core/page_settings_spec.rb +151 -0
  108. data/spec/lib/caracal/core/relationships_spec.rb +119 -0
  109. data/spec/lib/caracal/core/rules_spec.rb +25 -0
  110. data/spec/lib/caracal/core/styles_spec.rb +129 -0
  111. data/spec/lib/caracal/core/tables_spec.rb +25 -0
  112. data/spec/lib/caracal/core/text_spec.rb +52 -0
  113. data/spec/lib/caracal/errors_spec.rb +10 -0
  114. data/spec/spec_helper.rb +8 -0
  115. data/spec/support/_fixtures/snippet.docx +0 -0
  116. metadata +292 -0
@@ -0,0 +1,74 @@
1
+ require 'caracal/core/models/margin_model'
2
+ require 'caracal/core/models/page_size_model'
3
+ require 'caracal/errors'
4
+
5
+
6
+ module Caracal
7
+ module Core
8
+
9
+ # This module encapsulates all the functionality related to setting the
10
+ # document's size and margins.
11
+ #
12
+ module PageSettings
13
+ def self.included(base)
14
+ base.class_eval do
15
+
16
+ #-------------------------------------------------------------
17
+ # Configuration
18
+ #-------------------------------------------------------------
19
+
20
+ # accessors
21
+ attr_reader :page_width
22
+ attr_reader :page_orientation
23
+ attr_reader :page_height
24
+ attr_reader :page_margin_top
25
+ attr_reader :page_margin_bottom
26
+ attr_reader :page_margin_left
27
+ attr_reader :page_margin_right
28
+
29
+
30
+ #-------------------------------------------------------------
31
+ # Public Methods
32
+ #-------------------------------------------------------------
33
+
34
+ # This method controls the physical margins of the printed page. Defaults
35
+ # to 1in on each side.
36
+ #
37
+ def page_margins(options={}, &block)
38
+ model = Caracal::Core::Models::MarginModel.new(options, &block)
39
+
40
+ if model.valid?
41
+ if (model.margin_top + model.margin_bottom < page_height) && (model.margin_left + model.margin_right < page_width)
42
+ @page_margin_top = model.margin_top
43
+ @page_margin_bottom = model.margin_bottom
44
+ @page_margin_left = model.margin_left
45
+ @page_margin_right = model.margin_right
46
+ else
47
+ raise Caracal::Errors::InvalidModelError, 'page_margins method requires margins to be smaller than the page size.'
48
+ end
49
+ else
50
+ raise Caracal::Errors::InvalidModelError, 'page_margins method requires non-zero :top, :bottom, :left, and :right options.'
51
+ end
52
+ end
53
+
54
+ # This method controls the physical width and height of the printed page. Defaults
55
+ # to US standard A4 portrait size.
56
+ #
57
+ def page_size(options={}, &block)
58
+ model = Caracal::Core::Models::PageSizeModel.new(options, &block)
59
+
60
+ if model.valid?
61
+ @page_width = model.page_width
62
+ @page_height = model.page_height
63
+ @page_orientation = model.page_orientation
64
+ else
65
+ raise Caracal::Errors::InvalidModelError, 'page_size method requires non-zero :width and :height options.'
66
+ end
67
+ end
68
+
69
+ end
70
+ end
71
+ end
72
+
73
+ end
74
+ end
@@ -0,0 +1,90 @@
1
+ require 'caracal/core/models/relationship_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 relationships.
10
+ #
11
+ module Relationships
12
+ def self.included(base)
13
+ base.class_eval do
14
+
15
+ #-------------------------------------------------------------
16
+ # Configuration
17
+ #-------------------------------------------------------------
18
+
19
+ attr_reader :relationship_counter
20
+
21
+
22
+ #-------------------------------------------------------------
23
+ # Class Methods
24
+ #-------------------------------------------------------------
25
+
26
+ def self.default_relationships
27
+ [
28
+ { target: 'fontTable.xml', type: :font },
29
+ { target: 'footer1.xml', type: :footer },
30
+ { target: 'numbering.xml', type: :numbering },
31
+ { target: 'settings.xml', type: :setting },
32
+ { target: 'styles.xml', type: :style }
33
+ ]
34
+ end
35
+
36
+
37
+ #-------------------------------------------------------------
38
+ # Public Methods
39
+ #-------------------------------------------------------------
40
+
41
+ #============== ATTRIBUTES ==========================
42
+
43
+ def relationship(options={}, &block)
44
+ id = relationship_counter.to_i + 1
45
+ options.merge!({ id: id })
46
+
47
+ model = Caracal::Core::Models::RelationshipModel.new(options, &block)
48
+ if model.valid?
49
+ @relationship_counter = id
50
+ rel = register_relationship(model)
51
+ else
52
+ raise Caracal::Errors::InvalidModelError, 'relationship must specify the :id, :target, and :type attributes.'
53
+ end
54
+ rel
55
+ end
56
+
57
+
58
+ #============== GETTERS =============================
59
+
60
+ def relationships
61
+ @relationships ||= []
62
+ end
63
+
64
+ def find_relationship(target)
65
+ relationships.find { |r| r.matches?(target) }
66
+ end
67
+
68
+
69
+ #============== REGISTRATION ========================
70
+
71
+ def register_relationship(model)
72
+ unless r = find_relationship(model.relationship_target)
73
+ relationships << model
74
+ r = model
75
+ end
76
+ r
77
+ end
78
+
79
+ def unregister_relationship(target)
80
+ if r = find_relationship(target)
81
+ relationships.delete(r)
82
+ end
83
+ end
84
+
85
+ end
86
+ end
87
+ end
88
+
89
+ end
90
+ end
@@ -0,0 +1,35 @@
1
+ require 'caracal/core/models/rule_model'
2
+ require 'caracal/errors'
3
+
4
+
5
+ module Caracal
6
+ module Core
7
+
8
+ # This module encapsulates all the functionality related to adding
9
+ # horizontal rules to the document.
10
+ #
11
+ module Rules
12
+ def self.included(base)
13
+ base.class_eval do
14
+
15
+ #-------------------------------------------------------------
16
+ # Public Methods
17
+ #-------------------------------------------------------------
18
+
19
+ def hr(options={}, &block)
20
+ model = Caracal::Core::Models::RuleModel.new(options, &block)
21
+
22
+ if model.valid?
23
+ contents << model
24
+ else
25
+ raise Caracal::Errors::InvalidModelError, 'Horizontal rules require non-zero :size and :spacing values.'
26
+ end
27
+ model
28
+ end
29
+
30
+ end
31
+ end
32
+ end
33
+
34
+ end
35
+ end
@@ -0,0 +1,86 @@
1
+ require 'caracal/core/models/style_model'
2
+ require 'caracal/errors'
3
+
4
+
5
+ module Caracal
6
+ module Core
7
+
8
+ # This module encapsulates all the functionality related to defining
9
+ # paragraph styles.
10
+ #
11
+ module Styles
12
+ def self.included(base)
13
+ base.class_eval do
14
+
15
+ #-------------------------------------------------------------
16
+ # Class Methods
17
+ #-------------------------------------------------------------
18
+
19
+ def self.default_styles
20
+ [
21
+ { id: 'Normal', name: 'normal', font: 'Arial', size: 20, line: 320, color: '333333' },
22
+ { id: 'Heading1', name: 'heading 1', font: 'Palatino', size: 36, bottom: 120 },
23
+ { id: 'Heading2', name: 'heading 2', font: 'Arial', size: 26, top: 120, bottom: 160, bold: true },
24
+ { id: 'Heading3', name: 'heading 3', font: 'Arial', size: 24, top: 120, bottom: 160, bold: true, italic: true, color: '666666' },
25
+ { id: 'Heading4', name: 'heading 4', font: 'Palatino', size: 24, top: 120, bottom: 120, bold: true },
26
+ { id: 'Heading5', name: 'heading 5', font: 'Arial', size: 22, top: 120, bottom: 120, bold: true },
27
+ { id: 'Heading6', name: 'heading 6', font: 'Arial', size: 22, top: 120, bottom: 120, underline: true, italic: true, color: '666666' },
28
+ { id: 'Title', name: 'title', font: 'Palatino', size: 60 },
29
+ { id: 'Subtitle', name: 'subtitle', font: 'Arial', size: 28, top: 60 }
30
+ ]
31
+ end
32
+
33
+
34
+ #-------------------------------------------------------------
35
+ # Public Methods
36
+ #-------------------------------------------------------------
37
+
38
+ #============== ATTRIBUTES ==========================
39
+
40
+ def style(options={}, &block)
41
+ model = Caracal::Core::Models::StyleModel.new(options, &block)
42
+
43
+ if model.valid?
44
+ register_style(model)
45
+ else
46
+ raise Caracal::Errors::InvalidModelError, 'style must define an :id and :name.'
47
+ end
48
+ model
49
+ end
50
+
51
+
52
+ #============== GETTERS =============================
53
+
54
+ def styles
55
+ @styles ||= []
56
+ end
57
+
58
+ def default_style
59
+ styles.find { |s| s.style_default }
60
+ end
61
+
62
+ def find_style(id)
63
+ styles.find { |s| s.matches?(id) }
64
+ end
65
+
66
+
67
+ #============== REGISTRATION ========================
68
+
69
+ def register_style(model)
70
+ unregister_style(model.style_id)
71
+ styles << model
72
+ model
73
+ end
74
+
75
+ def unregister_style(id)
76
+ if s = find_style(id)
77
+ styles.delete(s)
78
+ end
79
+ end
80
+
81
+ end
82
+ end
83
+ end
84
+
85
+ end
86
+ end
@@ -0,0 +1,42 @@
1
+ require 'caracal/core/models/table_model'
2
+ require 'caracal/errors'
3
+
4
+
5
+ module Caracal
6
+ module Core
7
+
8
+ # This module encapsulates all the functionality related to adding tables
9
+ # to the document.
10
+ #
11
+ module Tables
12
+ def self.included(base)
13
+ base.class_eval do
14
+
15
+ #-------------------------------------------------------------
16
+ # Public Methods
17
+ #-------------------------------------------------------------
18
+
19
+ def table(*args, &block)
20
+ options = Caracal::Utilities.extract_options!(args)
21
+ options.merge!({ data: args.first }) if args.first
22
+
23
+ model = Caracal::Core::Models::TableModel.new(options, &block)
24
+ if respond_to?(:page_width)
25
+ container_width = page_width - page_margin_left - page_margin_right
26
+ model.calculate_width(container_width)
27
+ end
28
+
29
+ if model.valid?
30
+ contents << model
31
+ else
32
+ raise Caracal::Errors::InvalidModelError, 'Table must be provided data for at least one cell.'
33
+ end
34
+ model
35
+ end
36
+
37
+ end
38
+ end
39
+ end
40
+
41
+ end
42
+ end
@@ -0,0 +1,75 @@
1
+ require 'caracal/core/models/paragraph_model'
2
+ require 'caracal/core/models/line_break_model'
3
+ require 'caracal/errors'
4
+
5
+
6
+ module Caracal
7
+ module Core
8
+
9
+ # This module encapsulates all the functionality related to adding text
10
+ # to the document.
11
+ #
12
+ module Text
13
+ def self.included(base)
14
+ base.class_eval do
15
+
16
+ #-------------------------------------------------------------
17
+ # Public Methods
18
+ #-------------------------------------------------------------
19
+
20
+ #============== PARAGRAPHS ==========================
21
+
22
+ def p(*args, &block)
23
+ options = Caracal::Utilities.extract_options!(args)
24
+ options.merge!({ content: args.first }) if args.first
25
+
26
+ model = Caracal::Core::Models::ParagraphModel.new(options, &block)
27
+ if model.valid?
28
+ contents << model
29
+ else
30
+ raise Caracal::Errors::InvalidModelError, 'Paragraphs and headings, which delegate to the :p command, require at least one text string.'
31
+ end
32
+ model
33
+ end
34
+
35
+
36
+ #============== HEADINGS ============================
37
+
38
+ # All heading methods simply delegate to the paragraph
39
+ # model with an explicitly set style class.
40
+ #
41
+ [:h1, :h2, :h3, :h4, :h5, :h6].each do |cmd|
42
+ define_method "#{ cmd }" do |*args, &block|
43
+ options = Caracal::Utilities.extract_options!(args)
44
+ options.merge!({ style: style_id_for_header(cmd) })
45
+ p(args.first, options, &block)
46
+ end
47
+ end
48
+
49
+
50
+ #-------------------------------------------------------------
51
+ # Private Methods
52
+ #-------------------------------------------------------------
53
+ private
54
+
55
+ # This method translates the html-like command to the
56
+ # corresponding style id.
57
+ #
58
+ def style_id_for_header(command)
59
+ case command.to_s
60
+ when 'h1' then 'Heading1'
61
+ when 'h2' then 'Heading2'
62
+ when 'h3' then 'Heading3'
63
+ when 'h4' then 'Heading4'
64
+ when 'h5' then 'Heading5'
65
+ when 'h6' then 'Heading6'
66
+ else 'Normal'
67
+ end
68
+ end
69
+
70
+ end
71
+ end
72
+ end
73
+
74
+ end
75
+ end
@@ -0,0 +1,272 @@
1
+ require 'open-uri'
2
+ require 'zip'
3
+
4
+ require 'caracal/core/bookmarks'
5
+ require 'caracal/core/custom_properties'
6
+ require 'caracal/core/file_name'
7
+ require 'caracal/core/fonts'
8
+ require 'caracal/core/iframes'
9
+ require 'caracal/core/ignorables'
10
+ require 'caracal/core/images'
11
+ require 'caracal/core/list_styles'
12
+ require 'caracal/core/lists'
13
+ require 'caracal/core/namespaces'
14
+ require 'caracal/core/page_breaks'
15
+ require 'caracal/core/page_numbers'
16
+ require 'caracal/core/page_settings'
17
+ require 'caracal/core/relationships'
18
+ require 'caracal/core/rules'
19
+ require 'caracal/core/styles'
20
+ require 'caracal/core/tables'
21
+ require 'caracal/core/text'
22
+
23
+ require 'caracal/renderers/app_renderer'
24
+ require 'caracal/renderers/content_types_renderer'
25
+ require 'caracal/renderers/core_renderer'
26
+ require 'caracal/renderers/custom_renderer'
27
+ require 'caracal/renderers/document_renderer'
28
+ require 'caracal/renderers/fonts_renderer'
29
+ require 'caracal/renderers/footer_renderer'
30
+ require 'caracal/renderers/numbering_renderer'
31
+ require 'caracal/renderers/package_relationships_renderer'
32
+ require 'caracal/renderers/relationships_renderer'
33
+ require 'caracal/renderers/settings_renderer'
34
+ require 'caracal/renderers/styles_renderer'
35
+
36
+
37
+ module Caracal
38
+ class Document
39
+
40
+ #------------------------------------------------------
41
+ # Configuration
42
+ #------------------------------------------------------
43
+
44
+ # mixins (order is important)
45
+ include Caracal::Core::CustomProperties
46
+ include Caracal::Core::FileName
47
+ include Caracal::Core::Ignorables
48
+ include Caracal::Core::Namespaces
49
+ include Caracal::Core::Relationships
50
+
51
+ include Caracal::Core::Fonts
52
+ include Caracal::Core::PageSettings
53
+ include Caracal::Core::PageNumbers
54
+ include Caracal::Core::Styles
55
+ include Caracal::Core::ListStyles
56
+
57
+ include Caracal::Core::Bookmarks
58
+ include Caracal::Core::IFrames
59
+ include Caracal::Core::Images
60
+ include Caracal::Core::Lists
61
+ include Caracal::Core::PageBreaks
62
+ include Caracal::Core::Rules
63
+ include Caracal::Core::Tables
64
+ include Caracal::Core::Text
65
+
66
+
67
+ #------------------------------------------------------
68
+ # Public Class Methods
69
+ #------------------------------------------------------
70
+
71
+ #============ OUTPUT ==================================
72
+
73
+ # This method renders a new Word document and returns it as a
74
+ # a string.
75
+ #
76
+ def self.render(f_name = nil, &block)
77
+ docx = new(f_name, &block)
78
+ buffer = docx.render
79
+
80
+ buffer.rewind
81
+ buffer.sysread
82
+ end
83
+
84
+ # This method renders a new Word document and saves it to the
85
+ # file system.
86
+ #
87
+ def self.save(f_name = nil, &block)
88
+ docx = new(f_name, &block)
89
+ docx.save
90
+ # buffer = docx.render
91
+ #
92
+ # File.open(docx.path, 'wb') { |f| f.write(buffer.string) }
93
+ end
94
+
95
+
96
+
97
+ #------------------------------------------------------
98
+ # Public Instance Methods
99
+ #------------------------------------------------------
100
+
101
+ # This method instantiates a new word document.
102
+ #
103
+ def initialize(name = nil, &block)
104
+ file_name(name)
105
+
106
+ page_size
107
+ page_margins top: 1440, bottom: 1440, left: 1440, right: 1440
108
+ page_numbers
109
+
110
+ [:font, :list_style, :namespace, :relationship, :style].each do |method|
111
+ collection = self.class.send("default_#{ method }s")
112
+ collection.each do |item|
113
+ send(method, item)
114
+ end
115
+ end
116
+
117
+ if block_given?
118
+ (block.arity < 1) ? instance_eval(&block) : block[self]
119
+ end
120
+ end
121
+
122
+
123
+ #============ GETTERS =================================
124
+
125
+ # This method returns an array of models which constitute the
126
+ # set of instructions for producing the document content.
127
+ #
128
+ def contents
129
+ @contents ||= []
130
+ end
131
+
132
+
133
+ #============ RENDERING ===============================
134
+
135
+ # This method renders the word document instance into
136
+ # a string buffer. Order is important!
137
+ #
138
+ def render
139
+ buffer = ::Zip::OutputStream.write_buffer do |zip|
140
+ render_package_relationships(zip)
141
+ render_content_types(zip)
142
+ render_app(zip)
143
+ render_core(zip)
144
+ render_custom(zip)
145
+ render_fonts(zip)
146
+ render_footer(zip)
147
+ render_settings(zip)
148
+ render_styles(zip)
149
+ render_document(zip)
150
+ render_relationships(zip) # Must go here: Depends on document renderer
151
+ render_media(zip) # Must go here: Depends on document renderer
152
+ render_numbering(zip) # Must go here: Depends on document renderer
153
+ end
154
+ end
155
+
156
+
157
+ #============ SAVING ==================================
158
+
159
+ def save
160
+ buffer = render
161
+
162
+ File.open(path, 'wb') { |f| f.write(buffer.string) }
163
+ end
164
+
165
+
166
+ #------------------------------------------------------
167
+ # Private Instance Methods
168
+ #------------------------------------------------------
169
+ private
170
+
171
+ #============ RENDERERS ===============================
172
+
173
+ def render_app(zip)
174
+ content = ::Caracal::Renderers::AppRenderer.render(self)
175
+
176
+ zip.put_next_entry('docProps/app.xml')
177
+ zip.write(content)
178
+ end
179
+
180
+ def render_content_types(zip)
181
+ content = ::Caracal::Renderers::ContentTypesRenderer.render(self)
182
+
183
+ zip.put_next_entry('[Content_Types].xml')
184
+ zip.write(content)
185
+ end
186
+
187
+ def render_core(zip)
188
+ content = ::Caracal::Renderers::CoreRenderer.render(self)
189
+
190
+ zip.put_next_entry('docProps/core.xml')
191
+ zip.write(content)
192
+ end
193
+
194
+ def render_custom(zip)
195
+ content = ::Caracal::Renderers::CustomRenderer.render(self)
196
+
197
+ zip.put_next_entry('docProps/custom.xml')
198
+ zip.write(content)
199
+ end
200
+
201
+ def render_document(zip)
202
+ content = ::Caracal::Renderers::DocumentRenderer.render(self)
203
+
204
+ zip.put_next_entry('word/document.xml')
205
+ zip.write(content)
206
+ end
207
+
208
+ def render_fonts(zip)
209
+ content = ::Caracal::Renderers::FontsRenderer.render(self)
210
+
211
+ zip.put_next_entry('word/fontTable.xml')
212
+ zip.write(content)
213
+ end
214
+
215
+ def render_footer(zip)
216
+ content = ::Caracal::Renderers::FooterRenderer.render(self)
217
+
218
+ zip.put_next_entry('word/footer1.xml')
219
+ zip.write(content)
220
+ end
221
+
222
+ def render_media(zip)
223
+ images = relationships.select { |r| r.relationship_type == :image }
224
+ images.each do |rel|
225
+ if rel.relationship_data.to_s.size > 0
226
+ content = rel.relationship_data
227
+ else
228
+ content = open(rel.relationship_target).read
229
+ end
230
+
231
+ zip.put_next_entry("word/#{ rel.formatted_target }")
232
+ zip.write(content)
233
+ end
234
+ end
235
+
236
+ def render_numbering(zip)
237
+ content = ::Caracal::Renderers::NumberingRenderer.render(self)
238
+
239
+ zip.put_next_entry('word/numbering.xml')
240
+ zip.write(content)
241
+ end
242
+
243
+ def render_package_relationships(zip)
244
+ content = ::Caracal::Renderers::PackageRelationshipsRenderer.render(self)
245
+
246
+ zip.put_next_entry('_rels/.rels')
247
+ zip.write(content)
248
+ end
249
+
250
+ def render_relationships(zip)
251
+ content = ::Caracal::Renderers::RelationshipsRenderer.render(self)
252
+
253
+ zip.put_next_entry('word/_rels/document.xml.rels')
254
+ zip.write(content)
255
+ end
256
+
257
+ def render_settings(zip)
258
+ content = ::Caracal::Renderers::SettingsRenderer.render(self)
259
+
260
+ zip.put_next_entry('word/settings.xml')
261
+ zip.write(content)
262
+ end
263
+
264
+ def render_styles(zip)
265
+ content = ::Caracal::Renderers::StylesRenderer.render(self)
266
+
267
+ zip.put_next_entry('word/styles.xml')
268
+ zip.write(content)
269
+ end
270
+
271
+ end
272
+ end
@@ -0,0 +1,23 @@
1
+ module Caracal
2
+ module Errors
3
+ # This error is raised whenever a model is created with
4
+ # invalid options.
5
+ #
6
+ InvalidModelError = Class.new(StandardError)
7
+
8
+ # This error is raised whenever a table model is passed an
9
+ # invalid data structure.
10
+ #
11
+ InvalidTableDataError = Class.new(StandardError)
12
+
13
+ # This error is raised if the document does not have a default
14
+ # style declared.
15
+ #
16
+ NoDefaultStyleError = Class.new(StandardError)
17
+
18
+ # This error is raised when a class lacks a valid reference to a
19
+ # Caracal document object.
20
+ #
21
+ NoDocumentError = Class.new(StandardError)
22
+ end
23
+ end