You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

XSSFColor.java 13KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429
  1. /* ====================================================================
  2. Licensed to the Apache Software Foundation (ASF) under one or more
  3. contributor license agreements. See the NOTICE file distributed with
  4. this work for additional information regarding copyright ownership.
  5. The ASF licenses this file to You under the Apache License, Version 2.0
  6. (the "License"); you may not use this file except in compliance with
  7. the License. You may obtain a copy of the License at
  8. http://www.apache.org/licenses/LICENSE-2.0
  9. Unless required by applicable law or agreed to in writing, software
  10. distributed under the License is distributed on an "AS IS" BASIS,
  11. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. See the License for the specific language governing permissions and
  13. limitations under the License.
  14. ==================================================================== */
  15. package org.apache.poi.xssf.usermodel;
  16. import java.util.Arrays;
  17. import org.apache.poi.ss.usermodel.Color;
  18. import org.apache.poi.ss.usermodel.ExtendedColor;
  19. import org.apache.poi.ss.usermodel.IndexedColors;
  20. import org.apache.poi.util.Internal;
  21. import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTColor;
  22. /**
  23. * Represents a color in SpreadsheetML
  24. */
  25. public class XSSFColor extends ExtendedColor {
  26. private final CTColor ctColor;
  27. private final IndexedColorMap indexedColorMap;
  28. /**
  29. * @param color The ooxml color object to use
  30. * @param map The IndexedColorMap to use instead of the default one (can be null)
  31. * @return null if color is null, new instance otherwise
  32. */
  33. public static XSSFColor from(CTColor color, IndexedColorMap map) {
  34. return color == null ? null : new XSSFColor(color, map);
  35. }
  36. /**
  37. * @param color The ooxml color object to use
  38. * @return null if color is null, new instance otherwise
  39. * @since POI 5.2.0
  40. */
  41. public static XSSFColor from(CTColor color) {
  42. return color == null ? null : new XSSFColor(color, null);
  43. }
  44. private XSSFColor(CTColor color, IndexedColorMap map) {
  45. this.ctColor = color;
  46. this.indexedColorMap = map;
  47. }
  48. /**
  49. * @since POI 5.2.0
  50. */
  51. public XSSFColor() {
  52. this(CTColor.Factory.newInstance(), null);
  53. }
  54. /**
  55. * new color with the given indexed color map
  56. * @param colorMap The IndexedColorMap to use instead of the default one (can be null)
  57. */
  58. public XSSFColor(IndexedColorMap colorMap) {
  59. this(CTColor.Factory.newInstance(), colorMap);
  60. }
  61. /**
  62. * TEST ONLY
  63. * @param clr awt Color
  64. * @param map The IndexedColorMap to use instead of the default one (can be null)
  65. */
  66. public XSSFColor(java.awt.Color clr, IndexedColorMap map) {
  67. this(map);
  68. setColor(clr);
  69. }
  70. /**
  71. * @param rgb The RGB-byte-values for the Color
  72. * @param colorMap The IndexedColorMap to use instead of the default one (can be null)
  73. */
  74. public XSSFColor(byte[] rgb, IndexedColorMap colorMap) {
  75. this(CTColor.Factory.newInstance(), colorMap);
  76. ctColor.setRgb(rgb);
  77. }
  78. /**
  79. * @param rgb The RGB-byte-values for the Color
  80. * @since POI 5.2.0
  81. */
  82. public XSSFColor(byte[] rgb) {
  83. this(rgb, null);
  84. }
  85. /**
  86. * @param indexedColor color index (Enum named for default colors)
  87. * @param colorMap The IndexedColorMap to use instead of the default one
  88. */
  89. public XSSFColor(IndexedColors indexedColor, IndexedColorMap colorMap) {
  90. this(CTColor.Factory.newInstance(), colorMap);
  91. ctColor.setIndexed(indexedColor.index);
  92. }
  93. /**
  94. * A boolean value indicating the ctColor is automatic and system ctColor dependent.
  95. */
  96. @Override
  97. public boolean isAuto() {
  98. return ctColor.getAuto();
  99. }
  100. /**
  101. * @param auto true if the ctColor is automatic and system ctColor dependent.
  102. */
  103. public void setAuto(boolean auto) {
  104. ctColor.setAuto(auto);
  105. }
  106. /**
  107. * A boolean value indicating the ctColor is Indexed
  108. */
  109. @Override
  110. public boolean isIndexed() {
  111. return ctColor.isSetIndexed();
  112. }
  113. /**
  114. * @return true if the ctColor is RGB or ARGB based
  115. */
  116. @Override
  117. public boolean isRGB() {
  118. return ctColor.isSetRgb();
  119. }
  120. /**
  121. * @return true if the ctColor is Theme based
  122. */
  123. @Override
  124. public boolean isThemed() {
  125. return ctColor.isSetTheme();
  126. }
  127. /**
  128. * @return true if the ctColor has a alpha
  129. */
  130. public boolean hasAlpha() {
  131. return ctColor.isSetRgb() && ctColor.getRgb().length == 4;
  132. }
  133. /**
  134. * @return true if the ctColor has a tint
  135. */
  136. public boolean hasTint() {
  137. return ctColor.isSetTint() && ctColor.getTint() != 0;
  138. }
  139. /**
  140. * Indexed ctColor value. Only used for backwards compatibility. References a ctColor in indexedColors.
  141. */
  142. @Override
  143. public short getIndex() {
  144. return (short)ctColor.getIndexed();
  145. }
  146. /**
  147. * @return Indexed ctColor value. Only used for backwards compatibility. References a ctColor in indexedColors.
  148. */
  149. public short getIndexed() {
  150. return getIndex();
  151. }
  152. /**
  153. * Indexed ctColor value. Only used for backwards compatibility. References a ctColor in indexedColors.
  154. * @param indexed color index
  155. */
  156. public void setIndexed(int indexed) {
  157. ctColor.setIndexed(indexed);
  158. }
  159. /**
  160. * Standard Red Green Blue ctColor value (RGB).
  161. * If there was an A (Alpha) value, it will be stripped.
  162. */
  163. @Override
  164. public byte[] getRGB() {
  165. byte[] rgb = getRGBOrARGB();
  166. if(rgb == null) {
  167. return null;
  168. }
  169. // Need to trim off the alpha
  170. return rgb.length == 4 ? Arrays.copyOfRange(rgb, 1, 4) : rgb;
  171. }
  172. /**
  173. * Standard Alpha Red Green Blue ctColor value (ARGB).
  174. */
  175. @Override
  176. public byte[] getARGB() {
  177. byte[] rgb = getRGBOrARGB();
  178. if(rgb == null) {
  179. return null;
  180. }
  181. if(rgb.length == 3) {
  182. // Pad with the default Alpha
  183. byte[] tmp = new byte[4];
  184. tmp[0] = -1;
  185. System.arraycopy(rgb, 0, tmp, 1, 3);
  186. return tmp;
  187. } else {
  188. return rgb;
  189. }
  190. }
  191. @Override
  192. protected byte[] getStoredRBG() {
  193. return ctColor.getRgb();
  194. }
  195. @Override
  196. protected byte[] getIndexedRGB() {
  197. if (isIndexed()) {
  198. if (indexedColorMap != null) return indexedColorMap.getRGB(getIndex());
  199. return DefaultIndexedColorMap.getDefaultRGB(getIndex());
  200. }
  201. return null;
  202. }
  203. /**
  204. * Standard Alpha Red Green Blue ctColor value (ARGB).
  205. */
  206. @Override
  207. public void setRGB(byte[] rgb) {
  208. ctColor.setRgb(rgb);
  209. }
  210. /**
  211. * Index into the {@code clrScheme} collection, referencing a particular {@code sysClr} or
  212. * {@code srgbClr} value expressed in the Theme part.
  213. */
  214. @Override
  215. public int getTheme() {
  216. return (int)ctColor.getTheme();
  217. }
  218. /**
  219. * Index into the {@code clrScheme} collection, referencing a particular {@code sysClr} or
  220. * {@code srgbClr} value expressed in the Theme part.
  221. * @param theme index
  222. */
  223. public void setTheme(int theme) {
  224. ctColor.setTheme(theme);
  225. }
  226. /**
  227. * Specifies the tint value applied to the ctColor.
  228. *
  229. * <p>
  230. * If tint is supplied, then it is applied to the RGB value of the ctColor to determine the final
  231. * ctColor applied.
  232. * </p>
  233. * <p>
  234. * The tint value is stored as a double from -1.0 .. 1.0, where -1.0 means 100% darken and
  235. * 1.0 means 100% lighten. Also, 0.0 means no change.
  236. * </p>
  237. * <p>
  238. * In loading the RGB value, it is converted to HLS where HLS values are (0..HLSMAX), where
  239. * HLSMAX is currently 255.
  240. * </p>
  241. * Here are some examples of how to apply tint to ctColor:
  242. * <blockquote>
  243. * <pre>
  244. * If (tint &lt; 0)
  245. * Lum' = Lum * (1.0 + tint)
  246. *
  247. * For example: Lum = 200; tint = -0.5; Darken 50%
  248. * Lum' = 200 * (0.5) =&gt; 100
  249. * For example: Lum = 200; tint = -1.0; Darken 100% (make black)
  250. * Lum' = 200 * (1.0-1.0) =&gt; 0
  251. * If (tint &gt; 0)
  252. * Lum' = Lum * (1.0-tint) + (HLSMAX - HLSMAX * (1.0-tint))
  253. * For example: Lum = 100; tint = 0.75; Lighten 75%
  254. *
  255. * Lum' = 100 * (1-.75) + (HLSMAX - HLSMAX*(1-.75))
  256. * = 100 * .25 + (255 - 255 * .25)
  257. * = 25 + (255 - 63) = 25 + 192 = 217
  258. * For example: Lum = 100; tint = 1.0; Lighten 100% (make white)
  259. * Lum' = 100 * (1-1) + (HLSMAX - HLSMAX*(1-1))
  260. * = 100 * 0 + (255 - 255 * 0)
  261. * = 0 + (255 - 0) = 255
  262. * </pre>
  263. * </blockquote>
  264. *
  265. * @return the tint value
  266. */
  267. @Override
  268. public double getTint() {
  269. return ctColor.getTint();
  270. }
  271. /**
  272. * Specifies the tint value applied to the ctColor.
  273. *
  274. * <p>
  275. * If tint is supplied, then it is applied to the RGB value of the ctColor to determine the final
  276. * ctColor applied.
  277. * </p>
  278. * <p>
  279. * The tint value is stored as a double from -1.0 .. 1.0, where -1.0 means 100% darken and
  280. * 1.0 means 100% lighten. Also, 0.0 means no change.
  281. * </p>
  282. * <p>
  283. * In loading the RGB value, it is converted to HLS where HLS values are (0..HLSMAX), where
  284. * HLSMAX is currently 255.
  285. * </p>
  286. * Here are some examples of how to apply tint to ctColor:
  287. * <blockquote>
  288. * <pre>
  289. * If (tint &lt; 0)
  290. * Lum' = Lum * (1.0 + tint)
  291. *
  292. * For example: Lum = 200; tint = -0.5; Darken 50%
  293. * Lum' = 200 * (0.5) =&gt; 100
  294. * For example: Lum = 200; tint = -1.0; Darken 100% (make black)
  295. * Lum' = 200 * (1.0-1.0) =&gt; 0
  296. * If (tint &gt; 0)
  297. * Lum' = Lum * (1.0-tint) + (HLSMAX - HLSMAX * (1.0-tint))
  298. * For example: Lum = 100; tint = 0.75; Lighten 75%
  299. *
  300. * Lum' = 100 * (1-.75) + (HLSMAX - HLSMAX*(1-.75))
  301. * = 100 * .25 + (255 - 255 * .25)
  302. * = 25 + (255 - 63) = 25 + 192 = 217
  303. * For example: Lum = 100; tint = 1.0; Lighten 100% (make white)
  304. * Lum' = 100 * (1-1) + (HLSMAX - HLSMAX*(1-1))
  305. * = 100 * 0 + (255 - 255 * 0)
  306. * = 0 + (255 - 0) = 255
  307. * </pre>
  308. * </blockquote>
  309. *
  310. * @param tint the tint value
  311. */
  312. @Override
  313. public void setTint(double tint) {
  314. ctColor.setTint(tint);
  315. }
  316. /**
  317. * Returns the underlying XML bean
  318. *
  319. * @return the underlying XML bean
  320. */
  321. @Internal
  322. public CTColor getCTColor(){
  323. return ctColor;
  324. }
  325. /**
  326. * Checked type cast {@code color} to an XSSFColor.
  327. *
  328. * @param color the color to type cast
  329. * @return the type casted color
  330. * @throws IllegalArgumentException if color is null or is not an instance of XSSFColor
  331. */
  332. public static XSSFColor toXSSFColor(Color color) {
  333. // FIXME: this method would be more useful if it could convert any Color to an XSSFColor
  334. // Currently the only benefit of this method is to throw an IllegalArgumentException
  335. // instead of a ClassCastException.
  336. if (color != null && !(color instanceof XSSFColor)) {
  337. throw new IllegalArgumentException("Only XSSFColor objects are supported, but had " + color.getClass());
  338. }
  339. return (XSSFColor)color;
  340. }
  341. @Override
  342. public int hashCode(){
  343. return ctColor.toString().hashCode();
  344. }
  345. // Helper methods for {@link #equals(Object)}
  346. private boolean sameIndexed(XSSFColor other) {
  347. if (isIndexed() == other.isIndexed()) {
  348. return !isIndexed() || getIndexed() == other.getIndexed();
  349. }
  350. return false;
  351. }
  352. private boolean sameARGB(XSSFColor other) {
  353. if (isRGB() == other.isRGB()) {
  354. return !isRGB() || Arrays.equals(getARGB(), other.getARGB());
  355. }
  356. return false;
  357. }
  358. private boolean sameTheme(XSSFColor other) {
  359. if (isThemed() == other.isThemed()) {
  360. return !isThemed() || getTheme() == other.getTheme();
  361. }
  362. return false;
  363. }
  364. private boolean sameTint(XSSFColor other) {
  365. if (hasTint() == other.hasTint()) {
  366. return !hasTint() || getTint() == other.getTint();
  367. }
  368. return false;
  369. }
  370. private boolean sameAuto(XSSFColor other) {
  371. return isAuto() == other.isAuto();
  372. }
  373. @Override
  374. public boolean equals(Object o){
  375. if(!(o instanceof XSSFColor)) {
  376. return false;
  377. }
  378. XSSFColor other = (XSSFColor)o;
  379. // Compare each field in ctColor.
  380. // Cannot compare ctColor's XML string representation because equivalent
  381. // colors may have different relation namespace URI's
  382. return sameARGB(other)
  383. && sameTheme(other)
  384. && sameIndexed(other)
  385. && sameTint(other)
  386. && sameAuto(other);
  387. }
  388. }