diff --git a/lib/caracal/core/headers.rb b/lib/caracal/core/headers.rb new file mode 100644 index 00000000..a2e69fcc --- /dev/null +++ b/lib/caracal/core/headers.rb @@ -0,0 +1,39 @@ +require 'caracal/core/models/header_model' +require 'caracal/errors' + + + + +module Caracal + module Core + + + module Headers + def self.included(base) + base.class_eval do + const_set(:DEFAULT_HEADER_ALIGN, :center) + + attr_reader :header_align + attr_reader :header_text + attr_reader :header_show + + + + def headers(*args, &block) + options = Caracal::Utilities.extract_options!(args) + options.merge!({ show: !!args.first }) unless args.first.nil? # careful: falsey value + + model = Caracal::Core::Models::HeaderModel.new(options, &block) + if model.valid? + @header_align = model.header_align + @header_text = model.header_text + @header_show = model.header_show + else + raise Caracal::Errors::InvalidModelError, 'headers :align parameter must be :left, :center, or :right' + end + end + end + end + end + end +end diff --git a/lib/caracal/core/models/header_model.rb b/lib/caracal/core/models/header_model.rb new file mode 100644 index 00000000..7db462ba --- /dev/null +++ b/lib/caracal/core/models/header_model.rb @@ -0,0 +1,58 @@ +require 'caracal/core/models/base_model' + + + +module Caracal + module Core + module Models + + + + class HeaderModel < BaseModel + + const_set(:DEFAULT_HEADER_ALIGN, :center) + const_set(:DEFAULT_HEADER_SHOW, false ) + + attr_reader :header_align + attr_reader :header_text + attr_reader :header_show + + + def initialize(options={}, &block) + @header_align = DEFAULT_HEADER_ALIGN + @header_text = nil + @header_show = DEFAULT_HEADER_SHOW + + super options,&block + end + + def align(value) + @header_align = value.to_s.to_sym + end + + + def text(value) + @header_text = value.to_s.strip + end + + def show(value) + @header_show = !!value + end + + + + + def valid? + (!header_show || [:left, :center, :right].include?(header_align)) + end + + + private + + def option_keys + [:align, :label, :show] + end + end + end + end +end diff --git a/lib/caracal/core/models/relationship_model.rb b/lib/caracal/core/models/relationship_model.rb index c20fe2cb..4913b13a 100644 --- a/lib/caracal/core/models/relationship_model.rb +++ b/lib/caracal/core/models/relationship_model.rb @@ -18,6 +18,7 @@ class RelationshipModel < BaseModel TYPE_MAP = { font: 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/fontTable', footer: 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/footer', + header: 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/header', image: 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/image', link: 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink', numbering: 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/numbering', @@ -111,4 +112,4 @@ def option_keys end end -end \ No newline at end of file +end diff --git a/lib/caracal/core/relationships.rb b/lib/caracal/core/relationships.rb index e4b9316f..aa4413af 100644 --- a/lib/caracal/core/relationships.rb +++ b/lib/caracal/core/relationships.rb @@ -27,6 +27,7 @@ def self.default_relationships [ { target: 'fontTable.xml', type: :font }, { target: 'footer1.xml', type: :footer }, + { target: 'header1.xml', type: :header }, { target: 'numbering.xml', type: :numbering }, { target: 'settings.xml', type: :setting }, { target: 'styles.xml', type: :style } @@ -87,4 +88,4 @@ def unregister_relationship(target) end end -end \ No newline at end of file +end diff --git a/lib/caracal/document.rb b/lib/caracal/document.rb index 91593ee8..27b2a409 100644 --- a/lib/caracal/document.rb +++ b/lib/caracal/document.rb @@ -12,6 +12,7 @@ require 'caracal/core/namespaces' require 'caracal/core/page_breaks' require 'caracal/core/page_numbers' +require 'caracal/core/headers' require 'caracal/core/page_settings' require 'caracal/core/relationships' require 'caracal/core/rules' @@ -26,6 +27,7 @@ require 'caracal/renderers/document_renderer' require 'caracal/renderers/fonts_renderer' require 'caracal/renderers/footer_renderer' +require 'caracal/renderers/header_renderer' require 'caracal/renderers/numbering_renderer' require 'caracal/renderers/package_relationships_renderer' require 'caracal/renderers/relationships_renderer' @@ -52,6 +54,7 @@ class Document include Caracal::Core::PageNumbers include Caracal::Core::Styles include Caracal::Core::ListStyles + include Caracal::Core::Headers include Caracal::Core::IFrames include Caracal::Core::Images @@ -141,6 +144,7 @@ def render render_custom(zip) render_fonts(zip) render_footer(zip) + render_header(zip) render_settings(zip) render_styles(zip) render_document(zip) @@ -208,6 +212,14 @@ def render_footer(zip) zip.write(content) end + def render_header(zip) + content = ::Caracal::Renderers::HeaderRenderer.render(self) + + zip.put_next_entry('word/header1.xml') + zip.write(content) + end + + def render_media(zip) images = relationships.select { |r| r.relationship_type == :image } images.each do |rel| diff --git a/lib/caracal/renderers/content_types_renderer.rb b/lib/caracal/renderers/content_types_renderer.rb index 0054159d..99597d9b 100644 --- a/lib/caracal/renderers/content_types_renderer.rb +++ b/lib/caracal/renderers/content_types_renderer.rb @@ -28,6 +28,7 @@ def to_xml xml.send 'Override', { 'PartName' => '/docProps/custom.xml', 'ContentType' => 'application/vnd.openxmlformats-officedocument.custom-properties+xml' } xml.send 'Override', { 'PartName' => '/word/document.xml', 'ContentType' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document.main+xml' } xml.send 'Override', { 'PartName' => '/word/footer1.xml', 'ContentType' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.footer+xml' } + xml.send 'Override', { 'PartName' => '/word/header1.xml', 'ContentType' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.header+xml' } xml.send 'Override', { 'PartName' => '/word/fontTable.xml', 'ContentType' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.fontTable+xml' } xml.send 'Override', { 'PartName' => '/word/numbering.xml', 'ContentType' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.numbering+xml' } xml.send 'Override', { 'PartName' => '/word/settings.xml', 'ContentType' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.settings+xml' } diff --git a/lib/caracal/renderers/document_renderer.rb b/lib/caracal/renderers/document_renderer.rb index aab2cb22..7a8ec613 100644 --- a/lib/caracal/renderers/document_renderer.rb +++ b/lib/caracal/renderers/document_renderer.rb @@ -36,6 +36,11 @@ def to_xml xml['w'].footerReference({ 'r:id' => rel.formatted_id, 'w:type' => 'default' }) end end + if document.header_show + if rel = document.find_relationship('header1.xml') + xml['w'].headerReference({ 'r:id' => rel.formatted_id, 'w:type' => 'default' }) + end + end xml['w'].pgSz page_size_options xml['w'].pgMar page_margin_options end diff --git a/lib/caracal/renderers/header_renderer.rb b/lib/caracal/renderers/header_renderer.rb new file mode 100644 index 00000000..78919bf1 --- /dev/null +++ b/lib/caracal/renderers/header_renderer.rb @@ -0,0 +1,75 @@ +require 'nokogiri' + +require 'caracal/renderers/xml_renderer' + + +module Caracal + module Renderers + class HeaderRenderer < XmlRenderer + + #------------------------------------------------------------- + # Public Methods + #------------------------------------------------------------- + + # This method produces the xml required for the `word/settings.xml` + # sub-document. + + def to_xml + builder = ::Nokogiri::XML::Builder.with(declaration_xml) do |xml| + xml['w'].ftr root_options do + xml['w'].p paragraph_options do + xml['w'].pPr do + xml['w'].contextualSpacing({ 'w:val' => '0' }) + xml['w'].jc({ 'w:val' => "#{ document.header_align }" }) + end + unless document.header_text.nil? + xml['w'].r run_options do + xml['w'].rPr do + xml['w'].rStyle({ 'w:val' => 'Header' }) + end + xml['w'].t({ 'xml:space' => 'preserve' }) do + xml.text "#{ document.header_text } " + end + end + end + xml['w'].r run_options do + xml['w'].rPr do + xml['w'].rtl({ 'w:val' => '0' }) + end + end + end + end + end + builder.to_xml(save_options) + end + + + #------------------------------------------------------------- + # Private Methods + #------------------------------------------------------------- + private + + def root_options + { + 'xmlns:mc' => 'http://schemas.openxmlformats.org/markup-compatibility/2006', + 'xmlns:o' => 'urn:schemas-microsoft-com:office:office', + 'xmlns:r' => 'http://schemas.openxmlformats.org/officeDocument/2006/relationships', + 'xmlns:m' => 'http://schemas.openxmlformats.org/officeDocument/2006/math', + 'xmlns:v' => 'urn:schemas-microsoft-com:vml', + 'xmlns:wp' => 'http://schemas.openxmlformats.org/drawingml/2006/wordprocessingDrawing', + 'xmlns:w10' => 'urn:schemas-microsoft-com:office:word', + 'xmlns:w' => 'http://schemas.openxmlformats.org/wordprocessingml/2006/main', + 'xmlns:wne' => 'http://schemas.microsoft.com/office/word/2006/wordml', + 'xmlns:sl' => 'http://schemas.openxmlformats.org/schemaLibrary/2006/main', + 'xmlns:a' => 'http://schemas.openxmlformats.org/drawingml/2006/main', + 'xmlns:pic' => 'http://schemas.openxmlformats.org/drawingml/2006/picture', + 'xmlns:c' => 'http://schemas.openxmlformats.org/drawingml/2006/chart', + 'xmlns:lc' => 'http://schemas.openxmlformats.org/drawingml/2006/lockedCanvas', + 'xmlns:dgm' => 'http://schemas.openxmlformats.org/drawingml/2006/diagram' + } + end + + end + end +end +