
Security News
New Website “Is It Really FOSS?” Tracks Transparency in Open Source Distribution Models
A new site reviews software projects to reveal if they’re truly FOSS, making complex licensing and distribution models easy to understand.
= RODS - Ruby Open Document Spreadsheet This class provides a convenient interface for reading and writing spreadsheets conforming to Open Document Format v1.1. Installiation of an office-application (LibreOffice, OpenOffice.org) is not required as the code directly manipulates the XML-files in the zipped *.ods-container. The mere prerequisites are gem install zip gem install rods
On my website http://ruby.homelinux.com/ruby/rods/ you can see the output of the script below. You can contact me at rods.ruby@online.de (and tell me about experiences, problems you encountered or drop me a line, if you like it ;-).
link:images/Rods.jpg
= Copyright Copyright (c) Dr. Heinz Breinlinger (2011). Licensed under the same terms as Ruby. No warranty is provided.
= Changelog
= Disclaimer
The example code was tested on
= What you must know When you open a (new) spreadsheet in an application like OpenOffice.org, you'll see an infinity of empty cells and might be tempted to assume that all these cells actually "exist" in the underlying file format/XML-structure. This is not the case ! A "newly born" spreadsheet does not contain even a single cell ! They're created on demand. That is: If you write a cell with coordinates (10,10) into a new sheet only 2 rows and 2 cells are created
This is very important for using the interface appropriately: All routines matching 'Existent' merely return "living" cells, while all other routines bearing a "get|Get" create a row or cell if necessary. In other words: Use
= Howto $ sudo gem install rods (root-privileges necessary)
require 'rubygems' require 'rods'
#-----------------------------------------------------------------
#----------------------------------------------------------------- mySheet=Rods.new("Template.ods") # empty or prefilled sheet #-----------------------------------------------------------------
#-----------------------------------------------------------------
mySheet.renameTable("Tabelle1","not needed")
mySheet.insertTable("example")
#-----------------------------------------------------------------
#----------------------------------------------------------------- mySheet.setCurrentTable("example") mySheet.deleteTable("Tabelle2") mySheet.deleteTable("Tabelle3") #-----------------------------------------------------------------
#----------------------------------------------------------------- mySheet.writeCell(1,1,"date","31.12.2010") mySheet.writeCell(2,1,"formula:date","=A1+1") #-----------------------------------------------------------------
#----------------------------------------------------------------- mySheet.setDateFormat("myDateDay") # "05.01.2011" -> "Mi" (if German) #-----------------------------------------------------------------
#----------------------------------------------------------------- mySheet.writeCell(1,2,"formula:date","=A1") mySheet.writeCell(2,2,"formula:date","=A2") #-----------------------------------------------------------------
#----------------------------------------------------------------- mySheet.setDateFormat("myDate") # back to "DD.MM.YYYY" #-----------------------------------------------------------------
#----------------------------------------------------------------- mySheet.writeCell(1,3,"time","13:37") mySheet.writeCell(2,3,"time","20:15") #-----------------------------------------------------------------
#----------------------------------------------------------------- mySheet.writeCell(1,4,"currency","19,99") mySheet.writeCell(2,4,"currency","-7,78") #-----------------------------------------------------------------
#----------------------------------------------------------------- cell=mySheet.writeGetCell(3,3,"formula:time","=C2-C1") #-----------------------------------------------------------------
#-----------------------------------------------------------------
mySheet.setAttributes(cell,{ "border" => "0.01cm solid turquoise",
"text-align" => "center",
"background-color" => "yellow2",
"color" => "blue"})
#-----------------------------------------------------------------
#----------------------------------------------------------------- cell=mySheet.writeGetCell(3,4,"formula:currency","=D2+D1") #-----------------------------------------------------------------
#----------------------------------------------------------------- mySheet.setAttributes(cell,{ "border-right" => "0.05cm solid magenta4", "border-bottom" => "0.03cm solid lightgreen", "border-top" => "0.08cm solid salmon", "font-style" => "italic", "font-weight" => "bold"}) #-----------------------------------------------------------------
#-----------------------------------------------------------------
mySheet.writeStyleAbbr({"name" => "myNewPercentStyle",
"margin-left" => "0.3cm",
"text-align" => "start",
"color" => "blue",
"border" => "0.01cm solid black",
"font-style" => "italic",
"data-style-name" => "myPercentFormat", # <- data-style !
"font-weight" => "bold"})
cell=mySheet.writeGetCell(4,2,"percent","4,71")
mySheet.setStyle(cell,"myNewPercentStyle")
#-----------------------------------------------------------------
#----------------------------------------------------------------- mySheet.writeComment(cell,"by Dr. Heinz Breinlinger") cell=mySheet.writeGetCell(4,3,"formula:time","=B4*C3") mySheet.setAttributes(cell,{ "color" => "lightmagenta", "font-style" => "italic"}) #-----------------------------------------------------------------
#----------------------------------------------------------------- cell=mySheet.writeGetCell(4,4,"formula:currency","=B4*D3") #-----------------------------------------------------------------
#----------------------------------------------------------------- mySheet.setAttributes(cell,{ "color" => "turquoise7", "text-align" => "center", "font-style" => "bold"}) #-----------------------------------------------------------------
#----------------------------------------------------------------- mySheet.writeStyleAbbr({"name" => "myBold", "text-align" => "end", "font-weight" => "bold", "background-color" => "purple"}) cell=mySheet.writeGetCell(3,1,"string","Diff/Sum") mySheet.setStyle(cell,"myBold") cell=mySheet.writeGetCell(4,1,"string","Percent") mySheet.setStyle(cell,"myBold") #-----------------------------------------------------------------
#----------------------------------------------------------------- cell=mySheet.writeGetCell(6,1,"string","annotation") mySheet.writeComment(cell,"C3,C4,D3,D4 are formulas") #-----------------------------------------------------------------
#----------------------------------------------------------------- 1.upto(7){ |row| cell=mySheet.getCell(row,5) mySheet.setAttributes(cell,{ "border-right" => "0.07cm solid green6" }) } #-----------------------------------------------------------------
#----------------------------------------------------------------- 1.upto(5){ |col| cell=mySheet.getCell(7,col) mySheet.setAttributes(cell,{ "border-bottom" => "0.085cm solid red5" }) # } #-----------------------------------------------------------------
#----------------------------------------------------------------- amount=0.0 1.upto(2){ |i| row=mySheet.getRow(i) text,type=mySheet.readCellFromRow(row,4) if(type == "currency") amount+=text.to_f end } #-----------------------------------------------------------------
#----------------------------------------------------------------- mySheet.deleteTable("not needed") #-----------------------------------------------------------------
#----------------------------------------------------------------- mySheet.saveAs("Example.ods") #-----------------------------------------------------------------
#----------------------------------------------------------------- puts("Sums up to: #{amount}") # -> "Sums up to: 12.21" #-----------------------------------------------------------------
#----------------------------------------------------------------- mySheet=Rods.new("Example.ods") #-----------------------------------------------------------------
#----------------------------------------------------------------- mySheet.setCurrentTable("example") #-----------------------------------------------------------------
#----------------------------------------------------------------- text,type=mySheet.readCell(2,4) if(text && type) if(type == "currency") puts("not so much: #{text} bucks") # -> "not so much: -7.78 bucks" end end
= Example 0.9.0 a
#!/usr/bin/ruby
require 'rubygems' require 'rods'
mySheet=Rods.new("Template.ods") 1.upto(3){ |row| 1.upto(4){ |col| mySheet.writeCell(row,col,"time","13:47") } } mySheet.insertColumn(3) 1.upto(3){ |row| mySheet.writeCell(row,3,"string","o' clock") } mySheet.insertRow(2) cell=mySheet.insertCell(3,3) mySheet.writeText(cell,"string","insertCell") row=mySheet.getRow(4) row=mySheet.insertRowAbove(row) mySheet.writeCell(4,1,"string","Willi") cell=mySheet.insertCellFromRow(row,1) mySheet.writeText(cell,"string","Supi") mySheet.saveAs("Test8.ods")
= Example 0.9.0 b
#!/usr/bin/ruby
require 'rubygems' require 'rods'
mySheet=Rods.new("Template.ods") mySheet.writeCell(1,1,"string","oneone") cell=mySheet.writeGetCell(1,2,"string","onetwo") mySheet.deleteCellBefore(cell) cell=mySheet.writeGetCell(1,5,"string","onefive") mySheet.deleteCellBefore(cell)
cellOne=mySheet.writeGetCell(5,1,"string","fiveone") cellFive=mySheet.writeGetCell(5,5,"string","fivefive") cellSix=mySheet.writeGetCell(5,6,"string","fivesix") cellNine=mySheet.writeGetCell(5,9,"string","fivenine") mySheet.deleteCellAfter(cellOne) mySheet.deleteCellAfter(cellFive) cell=mySheet.getCell(5,4) mySheet.deleteCellBefore(cell) cell=mySheet.getCell(5,4) mySheet.deleteCellAfter(cell) mySheet.deleteCell(5,2) mySheet.deleteCell(5,3) row=mySheet.getRow(5) mySheet.deleteRowAbove(row) mySheet.insertRow(2) mySheet.insertRow(2) row=mySheet.getRow(2) mySheet.deleteRowBelow(row) row=mySheet.getRow(1) mySheet.deleteRowBelow(row) row=mySheet.getRow(2) mySheet.deleteRowBelow(row) mySheet.deleteRow(2) mySheet.deleteRow(1) mySheet.deleteRow(1) mySheet.saveAs("Test9.ods")
= Example 0.9.0 c
#!/usr/bin/ruby
require 'rubygems' require 'rods'
mySheet=Rods.new("Konten.ods") startRow=mySheet.getRow(11) i=0 while(row=mySheet.getNextExistentRow(startRow)) i+=1 puts("#{i}") mySheet.deleteRow2(row) end mySheet.saveAs("Test10.ods")
= Example 0.9.0 d
#!/usr/bin/ruby
require 'rubygems' require 'rods'
mySheet=Rods.new("Konten.ods") mySheet.deleteColumn(8) mySheet.saveAs("Test11.ods")
= Example 0.9.0 e
#!/usr/bin/ruby
require 'rubygems' require 'rods'
mySheet=Rods.new("Konten.ods") startCell=mySheet.getCell(12,1) i=0 while(cell=mySheet.getNextExistentCell(startCell)) i+=1 puts("#{i}") mySheet.deleteCell2(cell) end mySheet.saveAs("Test12.ods")
= Example 0.8.0
#!/usr/bin/ruby
require 'rubygems' require 'rods'
mySheet=Rods.new("Template.ods") 1.upto(3){ |row| 1.upto(4){ |col| mySheet.writeCell(row,col,"time","13:47") } } #-----------------------------------------------------
#----------------------------------------------------- mySheet.insertColumn(3) 1.upto(3){ |row| mySheet.writeCell(row,3,"string","o' clock") } mySheet.saveAs("Test7.ods")
= Example 0.7.5
#!/usr/bin/ruby
require 'rubygems' require 'rods'
mySheet=Rods.new("Template.ods") mySheet.writeCell(1,1,"string","oneOne") mySheet.writeCell(1,2,"string","oneTwo") mySheet.writeCell(2,1,"string","twoOne") # finally becomes cell 3,1 mySheet.writeCell(2,2,"string","twoTwo") # finally becomes cell 3,2 row=mySheet.getRow(1) newRow=mySheet.insertRowBelow(row) mySheet.writeCell(2,1,"string","twoNewOne") cell=mySheet.writeGetCell(2,2,"string","moved") # finally becomes cell "2,3" mySheet.insertCellBefore(cell) mySheet.writeCell(2,2,"string","twoNewTwoNew") # new cell "2,2" mySheet.saveAs("Test6.ods")
= Example 0.7.0
#!/usr/bin/ruby
require 'rubygems' require 'rods'
mySheet=Rods.new("Konten.ods")
#------------------------------------------------------------------------- result=mySheet.getCellsAndIndicesFor('\d{1}[.,]\d{2}') result.each{ |cellHash| puts("----------------------------------------------") puts("Node: #{cellHash[:cell]}") # Be aware: Prints out whole node ! ;-) puts("Row: #{cellHash[:row]}") puts("Column: #{cellHash[:col]}") } puts("done")
= Example for additions in 0.6.2
#!/usr/bin/ruby
require 'rubygems' require 'rods'
mySheet=Rods.new("Template.ods") cell=mySheet.writeGetCell(3,3,"string","Underline") mySheet.setAttributes(cell,{ "style:text-underline-color" => "blue", "style:text-underline-style" => "solid", "style:text-underline-width" => "auto"}) cell=mySheet.writeGetCell(4,4,"string","Underline_Default_with_Black") #----------------------------------------------------------------------
#---------------------------------------------------------------------- mySheet.setAttributes(cell,{ "style:text-underline-style" => "solid" }) mySheet.saveAs("Test3.ods") puts("done")
= Example for additions in 0.6.1
#!/usr/bin/ruby
require 'rubygems' require 'rods'
mySheet=Rods.new("Template.ods") mySheet.insertTableAfter("Tabelle1","Neue Tabelle") mySheet.insertTableAfter("Neue Tabelle","Neue Tabelle2") mySheet.insertTableAfter("Neue Tabelle2","Neue Tabelle3") mySheet.insertTableAfter("Tabelle3","Neue Tabelle4") mySheet.insertTableBefore("Tabelle1","Vor1") mySheet.insertTableBefore("Neue Tabelle4","Vor4") mySheet.saveAs("Test2.ods") puts("done")
= Caveat
The XML-structure of a .ods is
As a result
1.upto(500){ |i| text1,type1=readCell(i,3) # XML-Parser starts from the top-node text2,type2=readCell(i,4) # XML-Parser starts from the top-node puts("Read #{text1} of #{type1} and #{text2} of #{type2}") }
is significantly slower than the slight variation
1.upto(500){ |i| row=getRow(i) # XML-Parser starts from top-node text1,type1=readCellFromRow(row,3) # XML-Parser continues from row-node text2,type2=readCellFromRow(row,4) # XML-Parser continues from row-node puts("Read #{text1} of #{type1} and #{text2} of #{type2} }
This difference hardly matters while dealing with small documents, but degrades performance significantly when you move up or down a sheet with a lot of rows and process several cells on the same row !
Provided you just want to read exisiting cells (cf. explanation above), the following is a real speed-buster.
require 'rubygems' require 'rods'
mySheet=Rods.new("Konten.ods") sum=0.0 i=0 row=mySheet.getRow(1) #---------------------------------------------------------------------------------
#---------------------------------------------------------------------------------
while(row=mySheet.getNextExistentRow(row))
i+=1
puts("#{i}")
confirmed,type=mySheet.readCellFromRow(row,1)
next if (! confirmed || confirmed.empty?())
account,type=mySheet.readCellFromRow(row,9)
next if (! account || account.empty?())
transfer,type=mySheet.readCellFromRow(row,7)
next if (! transfer || transfer.empty?())
if(confirmed.match(/x/i) && account.match(/Hauskonto/))
puts("#{i}: --> #{transfer} €")
sum+=transfer.to_f
end
end
puts("------------")
puts("Sum: #{sum}")
On the ATOM-Nettop I developed the gem, even the first script above took just 2-3 seconds and on my Core-i7-Notebook it was finished so quickly that I supposed it had halted on an immediate error, so: don't be concerned and just experiment :-).
The same considerations apply for the family of 'delete'-functions added in version 0.9.0 ! See the examples above.
= Standards
FAQs
Unknown package
We found that rods demonstrated a not healthy version release cadence and project activity because the last version was released a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Security News
A new site reviews software projects to reveal if they’re truly FOSS, making complex licensing and distribution models easy to understand.
Security News
Astral unveils pyx, a Python-native package registry in beta, designed to speed installs, enhance security, and integrate deeply with uv.
Security News
The Latio podcast explores how static and runtime reachability help teams prioritize exploitable vulnerabilities and streamline AppSec workflows.