rubyword 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
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>