summaryrefslogtreecommitdiffhomepage
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/axlsx/util/validators.rb29
-rw-r--r--lib/axlsx/workbook/workbook.rb2
-rw-r--r--lib/axlsx/workbook/worksheet/conditional_formatting.rb69
-rw-r--r--lib/axlsx/workbook/worksheet/conditional_formatting_rule.rb169
-rw-r--r--lib/axlsx/workbook/worksheet/worksheet.rb21
5 files changed, 288 insertions, 2 deletions
diff --git a/lib/axlsx/util/validators.rb b/lib/axlsx/util/validators.rb
index 54a0aeb4..14d34d36 100644
--- a/lib/axlsx/util/validators.rb
+++ b/lib/axlsx/util/validators.rb
@@ -96,6 +96,35 @@ module Axlsx
:darkTrellis, :lightHorizontal, :lightVertical, :lightDown, :lightUp, :lightGrid, :lightTrellis, :gray125, :gray0625], v
end
+ # Requires that the value is one of the ST_TimePeriod types
+ # valid time period types are today, yesterday, tomorrow, last7Days,
+ # thisMonth, lastMonth, nextMonth, thisWeek, lastWeek, nextWeek
+ def self.validate_time_period_type(v)
+ RestrictionValidator.validate :time_period_type, [:today, :yesterday, :tomorrow, :last7Days, :thisMonth, :lastMonth, :nextMonth, :thisWeek, :lastWeek, :nextWeek], v
+
+
+ end
+
+ # Requires that the value is valid conditional formatting type.
+ # valid types must be one of expression, cellIs, colorScale,
+ # dataBar, iconSet, top10, uniqueValues, duplicateValues,
+ # containsText, notContainsText, beginsWith, endsWith,
+ # containsBlanks, notContainsBlanks, containsErrors,
+ # notContainsErrors, timePeriod, aboveAverage
+ # @param [Any] v The value validated
+ def self.validate_conditional_formatting_type(v)
+ RestrictionValidator.validate :conditional_formatting_type, [:expression, :cellIs, :colorScale, :dataBar, :iconSet, :top10, :uniqueValues, :duplicateValues, :containsText, :notContainsText, :beginsWith, :endsWith, :containsBlanks, :notContainsBlanks, :containsErrors, :notContainsErrors, :timePeriod, :aboveAverage], v
+ end
+
+ # Requires that the value is valid conditional formatting operator.
+ # valid operators must be one of lessThan, lessThanOrEqual, equal,
+ # notEqual, greaterThanOrEqual, greaterThan, between, notBetween,
+ # containsText, notContains, beginsWith, endsWith
+ # @param [Any] v The value validated
+ def self.validate_conditional_formatting_operator(v)
+ RestrictionValidator.validate :conditional_formatting_type, [:lessThan, :lessThanOrEqual, :equal, :notEqual, :greaterThanOrEqual, :greaterThan, :between, :notBetween, :containsText, :notContains, :beginsWith, :endsWith], v
+ end
+
# Requires that the value is a gradient_type.
# valid types are :linear and :path
# @param [Any] v The value validated
diff --git a/lib/axlsx/workbook/workbook.rb b/lib/axlsx/workbook/workbook.rb
index ce4e161b..2d406890 100644
--- a/lib/axlsx/workbook/workbook.rb
+++ b/lib/axlsx/workbook/workbook.rb
@@ -4,6 +4,8 @@ module Axlsx
require 'axlsx/workbook/worksheet/date_time_converter.rb'
require 'axlsx/workbook/worksheet/cell.rb'
require 'axlsx/workbook/worksheet/page_margins.rb'
+require 'axlsx/workbook/worksheet/conditional_formatting.rb'
+require 'axlsx/workbook/worksheet/conditional_formatting_rule.rb'
require 'axlsx/workbook/worksheet/row.rb'
require 'axlsx/workbook/worksheet/col.rb'
require 'axlsx/workbook/worksheet/worksheet.rb'
diff --git a/lib/axlsx/workbook/worksheet/conditional_formatting.rb b/lib/axlsx/workbook/worksheet/conditional_formatting.rb
new file mode 100644
index 00000000..01a1d719
--- /dev/null
+++ b/lib/axlsx/workbook/worksheet/conditional_formatting.rb
@@ -0,0 +1,69 @@
+module Axlsx
+ # Conditional formatting allows styling of ranges based on functions
+ #
+ # @note The recommended way to manage conditional formatting is via Worksheet#add_conditional_formatting
+ # @see Worksheet#add_conditional_formatting
+ class ConditionalFormatting
+
+ # Sqref
+ # Range over which the formatting is applied
+ # @return [String]
+ attr_reader :sqref
+
+ # Rules
+ # Rules to apply the formatting to. Can be either a hash of
+ # options for one ConditionalFormattingRule, an array of hashes
+ # for multiple ConditionalFormattingRules, or an array of already
+ # created ConditionalFormattingRules.
+ # @return [Array]
+ attr_reader :rules
+
+ # Creates a new ConditionalFormatting object
+ # @option options [Array] rules The rules to apply
+ # @option options [String] sqref The range to apply the rules to
+ def initialize(options={})
+ @rules = []
+ options.each do |o|
+ self.send("#{o[0]}=", o[1]) if self.respond_to? "#{o[0]}="
+ end
+ end
+
+ # Add ConditionalFormattingRules to this object. Rules can either
+ # be already created objects or hashes of options for automatic
+ # creation.
+ # @option rules [Array, Hash] the rules apply, can be just one in hash form
+ # @see ConditionalFormattingRule#initialize
+ def add_rules(rules)
+ rules = [rules] if rules.is_a? Hash
+ conditional_rules = rules.each do |rule|
+ add_rule rule
+ end
+ end
+
+ # Add a ConditionalFormattingRule. If a hash of options is passed
+ # in create a rule on the fly
+ # @option rule [ConditionalFormattingRule, Hash] A rule to create
+ # @see ConditionalFormattingRule#initialize
+ def add_rule(rule)
+ if rule.is_a? Axlsx::ConditionalFormattingRule
+ @rules << rule
+ elsif rule.is_a? Hash
+ @rules << ConditionalFormattingRule.new(rule)
+ end
+ end
+
+ # @see rules
+ def rules=(v); @rules = v end
+ # @see sqref
+ def sqref=(v); Axlsx::validate_string(v); @sqref = v end
+
+ # Serializes the conditional formatting element
+ # @param [String] str
+ # @return [String]
+ def to_xml_string(str = '')
+ str << '<conditionalFormatting sqref="' << sqref << '">'
+ str << rules.collect{ |rule| rule.to_xml_string }.join(' ')
+ str << '</conditionalFormatting>'
+ end
+ end
+end
diff --git a/lib/axlsx/workbook/worksheet/conditional_formatting_rule.rb b/lib/axlsx/workbook/worksheet/conditional_formatting_rule.rb
new file mode 100644
index 00000000..bddea5a5
--- /dev/null
+++ b/lib/axlsx/workbook/worksheet/conditional_formatting_rule.rb
@@ -0,0 +1,169 @@
+module Axlsx
+ # Conditional formatting rules specify formulas whose evaluations
+ # format cells
+ #
+ # @note The recommended way to manage these rules is via Worksheet#add_conditional_formatting
+ # @see Worksheet#add_conditional_formatting
+ class ConditionalFormattingRule
+ @@child_elements = [:formula]
+
+ # Formula
+ # @return [String]
+ attr_reader :formula
+
+ # Type (ST_CfType)
+ # options are expression, cellIs, colorScale, dataBar, iconSet,
+ # top10, uniqueValues, duplicateValues, containsText,
+ # notContainsText, beginsWith, endsWith, containsBlanks,
+ # notContainsBlanks, containsErrors, notContainsErrors,
+ # timePeriod, aboveAverage
+ # @return [Symbol]
+ attr_reader :type
+
+ # Above average rule
+ # Indicates whether the rule is an "above average" rule. True
+ # indicates 'above average'. This attribute is ignored if type is
+ # not equal to aboveAverage.
+ # @return [Boolean]
+ attr_reader :aboveAverage
+
+ # Bottom N rule
+ # @return [Boolean]
+ attr_reader :bottom
+
+ # Differential Formatting Id
+ # @return [Integer]
+ attr_reader :dxfId
+
+ # Equal Average
+ # Flag indicating whether the 'aboveAverage' and 'belowAverage'
+ # criteria is inclusive of the average itself, or exclusive of
+ # that value.
+ # @return [Boolean]
+ attr_reader :equalAverage
+
+ # Operator
+ # The operator in a "cell value is" conditional formatting
+ # rule. This attribute is ignored if type is not equal to cellIs
+ #
+ # Operator must be one of lessThan, lessThanOrEqual, equal,
+ # notEqual, greaterThanOrEqual, greaterThan, between, notBetween,
+ # containsText, notContains, beginsWith, endsWith
+ # @return [Symbol]
+ attr_reader :operator
+
+ # Priority
+ # The priority of this conditional formatting rule. This value is
+ # used to determine which format should be evaluated and
+ # rendered. Lower numeric values are higher priority than higher
+ # numeric values, where '1' is the highest priority.
+ # @return [Integer]
+ attr_reader :priority
+
+ # Text
+ # The text value in a "text contains" conditional formatting
+ # rule.
+ # @return [String]
+ attr_reader :text
+
+ # percent (Top 10 Percent)
+ # Indicates whether a "top/bottom n" rule is a "top/bottom n
+ # percent" rule. This attribute is ignored if type is not equal to
+ # top10.
+ # @return [Boolean]
+ attr_reader :percent
+
+ # rank (Rank)
+ # The value of "n" in a "top/bottom n" conditional formatting
+ # rule. This attribute is ignored if type is not equal to top10.
+ # @return [Integer]
+ attr_reader :rank
+
+ # stdDev (StdDev)
+ # The number of standard deviations to include above or below the
+ # average in the conditional formatting rule. This attribute is
+ # ignored if type is not equal to aboveAverage. If a value is
+ # present for stdDev and the rule type = aboveAverage, then this
+ # rule is automatically an "above or below N standard deviations"
+ # rule.
+ # @return [Integer]
+ attr_reader :stdDev
+
+ # stopIfTrue (Stop If True)
+ # If this flag is '1', no rules with lower priority shall be
+ # applied over this rule, when this rule evaluates to true.
+ # @return [Boolean]
+ attr_reader :stopIfTrue
+
+ # timePeriod (Time Period)
+ # The applicable time period in a "date occurring…" conditional
+ # formatting rule. This attribute is ignored if type is not equal
+ # to timePeriod.
+ # Valid types are today, yesterday, tomorrow, last7Days,
+ # thisMonth, lastMonth, nextMonth, thisWeek, lastWeek, nextWeek
+ attr_reader :timePeriod
+
+ # Creates a new Conditional Formatting Rule object
+ # @option options [Symbol] type The type of this formatting rule
+ # @option options [Boolean] aboveAverage This is an aboveAverage rule
+ # @option options [Boolean] bottom This is a bottom N rule.
+ # @option options [Integer] dxfId The formatting id to apply to matches
+ # @option options [Boolean] equalAverage Is the aboveAverage or belowAverage rule inclusive
+ # @option options [Integer] priority The priority of the rule, 1 is highest
+ # @option options [Symbol] operator Which operator to apply
+ # @option options [String] text The value to apply a text operator against
+ # @option options [Boolean] percent If a top/bottom N rule, evaluate as N% rather than N
+ # @option options [Integer] rank If a top/bottom N rule, the value of N
+ # @option options [Integer] stdDev The number of standard deviations above or below the average to match
+ # @option options [Boolean] stopIfTrue Stop evaluating rules after this rule matches
+ # @option options [Symbol] timePeriod The time period in a date occuring... rule
+ # @option options [String] formula The formula to match against in i.e. an equal rule
+ def initialize(options={})
+ options.each do |o|
+ self.send("#{o[0]}=", o[1]) if self.respond_to? "#{o[0]}="
+ end
+ end
+
+ # @see type
+ def type=(v); Axlsx::validate_conditional_formatting_type(v); @type = v end
+ # @see aboveAverage
+ def aboveAverage=(v); Axlsx::validate_boolean(v); @aboveAverage = v end
+ # @see bottom
+ def bottom=(v); Axlsx::validate_boolean(v); @bottom = v end
+ # @see dxfId
+ def dxfId=(v); Axlsx::validate_unsigned_numeric(v); @dxfId = v end
+ # @see equalAverage
+ def equalAverage=(v); Axlsx::validate_boolean(v); @equalAverage = v end
+ # @see priority
+ def priority=(v); Axlsx::validate_unsigned_numeric(v); @priority = v end
+ # @see operator
+ def operator=(v); Axlsx::validate_conditional_formatting_operator(v); @operator = v end
+ # @see text
+ def text=(v); Axlsx::validate_string(v); @text = v end
+ # @see percent
+ def percent=(v); Axlsx::validate_boolean(v); @percent = v end
+ # @see rank
+ def rank=(v); Axlsx::validate_unsigned_numeric(v); @rank = v end
+ # @see stdDev
+ def stdDev=(v); Axlsx::validate_unsigned_numeric(v); @stdDev = v end
+ # @see stopIfTrue
+ def stopIfTrue=(v); Axlsx::validate_boolean(v); @stopIfTrue = v end
+ # @see timePeriod
+ def timePeriod=(v); Axlsx::validate_time_period_type(v); @timePeriod = v end
+ # @see formula
+ def formula=(v); Axlsx::validate_string(v); @formula = v end
+
+ # Serializes the conditional formatting rule
+ # @param [String] str
+ # @return [String]
+ def to_xml_string(str = '')
+ str << '<cfRule '
+ str << instance_values.map { |key, value| '' << key << '="' << value.to_s << '"' unless @@child_elements.include?(key.to_sym) }.join(' ')
+ str << '>'
+ @@child_elements.each do |el|
+ str << "<#{el}>" << self.send(el) << "</#{el}>" if self.send(el)
+ end
+ str << '</cfRule>'
+ end
+ end
+end
diff --git a/lib/axlsx/workbook/worksheet/worksheet.rb b/lib/axlsx/workbook/worksheet/worksheet.rb
index 6433b9bc..05b13195 100644
--- a/lib/axlsx/workbook/worksheet/worksheet.rb
+++ b/lib/axlsx/workbook/worksheet/worksheet.rb
@@ -1,4 +1,3 @@
-# encoding: UTF-8
module Axlsx
# The Worksheet class represents a worksheet in the workbook.
@@ -101,7 +100,8 @@ module Axlsx
@drawing = @page_margins = @auto_filter = nil
@merged_cells = []
@auto_fit_data = []
-
+ @conditional_formattings = []
+
@selected = false
@show_gridlines = true
self.name = "Sheet" + (index+1).to_s
@@ -123,6 +123,20 @@ module Axlsx
rows.flatten
end
+ # Add conditinoal formatting to this worksheet.
+ #
+ # @option cells [String] The range to apply the formatting to
+ # @option rules [Array, Hash] An array of hashes (or just one) to create Conditional formatting rules from
+ # @example This would color column A whenever it is FALSE
+ # worksheet.add_conditional_formatting( "A1:A1048576", { :type => :cellIs, :operator => :equal, :formula => "FALSE", :dxfId => 0, priority => 1 }
+ #
+ # @see ConditionalFormattingRule.#initialize
+ def add_conditional_formatting(cells, rules)
+ cf = ConditionalFormatting.new( :sqref => cells )
+ cf.add_rules rules
+ @conditional_formattings << cf
+ end
+
# Creates merge information for this worksheet.
# Cells can be merged by calling the merge_cells method on a worksheet.
# @example This would merge the three cells C1..E1 #
@@ -411,6 +425,9 @@ module Axlsx
unless @tables.empty?
str.concat "<tableParts count='%s'>%s</tableParts>" % [@tables.size, @tables.reduce('') { |memo, obj| memo += "<tablePart r:id='%s'/>" % obj.rId }]
end
+ @conditional_formattings.each do |cf|
+ str.concat cf.to_xml_string
+ end
str + '</worksheet>'
end