mirror of
https://github.com/mozilla/pdf.js.git
synced 2025-04-20 15:18:08 +02:00
[api-minor] Use an icc profile for converting CMYK to RGB
This commit is contained in:
parent
1bc98dfbd9
commit
7280540901
14 changed files with 224 additions and 274 deletions
|
@ -25,9 +25,9 @@ import {
|
|||
LabCS,
|
||||
PatternCS,
|
||||
} from "./colorspace.js";
|
||||
import { CmykICCBasedCS, IccColorSpace } from "./icc_colorspace.js";
|
||||
import { Dict, Name, Ref } from "./primitives.js";
|
||||
import { MathClamp, shadow, unreachable, warn } from "../shared/util.js";
|
||||
import { IccColorSpace } from "./icc_colorspace.js";
|
||||
import { MissingDataException } from "./core_utils.js";
|
||||
|
||||
class ColorSpaceUtils {
|
||||
|
@ -205,7 +205,11 @@ class ColorSpaceUtils {
|
|||
|
||||
if (IccColorSpace.isUsable) {
|
||||
try {
|
||||
const iccCS = new IccColorSpace(stream.getBytes(), numComps);
|
||||
const iccCS = new IccColorSpace(
|
||||
stream.getBytes(),
|
||||
"ICCBased",
|
||||
numComps
|
||||
);
|
||||
if (isRef) {
|
||||
globalColorSpaceCache.set(/* name = */ null, cs[1], iccCS);
|
||||
}
|
||||
|
@ -285,6 +289,13 @@ class ColorSpaceUtils {
|
|||
}
|
||||
|
||||
static get cmyk() {
|
||||
if (IccColorSpace.isUsable) {
|
||||
try {
|
||||
return shadow(this, "cmyk", new CmykICCBasedCS());
|
||||
} catch {
|
||||
warn("CMYK fallback: DeviceCMYK");
|
||||
}
|
||||
}
|
||||
return shadow(this, "cmyk", new DeviceCmykCS());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -94,6 +94,7 @@ const DefaultPartialEvaluatorOptions = Object.freeze({
|
|||
useWasm: true,
|
||||
useWorkerFetch: true,
|
||||
cMapUrl: null,
|
||||
iccUrl: null,
|
||||
standardFontDataUrl: null,
|
||||
wasmUrl: null,
|
||||
});
|
||||
|
|
|
@ -41,12 +41,12 @@ class IccColorSpace extends ColorSpace {
|
|||
qcms_drop_transformer(transformer);
|
||||
});
|
||||
|
||||
constructor(iccProfile, numComps) {
|
||||
constructor(iccProfile, name, numComps) {
|
||||
if (!IccColorSpace.isUsable) {
|
||||
throw new Error("No ICC color space support");
|
||||
}
|
||||
|
||||
super("ICCBased", numComps);
|
||||
super(name, numComps);
|
||||
|
||||
let inType;
|
||||
switch (numComps) {
|
||||
|
@ -104,11 +104,13 @@ class IccColorSpace extends ColorSpace {
|
|||
src[i] *= scale;
|
||||
}
|
||||
}
|
||||
QCMS._mustAddAlpha = alpha01 && dest.buffer === src.buffer;
|
||||
QCMS._destBuffer = dest.subarray(
|
||||
destOffset,
|
||||
destOffset + count * (3 + alpha01)
|
||||
);
|
||||
qcms_convert_array(this.#transformer, src);
|
||||
QCMS._mustAddAlpha = false;
|
||||
QCMS._destBuffer = null;
|
||||
}
|
||||
|
||||
|
@ -152,4 +154,21 @@ class IccColorSpace extends ColorSpace {
|
|||
}
|
||||
}
|
||||
|
||||
export { IccColorSpace };
|
||||
class CmykICCBasedCS extends IccColorSpace {
|
||||
static #iccUrl;
|
||||
|
||||
constructor() {
|
||||
const filename = "CGATS001Compat-v2-micro.icc";
|
||||
const xhr = new XMLHttpRequest();
|
||||
xhr.open("GET", `${CmykICCBasedCS.#iccUrl}${filename}`, false);
|
||||
xhr.responseType = "arraybuffer";
|
||||
xhr.send(null);
|
||||
super(new Uint8Array(xhr.response), "DeviceCMYK", 4);
|
||||
}
|
||||
|
||||
static setOptions({ iccUrl }) {
|
||||
this.#iccUrl = iccUrl;
|
||||
}
|
||||
}
|
||||
|
||||
export { CmykICCBasedCS, IccColorSpace };
|
||||
|
|
272
src/core/jpg.js
272
src/core/jpg.js
|
@ -14,6 +14,7 @@
|
|||
*/
|
||||
|
||||
import { assert, BaseException, warn } from "../shared/util.js";
|
||||
import { ColorSpaceUtils } from "./colorspace_utils.js";
|
||||
import { grayToRGBA } from "../shared/image_utils.js";
|
||||
import { readUint16 } from "./core_utils.js";
|
||||
|
||||
|
@ -1317,141 +1318,13 @@ class JpegImage {
|
|||
}
|
||||
|
||||
_convertYcckToRgb(data) {
|
||||
let Y, Cb, Cr, k;
|
||||
let offset = 0;
|
||||
for (let i = 0, length = data.length; i < length; i += 4) {
|
||||
Y = data[i];
|
||||
Cb = data[i + 1];
|
||||
Cr = data[i + 2];
|
||||
k = data[i + 3];
|
||||
|
||||
data[offset++] =
|
||||
-122.67195406894 +
|
||||
Cb *
|
||||
(-6.60635669420364e-5 * Cb +
|
||||
0.000437130475926232 * Cr -
|
||||
5.4080610064599e-5 * Y +
|
||||
0.00048449797120281 * k -
|
||||
0.154362151871126) +
|
||||
Cr *
|
||||
(-0.000957964378445773 * Cr +
|
||||
0.000817076911346625 * Y -
|
||||
0.00477271405408747 * k +
|
||||
1.53380253221734) +
|
||||
Y *
|
||||
(0.000961250184130688 * Y -
|
||||
0.00266257332283933 * k +
|
||||
0.48357088451265) +
|
||||
k * (-0.000336197177618394 * k + 0.484791561490776);
|
||||
|
||||
data[offset++] =
|
||||
107.268039397724 +
|
||||
Cb *
|
||||
(2.19927104525741e-5 * Cb -
|
||||
0.000640992018297945 * Cr +
|
||||
0.000659397001245577 * Y +
|
||||
0.000426105652938837 * k -
|
||||
0.176491792462875) +
|
||||
Cr *
|
||||
(-0.000778269941513683 * Cr +
|
||||
0.00130872261408275 * Y +
|
||||
0.000770482631801132 * k -
|
||||
0.151051492775562) +
|
||||
Y *
|
||||
(0.00126935368114843 * Y -
|
||||
0.00265090189010898 * k +
|
||||
0.25802910206845) +
|
||||
k * (-0.000318913117588328 * k - 0.213742400323665);
|
||||
|
||||
data[offset++] =
|
||||
-20.810012546947 +
|
||||
Cb *
|
||||
(-0.000570115196973677 * Cb -
|
||||
2.63409051004589e-5 * Cr +
|
||||
0.0020741088115012 * Y -
|
||||
0.00288260236853442 * k +
|
||||
0.814272968359295) +
|
||||
Cr *
|
||||
(-1.53496057440975e-5 * Cr -
|
||||
0.000132689043961446 * Y +
|
||||
0.000560833691242812 * k -
|
||||
0.195152027534049) +
|
||||
Y *
|
||||
(0.00174418132927582 * Y -
|
||||
0.00255243321439347 * k +
|
||||
0.116935020465145) +
|
||||
k * (-0.000343531996510555 * k + 0.24165260232407);
|
||||
}
|
||||
// Ensure that only the converted RGB data is returned.
|
||||
return data.subarray(0, offset);
|
||||
this._convertYcckToCmyk(data);
|
||||
return this._convertCmykToRgb(data);
|
||||
}
|
||||
|
||||
_convertYcckToRgba(data) {
|
||||
for (let i = 0, length = data.length; i < length; i += 4) {
|
||||
const Y = data[i];
|
||||
const Cb = data[i + 1];
|
||||
const Cr = data[i + 2];
|
||||
const k = data[i + 3];
|
||||
|
||||
data[i] =
|
||||
-122.67195406894 +
|
||||
Cb *
|
||||
(-6.60635669420364e-5 * Cb +
|
||||
0.000437130475926232 * Cr -
|
||||
5.4080610064599e-5 * Y +
|
||||
0.00048449797120281 * k -
|
||||
0.154362151871126) +
|
||||
Cr *
|
||||
(-0.000957964378445773 * Cr +
|
||||
0.000817076911346625 * Y -
|
||||
0.00477271405408747 * k +
|
||||
1.53380253221734) +
|
||||
Y *
|
||||
(0.000961250184130688 * Y -
|
||||
0.00266257332283933 * k +
|
||||
0.48357088451265) +
|
||||
k * (-0.000336197177618394 * k + 0.484791561490776);
|
||||
|
||||
data[i + 1] =
|
||||
107.268039397724 +
|
||||
Cb *
|
||||
(2.19927104525741e-5 * Cb -
|
||||
0.000640992018297945 * Cr +
|
||||
0.000659397001245577 * Y +
|
||||
0.000426105652938837 * k -
|
||||
0.176491792462875) +
|
||||
Cr *
|
||||
(-0.000778269941513683 * Cr +
|
||||
0.00130872261408275 * Y +
|
||||
0.000770482631801132 * k -
|
||||
0.151051492775562) +
|
||||
Y *
|
||||
(0.00126935368114843 * Y -
|
||||
0.00265090189010898 * k +
|
||||
0.25802910206845) +
|
||||
k * (-0.000318913117588328 * k - 0.213742400323665);
|
||||
|
||||
data[i + 2] =
|
||||
-20.810012546947 +
|
||||
Cb *
|
||||
(-0.000570115196973677 * Cb -
|
||||
2.63409051004589e-5 * Cr +
|
||||
0.0020741088115012 * Y -
|
||||
0.00288260236853442 * k +
|
||||
0.814272968359295) +
|
||||
Cr *
|
||||
(-1.53496057440975e-5 * Cr -
|
||||
0.000132689043961446 * Y +
|
||||
0.000560833691242812 * k -
|
||||
0.195152027534049) +
|
||||
Y *
|
||||
(0.00174418132927582 * Y -
|
||||
0.00255243321439347 * k +
|
||||
0.116935020465145) +
|
||||
k * (-0.000343531996510555 * k + 0.24165260232407);
|
||||
data[i + 3] = 255;
|
||||
}
|
||||
return data;
|
||||
this._convertYcckToCmyk(data);
|
||||
return this._convertCmykToRgba(data);
|
||||
}
|
||||
|
||||
_convertYcckToCmyk(data) {
|
||||
|
@ -1469,140 +1342,13 @@ class JpegImage {
|
|||
}
|
||||
|
||||
_convertCmykToRgb(data) {
|
||||
let c, m, y, k;
|
||||
let offset = 0;
|
||||
for (let i = 0, length = data.length; i < length; i += 4) {
|
||||
c = data[i];
|
||||
m = data[i + 1];
|
||||
y = data[i + 2];
|
||||
k = data[i + 3];
|
||||
|
||||
data[offset++] =
|
||||
255 +
|
||||
c *
|
||||
(-0.00006747147073602441 * c +
|
||||
0.0008379262121013727 * m +
|
||||
0.0002894718188643294 * y +
|
||||
0.003264231057537806 * k -
|
||||
1.1185611867203937) +
|
||||
m *
|
||||
(0.000026374107616089405 * m -
|
||||
0.00008626949158638572 * y -
|
||||
0.0002748769067499491 * k -
|
||||
0.02155688794978967) +
|
||||
y *
|
||||
(-0.00003878099212869363 * y -
|
||||
0.0003267808279485286 * k +
|
||||
0.0686742238595345) -
|
||||
k * (0.0003361971776183937 * k + 0.7430659151342254);
|
||||
|
||||
data[offset++] =
|
||||
255 +
|
||||
c *
|
||||
(0.00013596372813588848 * c +
|
||||
0.000924537132573585 * m +
|
||||
0.00010567359618683593 * y +
|
||||
0.0004791864687436512 * k -
|
||||
0.3109689587515875) +
|
||||
m *
|
||||
(-0.00023545346108370344 * m +
|
||||
0.0002702845253534714 * y +
|
||||
0.0020200308977307156 * k -
|
||||
0.7488052167015494) +
|
||||
y *
|
||||
(0.00006834815998235662 * y +
|
||||
0.00015168452363460973 * k -
|
||||
0.09751927774728933) -
|
||||
k * (0.0003189131175883281 * k + 0.7364883807733168);
|
||||
|
||||
data[offset++] =
|
||||
255 +
|
||||
c *
|
||||
(0.000013598650411385307 * c +
|
||||
0.00012423956175490851 * m +
|
||||
0.0004751985097583589 * y -
|
||||
0.0000036729317476630422 * k -
|
||||
0.05562186980264034) +
|
||||
m *
|
||||
(0.00016141380598724676 * m +
|
||||
0.0009692239130725186 * y +
|
||||
0.0007782692450036253 * k -
|
||||
0.44015232367526463) +
|
||||
y *
|
||||
(5.068882914068769e-7 * y +
|
||||
0.0017778369011375071 * k -
|
||||
0.7591454649749609) -
|
||||
k * (0.0003435319965105553 * k + 0.7063770186160144);
|
||||
}
|
||||
// Ensure that only the converted RGB data is returned.
|
||||
return data.subarray(0, offset);
|
||||
const count = data.length / 4;
|
||||
ColorSpaceUtils.cmyk.getRgbBuffer(data, 0, count, data, 0, 8, 0);
|
||||
return data.subarray(0, count * 3);
|
||||
}
|
||||
|
||||
_convertCmykToRgba(data) {
|
||||
for (let i = 0, length = data.length; i < length; i += 4) {
|
||||
const c = data[i];
|
||||
const m = data[i + 1];
|
||||
const y = data[i + 2];
|
||||
const k = data[i + 3];
|
||||
|
||||
data[i] =
|
||||
255 +
|
||||
c *
|
||||
(-0.00006747147073602441 * c +
|
||||
0.0008379262121013727 * m +
|
||||
0.0002894718188643294 * y +
|
||||
0.003264231057537806 * k -
|
||||
1.1185611867203937) +
|
||||
m *
|
||||
(0.000026374107616089405 * m -
|
||||
0.00008626949158638572 * y -
|
||||
0.0002748769067499491 * k -
|
||||
0.02155688794978967) +
|
||||
y *
|
||||
(-0.00003878099212869363 * y -
|
||||
0.0003267808279485286 * k +
|
||||
0.0686742238595345) -
|
||||
k * (0.0003361971776183937 * k + 0.7430659151342254);
|
||||
|
||||
data[i + 1] =
|
||||
255 +
|
||||
c *
|
||||
(0.00013596372813588848 * c +
|
||||
0.000924537132573585 * m +
|
||||
0.00010567359618683593 * y +
|
||||
0.0004791864687436512 * k -
|
||||
0.3109689587515875) +
|
||||
m *
|
||||
(-0.00023545346108370344 * m +
|
||||
0.0002702845253534714 * y +
|
||||
0.0020200308977307156 * k -
|
||||
0.7488052167015494) +
|
||||
y *
|
||||
(0.00006834815998235662 * y +
|
||||
0.00015168452363460973 * k -
|
||||
0.09751927774728933) -
|
||||
k * (0.0003189131175883281 * k + 0.7364883807733168);
|
||||
|
||||
data[i + 2] =
|
||||
255 +
|
||||
c *
|
||||
(0.000013598650411385307 * c +
|
||||
0.00012423956175490851 * m +
|
||||
0.0004751985097583589 * y -
|
||||
0.0000036729317476630422 * k -
|
||||
0.05562186980264034) +
|
||||
m *
|
||||
(0.00016141380598724676 * m +
|
||||
0.0009692239130725186 * y +
|
||||
0.0007782692450036253 * k -
|
||||
0.44015232367526463) +
|
||||
y *
|
||||
(5.068882914068769e-7 * y +
|
||||
0.0017778369011375071 * k -
|
||||
0.7591454649749609) -
|
||||
k * (0.0003435319965105553 * k + 0.7063770186160144);
|
||||
data[i + 3] = 255;
|
||||
}
|
||||
ColorSpaceUtils.cmyk.getRgbBuffer(data, 0, data.length / 4, data, 0, 8, 1);
|
||||
return data;
|
||||
}
|
||||
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { CmykICCBasedCS, IccColorSpace } from "./icc_colorspace.js";
|
||||
import {
|
||||
createValidAbsoluteUrl,
|
||||
FeatureTest,
|
||||
|
@ -20,7 +21,6 @@ import {
|
|||
warn,
|
||||
} from "../shared/util.js";
|
||||
import { ChunkedStreamManager } from "./chunked_stream.js";
|
||||
import { IccColorSpace } from "./icc_colorspace.js";
|
||||
import { ImageResizer } from "./image_resizer.js";
|
||||
import { JpegStream } from "./jpeg_stream.js";
|
||||
import { JpxImage } from "./jpx.js";
|
||||
|
@ -78,6 +78,7 @@ class BasePdfManager {
|
|||
const options = { ...evaluatorOptions, handler };
|
||||
JpxImage.setOptions(options);
|
||||
IccColorSpace.setOptions(options);
|
||||
CmykICCBasedCS.setOptions(options);
|
||||
}
|
||||
|
||||
get docId() {
|
||||
|
|
|
@ -125,6 +125,8 @@ const RENDERING_CANCELLED_TIMEOUT = 100; // ms
|
|||
* @property {Object} [CMapReaderFactory] - The factory that will be used when
|
||||
* reading built-in CMap files.
|
||||
* The default value is {DOMCMapReaderFactory}.
|
||||
* @property {string} [iccUrl] - The URL where the predefined ICC profiles are
|
||||
* located. Include the trailing slash.
|
||||
* @property {boolean} [useSystemFonts] - When `true`, fonts that aren't
|
||||
* embedded in the PDF document will fallback to a system font.
|
||||
* The default value is `true` in web environments and `false` in Node.js;
|
||||
|
@ -269,6 +271,7 @@ function getDocument(src = {}) {
|
|||
(typeof PDFJSDev !== "undefined" && PDFJSDev.test("GENERIC") && isNodeJS
|
||||
? NodeCMapReaderFactory
|
||||
: DOMCMapReaderFactory);
|
||||
const iccUrl = getFactoryUrlProp(src.iccUrl);
|
||||
const standardFontDataUrl = getFactoryUrlProp(src.standardFontDataUrl);
|
||||
const StandardFontDataFactory =
|
||||
src.StandardFontDataFactory ||
|
||||
|
@ -418,6 +421,7 @@ function getDocument(src = {}) {
|
|||
useWasm,
|
||||
useWorkerFetch,
|
||||
cMapUrl,
|
||||
iccUrl,
|
||||
standardFontDataUrl,
|
||||
wasmUrl,
|
||||
},
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue