From a2e9845093118e25a93c1836a97df38adb6ac208 Mon Sep 17 00:00:00 2001 From: Tim van der Meij Date: Wed, 15 Jul 2015 16:59:25 +0200 Subject: [PATCH] Refactor annotation color handling and add unit tests --- src/core/annotation.js | 68 ++++++++++++++++++++---------- src/display/annotation_helper.js | 22 +++++----- test/unit/annotation_layer_spec.js | 58 ++++++++++++++++++++++++- 3 files changed, 112 insertions(+), 36 deletions(-) diff --git a/src/core/annotation.js b/src/core/annotation.js index 82b4e2af588ba..22ee0bf788720 100644 --- a/src/core/annotation.js +++ b/src/core/annotation.js @@ -17,7 +17,7 @@ /* globals PDFJS, Util, isDict, isName, stringToPDFString, warn, Dict, Stream, stringToBytes, Promise, isArray, ObjectLoader, OperatorList, isValidUrl, OPS, createPromiseCapability, AnnotationType, - stringToUTF8String, AnnotationBorderStyleType */ + stringToUTF8String, AnnotationBorderStyleType, ColorSpace */ 'use strict'; @@ -79,28 +79,8 @@ var Annotation = (function AnnotationClosure() { data.rect = Util.normalizeRect(rect); data.annotationFlags = dict.get('F'); - var color = dict.get('C'); - if (!color) { - // The PDF spec does not mention how a missing color array is interpreted. - // Adobe Reader seems to default to black in this case. - data.color = [0, 0, 0]; - } else if (isArray(color)) { - switch (color.length) { - case 0: - // Empty array denotes transparent border. - data.color = null; - break; - case 1: - // TODO: implement DeviceGray - break; - case 3: - data.color = color; - break; - case 4: - // TODO: implement DeviceCMYK - break; - } - } + this.setColor(dict.get('C')); + data.color = this.color; this.borderStyle = data.borderStyle = new AnnotationBorderStyle(); this.setBorderStyle(dict); @@ -111,6 +91,48 @@ var Annotation = (function AnnotationClosure() { } Annotation.prototype = { + /** + * Set the color and take care of color space conversion. + * + * @public + * @memberof Annotation + * @param {Array} color - The color array containing either 0 + * (transparent), 1 (grayscale), 3 (RGB) or + * 4 (CMYK) elements + */ + setColor: function Annotation_setColor(color) { + var rgbColor = new Uint8Array(3); // Black in RGB color space (default) + if (!isArray(color)) { + this.color = rgbColor; + return; + } + + switch (color.length) { + case 0: // Transparent, which we indicate with a null value + this.color = null; + break; + + case 1: // Convert grayscale to RGB + ColorSpace.singletons.gray.getRgbItem(color, 0, rgbColor, 0); + this.color = rgbColor; + break; + + case 3: // Convert RGB percentages to RGB + ColorSpace.singletons.rgb.getRgbItem(color, 0, rgbColor, 0); + this.color = rgbColor; + break; + + case 4: // Convert CMYK to RGB + ColorSpace.singletons.cmyk.getRgbItem(color, 0, rgbColor, 0); + this.color = rgbColor; + break; + + default: + this.color = rgbColor; + break; + } + }, + /** * Set the border style (as AnnotationBorderStyle object). * diff --git a/src/display/annotation_helper.js b/src/display/annotation_helper.js index 215c7c281b8b4..af30c28f2f809 100644 --- a/src/display/annotation_helper.js +++ b/src/display/annotation_helper.js @@ -100,12 +100,12 @@ var AnnotationUtils = (function AnnotationUtilsClosure() { // Border color if (item.color) { container.style.borderColor = - Util.makeCssRgb(Math.round(item.color[0] * 255), - Math.round(item.color[1] * 255), - Math.round(item.color[2] * 255)); + Util.makeCssRgb(item.color[0] | 0, + item.color[1] | 0, + item.color[2] | 0); } else { - // Default color is black, but that's not obvious from the spec. - container.style.borderColor = 'rgb(0,0,0)'; + // Transparent (invisible) border, so do not draw it at all. + container.style.borderWidth = 0; } } @@ -172,17 +172,15 @@ var AnnotationUtils = (function AnnotationUtilsClosure() { content.setAttribute('hidden', true); var i, ii; - if (item.hasBgColor) { + if (item.hasBgColor && item.color) { var color = item.color; // Enlighten the color (70%) var BACKGROUND_ENLIGHT = 0.7; - var r = BACKGROUND_ENLIGHT * (1.0 - color[0]) + color[0]; - var g = BACKGROUND_ENLIGHT * (1.0 - color[1]) + color[1]; - var b = BACKGROUND_ENLIGHT * (1.0 - color[2]) + color[2]; - content.style.backgroundColor = Util.makeCssRgb((r * 255) | 0, - (g * 255) | 0, - (b * 255) | 0); + var r = BACKGROUND_ENLIGHT * (255 - color[0]) + color[0]; + var g = BACKGROUND_ENLIGHT * (255 - color[1]) + color[1]; + var b = BACKGROUND_ENLIGHT * (255 - color[2]) + color[2]; + content.style.backgroundColor = Util.makeCssRgb(r | 0, g | 0, b | 0); } var title = document.createElement('h1'); diff --git a/test/unit/annotation_layer_spec.js b/test/unit/annotation_layer_spec.js index 728c59e3d7510..889004f3589b9 100644 --- a/test/unit/annotation_layer_spec.js +++ b/test/unit/annotation_layer_spec.js @@ -1,11 +1,67 @@ /* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */ -/* globals expect, it, describe, Dict, AnnotationBorderStyle, +/* globals expect, it, describe, Dict, Annotation, AnnotationBorderStyle, AnnotationBorderStyleType */ 'use strict'; describe('Annotation layer', function() { + describe('Annotation', function() { + it('should reject a color if it is not an array', function() { + var dict = new Dict(); + dict.set('Subtype', ''); + var annotation = new Annotation({ dict: dict, ref: 0 }); + annotation.setColor('red'); + + expect(annotation.color).toEqual([0, 0, 0]); + }); + + it('should set and get a transparent color', function() { + var dict = new Dict(); + dict.set('Subtype', ''); + var annotation = new Annotation({ dict: dict, ref: 0 }); + annotation.setColor([]); + + expect(annotation.color).toEqual(null); + }); + + it('should set and get a grayscale color', function() { + var dict = new Dict(); + dict.set('Subtype', ''); + var annotation = new Annotation({ dict: dict, ref: 0 }); + annotation.setColor([0.4]); + + expect(annotation.color).toEqual([102, 102, 102]); + }); + + it('should set and get an RGB color', function() { + var dict = new Dict(); + dict.set('Subtype', ''); + var annotation = new Annotation({ dict: dict, ref: 0 }); + annotation.setColor([0, 0, 1]); + + expect(annotation.color).toEqual([0, 0, 255]); + }); + + it('should set and get a CMYK color', function() { + var dict = new Dict(); + dict.set('Subtype', ''); + var annotation = new Annotation({ dict: dict, ref: 0 }); + annotation.setColor([0.1, 0.92, 0.84, 0.02]); + + expect(annotation.color).toEqual([233, 59, 47]); + }); + + it('should not set and get an invalid color', function() { + var dict = new Dict(); + dict.set('Subtype', ''); + var annotation = new Annotation({ dict: dict, ref: 0 }); + annotation.setColor([0.4, 0.6]); + + expect(annotation.color).toEqual([0, 0, 0]); + }); + }); + describe('AnnotationBorderStyle', function() { it('should set and get a valid width', function() { var borderStyle = new AnnotationBorderStyle();