rubyword 1.0.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 (51) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +10 -0
  3. data/.rspec +1 -0
  4. data/CHANGELOG.txt +10 -0
  5. data/Gemfile +6 -0
  6. data/LICENSE +22 -0
  7. data/README.md +125 -0
  8. data/Rakefile +4 -0
  9. data/bin/console +14 -0
  10. data/bin/setup +8 -0
  11. data/doc/README.md +1 -0
  12. data/doc/text.md +1 -0
  13. data/lib/rubyword/configuration.rb +5 -0
  14. data/lib/rubyword/document.rb +81 -0
  15. data/lib/rubyword/element/base.rb +12 -0
  16. data/lib/rubyword/element/image.rb +45 -0
  17. data/lib/rubyword/element/link.rb +35 -0
  18. data/lib/rubyword/element/list.rb +35 -0
  19. data/lib/rubyword/element/page_break.rb +26 -0
  20. data/lib/rubyword/element/section.rb +67 -0
  21. data/lib/rubyword/element/text.rb +137 -0
  22. data/lib/rubyword/element/text_break.rb +20 -0
  23. data/lib/rubyword/version.rb +3 -0
  24. data/lib/rubyword/writer/part/base.rb +13 -0
  25. data/lib/rubyword/writer/part/content_types.rb +39 -0
  26. data/lib/rubyword/writer/part/doc_props_app.rb +20 -0
  27. data/lib/rubyword/writer/part/doc_props_core.rb +32 -0
  28. data/lib/rubyword/writer/part/doc_props_custom.rb +16 -0
  29. data/lib/rubyword/writer/part/document.rb +56 -0
  30. data/lib/rubyword/writer/part/font_table.rb +12 -0
  31. data/lib/rubyword/writer/part/footer.rb +50 -0
  32. data/lib/rubyword/writer/part/header.rb +40 -0
  33. data/lib/rubyword/writer/part/numbering.rb +51 -0
  34. data/lib/rubyword/writer/part/rels.rb +26 -0
  35. data/lib/rubyword/writer/part/rels_document.rb +31 -0
  36. data/lib/rubyword/writer/part/settings.rb +93 -0
  37. data/lib/rubyword/writer/part/styles.rb +91 -0
  38. data/lib/rubyword/writer/part/theme.rb +12 -0
  39. data/lib/rubyword/writer/part/toc.rb +89 -0
  40. data/lib/rubyword/writer/part/web_settings.rb +21 -0
  41. data/lib/rubyword/writer/style/base.rb +16 -0
  42. data/lib/rubyword/writer/style/section.rb +65 -0
  43. data/lib/rubyword/writer.rb +87 -0
  44. data/lib/rubyword.rb +9 -0
  45. data/rubyword.gemspec +27 -0
  46. data/sample/test.rb +78 -0
  47. data/spec/rubyword/document_spec.rb +11 -0
  48. data/spec/spec_helper.rb +32 -0
  49. data/template/fontTable.xml +54 -0
  50. data/template/theme.xml +284 -0
  51. metadata +197 -0
@@ -0,0 +1,89 @@
1
+ # -*- encoding : utf-8 -*-
2
+ module Rubyword
3
+ module Writer
4
+ module Part
5
+ module Toc
6
+ def write_toc(rubyword, xml)
7
+ return if rubyword.toc.nil? || !rubyword.toc[:open]
8
+ @xml = xml
9
+ rubyword.sections.each_with_index do |section, section_index|
10
+ next if section.titles.nil?
11
+ section.titles.each_with_index do |title, title_index|
12
+ rid = title[:rid]
13
+ indent = 0
14
+
15
+ @xml.send('w:p') {
16
+ @xml.send('w:pPr') {
17
+ @xml.send('w:tabs') {
18
+ @xml.send('w:tab', 'w:val' => 'right', 'w:leader' => 'dot', 'w:pos' => '9062')
19
+ }
20
+
21
+ # According title size to set tab index, detail: rubyword/element/text.rb
22
+ if title[:indent] > 0
23
+ @xml.send('w:ind', 'w:left' => title[:indent])
24
+ end
25
+ }
26
+
27
+ # initailize in first loop
28
+ if section_index == 0 && title_index == 0
29
+ @xml.send('w:r') {
30
+ @xml.send('w:fldChar', 'w:fldCharType' => 'begin')
31
+ }
32
+
33
+ @xml.send('w:r') {
34
+ @xml.send('w:instrText', {'xml:space' => 'preserve'}, "TOC \\o 1-10 \\h \\z \\u") # What does '1-13' mean?
35
+ }
36
+
37
+ @xml.send('w:r') {
38
+ @xml.send('w:fldChar', 'w:fldCharType' => 'separate')
39
+ }
40
+ end
41
+
42
+ @xml.send('w:hyperlink', 'w:anchor' => "_Toc#{rid}", 'w:history' => 1) {
43
+ @xml.send('w:r') {
44
+ # add font style
45
+ if rubyword.toc[:font_size]
46
+ @xml.send('w:rPr') {
47
+ @xml.send('w:sz', 'w:val' => rubyword.toc[:font_size])
48
+ @xml.send('w:szCs', 'w:val' => rubyword.toc[:font_size])
49
+ }
50
+ end
51
+ # add text
52
+ @xml.send('w:t', title[:text])
53
+ }
54
+
55
+ @xml.send('w:r') {
56
+ @xml.send('w:tab')
57
+ }
58
+
59
+ @xml.send('w:r') {
60
+ @xml.send('fldChar', 'w:fldCharType' => 'begin')
61
+ }
62
+
63
+ @xml.send('w:r') {
64
+ @xml.send('w:instrText', {'xml:space' => 'preserve'}, "PAGEREF _Toc#{rid} \\h")
65
+ }
66
+
67
+ # page numbers
68
+ # @xml.send('w:r') {
69
+ # @xml.send('w:t', 1)
70
+ # }
71
+
72
+ @xml.send('w:r') {
73
+ @xml.send('w:fldChar', 'w:fldCharType' => 'end')
74
+ }
75
+ }
76
+ }
77
+ end
78
+ end
79
+ # end of fldchar
80
+ @xml.send('w:p') {
81
+ @xml.send('w:r') {
82
+ @xml.send('w:fldChar', 'w:fldCharType' => 'end')
83
+ }
84
+ }
85
+ end # end of write
86
+ end
87
+ end
88
+ end
89
+ end
@@ -0,0 +1,21 @@
1
+ # -*- encoding : utf-8 -*-
2
+ module Rubyword
3
+ module Writer
4
+ module Part
5
+ class WebSettings < Base
6
+ def write
7
+ webs = {
8
+ 'xmlns:r' => 'http://schemas.openxmlformats.org/officeDocument/2006/relationships',
9
+ 'xmlns:w' => 'http://schemas.openxmlformats.org/wordprocessingml/2006/main',
10
+ }
11
+ builder = Nokogiri::XML::Builder.new(:encoding => 'UTF-8') do |xml|
12
+ xml.send('w:webSettings', webs){
13
+ xml.send('w:optimizeForBrowser')
14
+ }
15
+ end
16
+ builder.to_xml
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,16 @@
1
+ # -*- encoding : utf-8 -*-
2
+ module Rubyword
3
+ module Writer
4
+ module Style
5
+ class Base
6
+ attr_accessor :rubyword, :style, :section, :xml
7
+ def initialize(section, xml, rubyword)
8
+ @style = section.style
9
+ @section = section
10
+ @xml = xml
11
+ @rubyword = rubyword
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,65 @@
1
+ # -*- encoding : utf-8 -*-
2
+ module Rubyword
3
+ module Writer
4
+ module Style
5
+ class Section < Base
6
+
7
+ MarginsDefault = {
8
+ width: 11870,
9
+ height: 16787,
10
+ margin: 1440,
11
+ gutter: 0,
12
+ header_height: 720,
13
+ footer_height: 720,
14
+ column_count: 1,
15
+ column_spacing: 720
16
+ }.freeze
17
+
18
+ Margins = {
19
+ 'w:top' => MarginsDefault[:margin],
20
+ 'w:right' => MarginsDefault[:margin],
21
+ 'w:bottom' => MarginsDefault[:margin],
22
+ 'w:left' => MarginsDefault[:margin],
23
+ 'w:header' => MarginsDefault[:header_height],
24
+ 'w:footer' => MarginsDefault[:footer_height],
25
+ 'w:gutter' => MarginsDefault[:gutter]
26
+ }
27
+
28
+ Orientation = 'portrait'.freeze
29
+ Landspace = 'landscape'.freeze
30
+
31
+ def write
32
+ @xml.send('w:sectPr') {
33
+ # header or footerReference
34
+
35
+ [@rubyword.header, @rubyword.footer].each do |target|
36
+ next if target.nil?
37
+ @xml.send("w:#{target[:type]}Reference", {
38
+ 'w:type' => 'default',
39
+ 'r:id' => "rId#{target[:rid]}"
40
+ })
41
+ end
42
+
43
+ # pgsize
44
+ @xml.send('w:pgSz', {
45
+ 'w:orient' => Orientation,
46
+ 'w:w' => MarginsDefault[:width],
47
+ 'w:h' => MarginsDefault[:height]
48
+ })
49
+ #borders 暂无
50
+
51
+ # pgMar
52
+ @xml.send('w:pgMar', Margins)
53
+ # cols
54
+ @xml.send('w:cols', 'w:num' => MarginsDefault[:column_count], 'w:space' => MarginsDefault[:column_spacing])
55
+ #Page numbering start 暂无
56
+
57
+ # Line numbering 暂无
58
+
59
+ }
60
+ end
61
+
62
+ end
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,87 @@
1
+ # -*- encoding : utf-8 -*-
2
+ require_relative "writer/part/base"
3
+ require_relative "writer/part/toc"
4
+ require_relative "writer/part/content_types"
5
+ require_relative "writer/part/doc_props_app"
6
+ require_relative "writer/part/doc_props_core"
7
+ require_relative "writer/part/doc_props_custom"
8
+ require_relative "writer/part/document"
9
+ require_relative "writer/part/font_table"
10
+ require_relative "writer/part/numbering"
11
+ require_relative "writer/part/rels_document"
12
+ require_relative "writer/part/rels"
13
+ require_relative "writer/part/settings"
14
+ require_relative "writer/part/styles"
15
+ require_relative "writer/part/theme"
16
+ require_relative "writer/part/web_settings"
17
+ require_relative "writer/part/footer"
18
+ require_relative "writer/part/header"
19
+ # style file
20
+ require_relative 'writer/style/base'
21
+ require_relative 'writer/style/section'
22
+
23
+ module Rubyword
24
+ module Writer
25
+ # word base file
26
+ DocumentBaseFile = {
27
+ 'ContentTypes' => '[Content_Types].xml',
28
+ 'Rels' => '_rels/.rels',
29
+ 'DocPropsApp' => 'docProps/app.xml',
30
+ 'DocPropsCore' => 'docProps/core.xml',
31
+ 'DocPropsCustom' => 'docProps/custom.xml',
32
+ 'RelsDocument' => 'word/_rels/document.xml.rels',
33
+ 'Document' => 'word/document.xml',
34
+ 'Styles' => 'word/styles.xml',
35
+ 'Numbering' => 'word/numbering.xml',
36
+ 'Settings' => 'word/settings.xml',
37
+ 'WebSettings' => 'word/webSettings.xml',
38
+ 'FontTable' => 'word/fontTable.xml',
39
+ 'Theme' => 'word/theme/theme1.xml'
40
+ }.freeze
41
+
42
+ def save(filename = 'document.docx')
43
+ buffer = Zip::OutputStream.write_buffer do |zio|
44
+ write_header_and_footer(zio)
45
+
46
+ # write image
47
+ self.images.each do |image|
48
+ source = open(image[:path]).read
49
+ zio.put_next_entry("word/media/#{image[:filename]}")
50
+ zio.write(source)
51
+ end
52
+
53
+ DocumentBaseFile.each do |helper_method, entry|
54
+ obj = eval "Part::#{helper_method}.new(self)"
55
+ source = obj.write
56
+ zio.put_next_entry(entry)
57
+ zio.write(source)
58
+ end
59
+ end
60
+ file = File.new(filename,'wb')
61
+ file.write(buffer.string)
62
+ file.close
63
+ end
64
+
65
+ def write_header_and_footer(zio)
66
+ header = self.header
67
+ footer = self.footer
68
+ if header
69
+ elmFile = "word/header#{header[:id]}.xml"
70
+ obj = Part::Header.new(self)
71
+ source = obj.write
72
+ zio.put_next_entry(elmFile)
73
+ zio.write(source)
74
+ end
75
+
76
+ if footer
77
+ elmFile = "word/footer#{footer[:id]}.xml"
78
+ obj = Part::Footer.new(self)
79
+ source = obj.write
80
+ zio.put_next_entry(elmFile)
81
+ zio.write(source)
82
+ end
83
+ end
84
+
85
+ end
86
+ end
87
+
data/lib/rubyword.rb ADDED
@@ -0,0 +1,9 @@
1
+ require "nokogiri"
2
+ require "zip"
3
+ require "open-uri"
4
+ require "fastimage"
5
+
6
+ require_relative "rubyword/version"
7
+ require_relative "rubyword/configuration"
8
+ require_relative "rubyword/writer"
9
+ require_relative "rubyword/document"
data/rubyword.gemspec ADDED
@@ -0,0 +1,27 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path("../lib", __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require "rubyword/version"
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "rubyword"
8
+ spec.version = Rubyword::VERSION
9
+ spec.authors = ["young"]
10
+ spec.email = ["youngjy6@gmail.com"]
11
+
12
+ spec.summary = %q{Generate MS Word for ruby}
13
+ spec.description = %q{RubyWord is a simple, efficient Word document generation gem and easy to generate docx file.}
14
+ spec.homepage = "https://github.com/yigger/rubyword"
15
+ spec.license = "MIT"
16
+ spec.files = `git ls-files`.split($/)
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+
20
+ spec.add_development_dependency "bundler", "~> 1.12"
21
+ spec.add_development_dependency "rake", "~> 10.0"
22
+ spec.add_development_dependency "rspec"
23
+ spec.add_development_dependency "nokogiri"
24
+ spec.add_development_dependency "zip"
25
+ spec.add_development_dependency "open-uri"
26
+ spec.add_development_dependency "fastimage"
27
+ end
data/sample/test.rb ADDED
@@ -0,0 +1,78 @@
1
+ require_relative '../lib/rubyword'
2
+ filename = File.join(Rubyword::TEMP_PATH, 'hello.docx')
3
+ Rubyword::Document::generate(filename) {
4
+ # write the doc infomation
5
+ information({
6
+ company: 'ruby word',
7
+ creator: 'young',
8
+ title: 'example word file',
9
+ description: 'this is a example docx',
10
+ subject: 'how to create doc info',
11
+ keywords: 'remark',
12
+ category: 'category'
13
+ })
14
+
15
+ # Generate the directory structure
16
+ title_directory font_size: 24
17
+
18
+ # insert header
19
+ add_header 'rubyword'
20
+
21
+ # insert footer with number
22
+ add_footer nil, text_align: 'center', nums_type: 'number'
23
+ # insert text
24
+ # add_footer 'hello', text_align: 'center'
25
+ # initialize section and insert something in the section
26
+ section {
27
+ # insert title
28
+ title_1 "It's a title", ignore_dir: true
29
+ # insert subtitle
30
+ title_2 "It's a subtitle"
31
+ # insert title
32
+ title_1 'Database'
33
+ # insert subtitle
34
+ title_2 'MySQL'
35
+ # insert No.3 title
36
+ title_3 'NoSQL'
37
+ # text break
38
+ text_break 3
39
+ # insert text
40
+ text 'hello word', bgcolor: 'yellow', text_align: 'center'
41
+ # page break
42
+ page_break 2
43
+ # insert text
44
+ text 'hello word', indent_between: '1440-1440'
45
+ text 'title', { font_size: 62, color: '996699', blod: true, text_align: 'center' }
46
+
47
+ # insert title
48
+ title_1 'section2 title'
49
+ title_2 'section2 title'
50
+ title_3 'section2 title'
51
+
52
+ # add a link
53
+ link 'baidu', 'http://www.baidu.com', text_align: 'center'
54
+ }
55
+
56
+ section {
57
+ # insert a text
58
+ text 'another Section', bgcolor: 'yellow', text_align: 'center'
59
+
60
+ # insert a text
61
+ text 'hello word', indent_between: '1440-1440'
62
+ text 'title', { font_size: 62, color: '996699', blod: true, text_align: 'center' }
63
+ }
64
+
65
+ section {
66
+ list 'test1', 1
67
+ list 'test1', 2
68
+ list 'test3', 2
69
+ list 'test2', 1
70
+ list 'test2', 1
71
+ }
72
+
73
+ section {
74
+ # add a link
75
+ link 'baidu', 'http://www.baidu.com', text_align: 'center'
76
+ image 'http://www.baidu.com/img/bd_logo1.png'
77
+ }
78
+ }
@@ -0,0 +1,11 @@
1
+ require "spec_helper"
2
+ module Rubyword
3
+ describe Document do
4
+ describe "#create section" do
5
+ let(:document) { Rubyword::Document.new }
6
+ it "return a document class" do
7
+
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,32 @@
1
+ require "rubyword"
2
+ RSpec.configure do |config|
3
+ # rspec-expectations config goes here. You can use an alternate
4
+ # assertion/expectation library such as wrong or the stdlib/minitest
5
+ # assertions if you prefer.
6
+ config.expect_with :rspec do |expectations|
7
+ # This option will default to `true` in RSpec 4. It makes the `description`
8
+ # and `failure_message` of custom matchers include text for helper methods
9
+ # defined using `chain`, e.g.:
10
+ # be_bigger_than(2).and_smaller_than(4).description
11
+ # # => "be bigger than 2 and smaller than 4"
12
+ # ...rather than:
13
+ # # => "be bigger than 2"
14
+ expectations.include_chain_clauses_in_custom_matcher_descriptions = true
15
+ end
16
+
17
+ # rspec-mocks config goes here. You can use an alternate test double
18
+ # library (such as bogus or mocha) by changing the `mock_with` option here.
19
+ config.mock_with :rspec do |mocks|
20
+ # Prevents you from mocking or stubbing a method that does not exist on
21
+ # a real object. This is generally recommended, and will default to
22
+ # `true` in RSpec 4.
23
+ mocks.verify_partial_doubles = true
24
+ end
25
+
26
+ # This option will default to `:apply_to_host_groups` in RSpec 4 (and will
27
+ # have no way to turn it off -- the option exists only for backwards
28
+ # compatibility in RSpec 3). It causes shared context metadata to be
29
+ # inherited by the metadata hash of host groups and examples, rather than
30
+ # triggering implicit auto-inclusion in groups with matching metadata.
31
+ config.shared_context_metadata_behavior = :apply_to_host_groups
32
+ end
@@ -0,0 +1,54 @@
1
+ <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
2
+ <w:fonts
3
+ xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships"
4
+ xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main">
5
+ <w:font w:name="Times New Roman">
6
+ <w:panose1 w:val="02020603050405020304" />
7
+ <w:charset w:val="00" />
8
+ <w:family w:val="roman" />
9
+ <w:pitch w:val="variable" />
10
+ <w:sig w:usb0="E0002AFF" w:usb1="C0007841" w:usb2="00000009" w:usb3="00000000" w:csb0="000001FF" w:csb1="00000000" />
11
+ </w:font>
12
+ <w:font w:name="Courier New">
13
+ <w:panose1 w:val="02070309020205020404" />
14
+ <w:charset w:val="00" />
15
+ <w:family w:val="modern" />
16
+ <w:pitch w:val="fixed" />
17
+ <w:sig w:usb0="E0002AFF" w:usb1="C0007843" w:usb2="00000009" w:usb3="00000000" w:csb0="000001FF" w:csb1="00000000" />
18
+ </w:font>
19
+ <w:font w:name="Wingdings">
20
+ <w:panose1 w:val="05000000000000000000" />
21
+ <w:charset w:val="02" />
22
+ <w:family w:val="auto" />
23
+ <w:pitch w:val="variable" />
24
+ <w:sig w:usb0="00000000" w:usb1="10000000" w:usb2="00000000" w:usb3="00000000" w:csb0="80000000" w:csb1="00000000" />
25
+ </w:font>
26
+ <w:font w:name="Symbol">
27
+ <w:panose1 w:val="05050102010706020507" />
28
+ <w:charset w:val="02" />
29
+ <w:family w:val="roman" />
30
+ <w:pitch w:val="variable" />
31
+ <w:sig w:usb0="00000000" w:usb1="10000000" w:usb2="00000000" w:usb3="00000000" w:csb0="80000000" w:csb1="00000000" />
32
+ </w:font>
33
+ <w:font w:name="Arial">
34
+ <w:panose1 w:val="020B0604020202020204" />
35
+ <w:charset w:val="00" />
36
+ <w:family w:val="swiss" />
37
+ <w:pitch w:val="variable" />
38
+ <w:sig w:usb0="E0002AFF" w:usb1="C0007843" w:usb2="00000009" w:usb3="00000000" w:csb0="000001FF" w:csb1="00000000" />
39
+ </w:font>
40
+ <w:font w:name="Cambria">
41
+ <w:panose1 w:val="02040503050406030204" />
42
+ <w:charset w:val="00" />
43
+ <w:family w:val="roman" />
44
+ <w:pitch w:val="variable" />
45
+ <w:sig w:usb0="A00002EF" w:usb1="4000004B" w:usb2="00000000" w:usb3="00000000" w:csb0="0000019F" w:csb1="00000000" />
46
+ </w:font>
47
+ <w:font w:name="Calibri">
48
+ <w:panose1 w:val="020F0502020204030204" />
49
+ <w:charset w:val="00" />
50
+ <w:family w:val="swiss" />
51
+ <w:pitch w:val="variable" />
52
+ <w:sig w:usb0="E10002FF" w:usb1="4000ACFF" w:usb2="00000009" w:usb3="00000000" w:csb0="0000019F" w:csb1="00000000" />
53
+ </w:font>
54
+ </w:fonts>