Vous ne pouvez pas sélectionner plus de 25 sujets Les noms de sujets doivent commencer par une lettre ou un nombre, peuvent contenir des tirets ('-') et peuvent comporter jusqu'à 35 caractères.

PDFColor.java 17KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561
  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. *
  9. * http://www.apache.org/licenses/LICENSE-2.0
  10. *
  11. * Unless required by applicable law or agreed to in writing, software
  12. * distributed under the License is distributed on an "AS IS" BASIS,
  13. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14. * See the License for the specific language governing permissions and
  15. * limitations under the License.
  16. */
  17. /* $Id$ */
  18. package org.apache.fop.pdf;
  19. import java.awt.Color;
  20. import java.awt.color.ColorSpace;
  21. import java.awt.color.ICC_ColorSpace;
  22. import java.io.IOException;
  23. import java.util.ArrayList;
  24. import java.util.List;
  25. import org.apache.xmlgraphics.java2d.color.DeviceCMYKColorSpace;
  26. import org.apache.fop.util.ColorExt;
  27. /**
  28. * PDF Color object.
  29. * This is used to output color to a PDF content stream.
  30. */
  31. public class PDFColor extends PDFPathPaint {
  32. // could be 3.0 as well.
  33. private static double blackFactor = 2.0;
  34. private double red = -1.0;
  35. private double green = -1.0;
  36. private double blue = -1.0;
  37. private double cyan = -1.0;
  38. private double magenta = -1.0;
  39. private double yellow = -1.0;
  40. private double black = -1.0;
  41. // TODO - It would probably be better to reorganize PDFPathPaint/PDFColor/PDFColorSpace
  42. // class hierarchy. However, at this early stages of my FOP understanding, I can
  43. // not really oversee the consequences of such a switch (nor whether it would be
  44. // appropriate).
  45. private ColorExt colorExt = null;
  46. /**
  47. * Create a PDF color with double values ranging from 0 to 1
  48. *
  49. * @param theRed the red double value
  50. * @param theGreen the green double value
  51. * @param theBlue the blue double value
  52. */
  53. public PDFColor(double theRed, double theGreen, double theBlue) {
  54. // super(theNumber);
  55. this.colorSpace = new PDFDeviceColorSpace(PDFDeviceColorSpace.DEVICE_RGB);
  56. this.red = theRed;
  57. this.green = theGreen;
  58. this.blue = theBlue;
  59. }
  60. /**
  61. * Create PDFColor for the given document and based on the java.awt.Color object
  62. *
  63. * In case the java.awt.Color is an instance of the ColorExt class a PDFICCStream is added to
  64. * the PDFDocument that is being created
  65. *
  66. * @param pdfDoc PDFDocument that is being created
  67. * @param col Color object from which to create this PDFColor
  68. */
  69. public PDFColor(PDFDocument pdfDoc, Color col) {
  70. this(col);
  71. // TODO - 1) There is a potential conflict when FOP and Batik elements use the same color
  72. // profile name for different profiles.
  73. // 2) In case the same color profile is used with different names it will be
  74. // included multiple times in the PDF
  75. //
  76. if (colorExt != null
  77. && pdfDoc.getResources().getColorSpace(colorExt.getIccProfileName()) == null) {
  78. PDFICCStream pdfIccStream = new PDFICCStream();
  79. ColorSpace ceCs = colorExt.getOrigColorSpace();
  80. try {
  81. pdfIccStream.setColorSpace(((ICC_ColorSpace)ceCs).getProfile(), null);
  82. pdfIccStream.setData(
  83. ((ICC_ColorSpace)colorExt.getColorSpace()).getProfile().getData());
  84. } catch (IOException ioe) {
  85. log.error("Failed to set profile data for " + colorExt.getIccProfileName());
  86. }
  87. pdfDoc.registerObject(pdfIccStream);
  88. pdfDoc.getFactory().makeICCBasedColorSpace(
  89. null, colorExt.getIccProfileName(), pdfIccStream);
  90. if (log.isInfoEnabled()) {
  91. log.info("Adding PDFICCStream " + colorExt.getIccProfileName()
  92. + " for " + colorExt.getIccProfileSrc());
  93. }
  94. }
  95. }
  96. /**
  97. * Create a PDF color from a java.awt.Color object.
  98. *
  99. * Different Color objects are handled differently. Cases recognized are.
  100. *
  101. * 1. CMYK color
  102. * 2. ColorExt color
  103. * 3. 'Normal' java.awt.Color (RGB case assumed)
  104. *
  105. * @param col the java.awt.Color object for which to create a PDFColor object
  106. */
  107. public PDFColor(java.awt.Color col) {
  108. ColorSpace cs = col.getColorSpace();
  109. ColorExt ce = null;
  110. if (col instanceof ColorExt) {
  111. ce = (ColorExt)col;
  112. cs = ce.getOrigColorSpace();
  113. }
  114. if (cs != null && cs instanceof DeviceCMYKColorSpace) {
  115. // CMYK case
  116. this.colorSpace = new PDFDeviceColorSpace(PDFDeviceColorSpace.DEVICE_CMYK);
  117. float[] cmyk = (ce == null
  118. ? col.getColorComponents(null)
  119. : ce.getOriginalColorComponents());
  120. this.cyan = cmyk[0];
  121. this.magenta = cmyk[1];
  122. this.yellow = cmyk[2];
  123. this.black = cmyk[3];
  124. } else if (ce != null) {
  125. // ColorExt (ICC) case
  126. this.colorExt = ce;
  127. float[] rgb = col.getRGBColorComponents(null);
  128. this.red = rgb[0];
  129. this.green = rgb[1];
  130. this.blue = rgb[2];
  131. // TODO - See earlier todo
  132. this.colorSpace = new PDFDeviceColorSpace(PDFDeviceColorSpace.DEVICE_RGB);
  133. } else {
  134. // Default (RGB) Color
  135. this.colorSpace = new PDFDeviceColorSpace(PDFDeviceColorSpace.DEVICE_RGB);
  136. float[] comps = new float[3];
  137. comps = col.getColorComponents(comps);
  138. this.red = comps[0];
  139. this.green = comps[1];
  140. this.blue = comps[2];
  141. }
  142. }
  143. /**
  144. * Create a PDF color with int values ranging from 0 to 255
  145. *
  146. * @param theRed the red integer value
  147. * @param theGreen the green integer value
  148. * @param theBlue the blue integer value
  149. */
  150. public PDFColor(int theRed, int theGreen, int theBlue) {
  151. this(((double)theRed) / 255d, ((double)theGreen) / 255d,
  152. ((double)theBlue) / 255d);
  153. }
  154. /**
  155. * Create a PDF color with CMYK values.
  156. *
  157. * @param theCyan the cyan value
  158. * @param theMagenta the magenta value
  159. * @param theYellow the yellow value
  160. * @param theBlack the black value
  161. */
  162. public PDFColor(double theCyan, double theMagenta, double theYellow,
  163. double theBlack) {
  164. // super(theNumber);//?
  165. this.colorSpace = new PDFDeviceColorSpace(PDFDeviceColorSpace.DEVICE_CMYK);
  166. this.cyan = theCyan;
  167. this.magenta = theMagenta;
  168. this.yellow = theYellow;
  169. this.black = theBlack;
  170. }
  171. /**
  172. * Return a vector representation of the color
  173. * in the appropriate colorspace.
  174. *
  175. * @return a list containing the Double values of the color
  176. */
  177. public List getVector() {
  178. List theColorVector = new ArrayList();
  179. if (this.colorSpace.getColorSpace() == PDFDeviceColorSpace.DEVICE_RGB) {
  180. // RGB
  181. theColorVector.add(new Double(this.red));
  182. theColorVector.add(new Double(this.green));
  183. theColorVector.add(new Double(this.blue));
  184. } else if (this.colorSpace.getColorSpace()
  185. == PDFDeviceColorSpace.DEVICE_CMYK) {
  186. // CMYK
  187. theColorVector.add(new Double(this.cyan));
  188. theColorVector.add(new Double(this.magenta));
  189. theColorVector.add(new Double(this.yellow));
  190. theColorVector.add(new Double(this.black));
  191. } else {
  192. // GRAY
  193. theColorVector.add(new Double(this.black));
  194. }
  195. return (theColorVector);
  196. }
  197. /**
  198. * Get the red component.
  199. *
  200. * @return the red double value
  201. */
  202. public double red() {
  203. return (this.red);
  204. }
  205. /**
  206. * Get the green component.
  207. *
  208. * @return the green double value
  209. */
  210. public double green() {
  211. return (this.green);
  212. }
  213. /**
  214. * Get the blue component.
  215. *
  216. * @return the blue double value
  217. */
  218. public double blue() {
  219. return (this.blue);
  220. }
  221. /**
  222. * Get the red integer component.
  223. *
  224. * @return the red integer value
  225. */
  226. public int red255() {
  227. return (int)(this.red * 255d);
  228. }
  229. /**
  230. * Get the green integer component.
  231. *
  232. * @return the green integer value
  233. */
  234. public int green255() {
  235. return (int)(this.green * 255d);
  236. }
  237. /**
  238. * Get the blue integer component.
  239. *
  240. * @return the blue integer value
  241. */
  242. public int blue255() {
  243. return (int)(this.blue * 255d);
  244. }
  245. /**
  246. * Get the cyan component.
  247. *
  248. * @return the cyan double value
  249. */
  250. public double cyan() {
  251. return (this.cyan);
  252. }
  253. /**
  254. * Get the magenta component.
  255. *
  256. * @return the magenta double value
  257. */
  258. public double magenta() {
  259. return (this.magenta);
  260. }
  261. /**
  262. * Get the yellow component.
  263. *
  264. * @return the yellow double value
  265. */
  266. public double yellow() {
  267. return (this.yellow);
  268. }
  269. /**
  270. * Get the black component.
  271. *
  272. * @return the black double value
  273. */
  274. public double black() {
  275. return (this.black);
  276. }
  277. /**
  278. * Set the color space for this color.
  279. * If the new color space is different the values are converted
  280. * to the new color space.
  281. *
  282. * @param theColorSpace the new color space
  283. */
  284. public void setColorSpace(int theColorSpace) {
  285. int theOldColorSpace = this.colorSpace.getColorSpace();
  286. if (theOldColorSpace != theColorSpace) {
  287. if (theOldColorSpace == PDFDeviceColorSpace.DEVICE_RGB) {
  288. if (theColorSpace == PDFDeviceColorSpace.DEVICE_CMYK) {
  289. this.convertRGBtoCMYK();
  290. } else {
  291. // convert to Gray?
  292. this.convertRGBtoGRAY();
  293. }
  294. } else if (theOldColorSpace == PDFDeviceColorSpace.DEVICE_CMYK) {
  295. if (theColorSpace == PDFDeviceColorSpace.DEVICE_RGB) {
  296. this.convertCMYKtoRGB();
  297. } else {
  298. // convert to Gray?
  299. this.convertCMYKtoGRAY();
  300. }
  301. } else {
  302. // used to be Gray
  303. if (theColorSpace == PDFDeviceColorSpace.DEVICE_RGB) {
  304. this.convertGRAYtoRGB();
  305. } else {
  306. // convert to CMYK?
  307. this.convertGRAYtoCMYK();
  308. }
  309. }
  310. this.colorSpace.setColorSpace(theColorSpace);
  311. }
  312. }
  313. /**
  314. * Get the PDF output string for this color.
  315. * This returns the string to be inserted into PDF for setting
  316. * the current color.
  317. *
  318. * @param fillNotStroke whether to return fill or stroke command
  319. * @return the PDF string for setting the fill/stroke color
  320. */
  321. public String getColorSpaceOut(boolean fillNotStroke) {
  322. StringBuffer p = new StringBuffer("");
  323. if (this.colorExt != null) {
  324. if (fillNotStroke) {
  325. p.append("/" + this.colorExt.getIccProfileName() + " cs ");
  326. } else {
  327. p.append("/" + this.colorExt.getIccProfileName() + " CS ");
  328. }
  329. float[] colorArgs;
  330. colorArgs = this.colorExt.getOriginalColorComponents();
  331. if (colorArgs == null) {
  332. colorArgs = this.colorExt.getColorComponents(null);
  333. }
  334. for (int ix = 0; ix < colorArgs.length; ix++) {
  335. p.append(colorArgs[ix] + " ");
  336. }
  337. if (fillNotStroke) {
  338. p.append("sc\n");
  339. } else {
  340. p.append("SC\n");
  341. }
  342. } else if (this.colorSpace.getColorSpace()
  343. == PDFDeviceColorSpace.DEVICE_RGB) { // colorspace is RGB
  344. // according to pdfspec 12.1 p.399
  345. // if the colors are the same then just use the g or G operator
  346. boolean same = false;
  347. if (this.red == this.green && this.red == this.blue) {
  348. same = true;
  349. }
  350. // output RGB
  351. if (fillNotStroke) {
  352. // fill
  353. if (same) {
  354. p.append(PDFNumber.doubleOut(this.red) + " g\n");
  355. } else {
  356. p.append(PDFNumber.doubleOut(this.red) + " "
  357. + PDFNumber.doubleOut(this.green) + " "
  358. + PDFNumber.doubleOut(this.blue)
  359. + " rg\n");
  360. }
  361. } else {
  362. // stroke/border
  363. if (same) {
  364. p.append(PDFNumber.doubleOut(this.red) + " G\n");
  365. } else {
  366. p.append(PDFNumber.doubleOut(this.red) + " "
  367. + PDFNumber.doubleOut(this.green) + " "
  368. + PDFNumber.doubleOut(this.blue)
  369. + " RG\n");
  370. }
  371. }
  372. } else if (this.colorSpace.getColorSpace()
  373. == PDFDeviceColorSpace.DEVICE_CMYK) {
  374. // colorspace is CMYK
  375. if (fillNotStroke) {
  376. // fill
  377. p.append(PDFNumber.doubleOut(this.cyan) + " "
  378. + PDFNumber.doubleOut(this.magenta) + " "
  379. + PDFNumber.doubleOut(this.yellow) + " "
  380. + PDFNumber.doubleOut(this.black) + " k\n");
  381. } else {
  382. // stroke
  383. p.append(PDFNumber.doubleOut(this.cyan) + " "
  384. + PDFNumber.doubleOut(this.magenta) + " "
  385. + PDFNumber.doubleOut(this.yellow) + " "
  386. + PDFNumber.doubleOut(this.black) + " K\n");
  387. }
  388. } else {
  389. // means we're in DeviceGray or Unknown.
  390. // assume we're in DeviceGray, because otherwise we're screwed.
  391. if (fillNotStroke) {
  392. p.append(PDFNumber.doubleOut(this.black) + " g\n");
  393. } else {
  394. p.append(PDFNumber.doubleOut(this.black) + " G\n");
  395. }
  396. }
  397. return (p.toString());
  398. }
  399. /**
  400. * Convert the color from CMYK to RGB.
  401. */
  402. protected void convertCMYKtoRGB() {
  403. // convert CMYK to RGB
  404. this.red = 1.0 - this.cyan;
  405. this.green = 1.0 - this.green;
  406. this.blue = 1.0 - this.yellow;
  407. this.red = (this.black / PDFColor.blackFactor) + this.red;
  408. this.green = (this.black / PDFColor.blackFactor) + this.green;
  409. this.blue = (this.black / PDFColor.blackFactor) + this.blue;
  410. }
  411. /**
  412. * Convert the color from RGB to CMYK.
  413. */
  414. protected void convertRGBtoCMYK() {
  415. // convert RGB to CMYK
  416. this.cyan = 1.0 - this.red;
  417. this.magenta = 1.0 - this.green;
  418. this.yellow = 1.0 - this.blue;
  419. this.black = 0.0;
  420. /*
  421. * If you want to calculate black, uncomment this
  422. * //pick the lowest color
  423. * tempDouble = this.red;
  424. *
  425. * if (this.green < tempDouble)
  426. * tempDouble = this.green;
  427. *
  428. * if (this.blue < tempDouble)
  429. * tempDouble = this.blue;
  430. *
  431. * this.black = tempDouble / this.blackFactor;
  432. */
  433. }
  434. /**
  435. * Convert the color from Gray to RGB.
  436. */
  437. protected void convertGRAYtoRGB() {
  438. this.red = 1.0 - this.black;
  439. this.green = 1.0 - this.black;
  440. this.blue = 1.0 - this.black;
  441. }
  442. /**
  443. * Convert the color from Gray to CMYK.
  444. */
  445. protected void convertGRAYtoCMYK() {
  446. this.cyan = this.black;
  447. this.magenta = this.black;
  448. this.yellow = this.black;
  449. // this.black=0.0;//?
  450. }
  451. /**
  452. * Convert the color from CMYK to Gray.
  453. */
  454. protected void convertCMYKtoGRAY() {
  455. double tempDouble = 0.0;
  456. // pick the lowest color
  457. tempDouble = this.cyan;
  458. if (this.magenta < tempDouble) {
  459. tempDouble = this.magenta;
  460. }
  461. if (this.yellow < tempDouble) {
  462. tempDouble = this.yellow;
  463. }
  464. this.black = (tempDouble / PDFColor.blackFactor);
  465. }
  466. /**
  467. * Convert the color from RGB to Gray.
  468. */
  469. protected void convertRGBtoGRAY() {
  470. double tempDouble = 0.0;
  471. // pick the lowest color
  472. tempDouble = this.red;
  473. if (this.green < tempDouble) {
  474. tempDouble = this.green;
  475. }
  476. if (this.blue < tempDouble) {
  477. tempDouble = this.blue;
  478. }
  479. this.black = 1.0 - (tempDouble / PDFColor.blackFactor);
  480. }
  481. /**
  482. * Create pdf.
  483. * Not used for this object.
  484. *
  485. * @return the bytes for the pdf
  486. */
  487. public byte[] toPDF() {
  488. return (new byte[0]);
  489. }
  490. /** {@inheritDoc} */
  491. protected boolean contentEquals(PDFObject obj) {
  492. if (!(obj instanceof PDFColor)) {
  493. return false;
  494. }
  495. PDFColor color = (PDFColor)obj;
  496. if (color.red == this.red
  497. && color.green == this.green
  498. && color.blue == this.blue) {
  499. return true;
  500. }
  501. return false;
  502. }
  503. }