Support rendering appearance streams for annotations

This commit is contained in:
Mack Duan 2013-03-13 12:24:55 -07:00
parent 772c7894fb
commit 79831d7ec5
6 changed files with 218 additions and 5 deletions

View File

@ -1450,6 +1450,38 @@ var CanvasGraphics = (function CanvasGraphicsClosure() {
this.restore();
},
beginAnnotation: function CanvasGraphics_beginAnnotation(rect, transform,
matrix, border) {
this.save();
if (rect && isArray(rect) && 4 == rect.length) {
var width = rect[2] - rect[0];
var height = rect[3] - rect[1];
if (border) {
// TODO(mack): Support different border styles
this.save();
var rgb = border.rgb;
this.setStrokeRGBColor(rgb[0], rgb[1], rgb[2]);
this.setLineWidth(border.width);
this.rectangle(rect[0], rect[1], width, height);
this.stroke();
this.restore();
}
this.rectangle(rect[0], rect[1], width, height);
this.clip();
this.endPath();
}
this.transform.apply(this, transform);
this.transform.apply(this, matrix);
},
endAnnotation: function CanvasGraphics_endAnnotation() {
this.restore();
},
paintJpegXObject: function CanvasGraphics_paintJpegXObject(objId, w, h) {
var domImage = this.objs.get(objId);
if (!domImage) {

View File

@ -100,7 +100,28 @@ function getPdf(arg, callback) {
globalScope.PDFJS.getPdf = getPdf;
globalScope.PDFJS.pdfBug = false;
var Page = (function PageClosure() {
function getDefaultAnnotationAppearance(annotationDict) {
var appearanceState = annotationDict.get('AP');
if (!isDict(appearanceState)) {
return;
}
var appearance;
var appearances = appearanceState.get('N');
if (isDict(appearances)) {
var as = annotationDict.get('AS');
if (as && appearances.has(as.name)) {
appearance = appearances.get(as.name);
}
} else {
appearance = appearances;
}
return appearance;
}
function Page(xref, pageIndex, pageDict, ref) {
this.pageIndex = pageIndex;
this.pageDict = pageDict;
@ -198,6 +219,17 @@ var Page = (function PageClosure() {
'p' + this.pageIndex + '_');
var list = pe.getOperatorList(contentStream, resources, dependency);
var annotations = this.getAnnotationsForDraw();
var annotationEvaluator = new PartialEvaluator(
xref, handler, this.pageIndex,
'p' + this.pageIndex + '_annotation');
var annotationsList = annotationEvaluator.getAnnotationsOperatorList(
annotations, dependency);
Util.concatenateToArray(list.fnArray, annotationsList.fnArray);
Util.concatenateToArray(list.argsArray, annotationsList.argsArray);
pe.optimizeQueue(list);
return list;
},
@ -227,7 +259,59 @@ var Page = (function PageClosure() {
}
return links;
},
getAnnotations: function Page_getAnnotations() {
var annotations = this.getAnnotationsBase();
var items = [];
for (var i = 0, length = annotations.length; i < length; ++i) {
items.push(annotations[i].item);
}
return items;
},
getAnnotationsForDraw: function Page_getAnnotationsForDraw() {
var annotations = this.getAnnotationsBase();
var items = [];
for (var i = 0, length = annotations.length; i < length; ++i) {
var item = annotations[i].item;
var annotationDict = annotations[i].dict;
item.annotationFlags = annotationDict.get('F');
var appearance = getDefaultAnnotationAppearance(annotationDict);
if (appearance &&
// TODO(mack): The proper implementation requires that the
// appearance stream overrides Name, but we're currently
// doing it the other way around for 'Text' annotations since we
// have special rendering for it
item.type !== 'Text') {
item.appearance = appearance;
var appearanceDict = appearance.dict;
item.resources = appearanceDict.get('Resources');
item.bbox = appearanceDict.get('BBox') || [0, 0, 1, 1];
item.matrix = appearanceDict.get('Matrix') || [1, 0, 0, 1, 0 ,0];
}
var border = annotationDict.get('BS');
if (isDict(border) && !item.appearance) {
var borderWidth = border.has('W') ? border.get('W') : 1;
if (borderWidth !== 0) {
item.border = {
width: borderWidth,
type: border.get('S') || 'S',
rgb: annotationDict.get('C') || [0, 0, 1]
};
}
}
items.push(item);
}
return items;
},
getAnnotationsBase: function Page_getAnnotationsBase() {
var xref = this.xref;
function getInheritableProperty(annotation, name) {
var item = annotation;
@ -267,11 +351,13 @@ var Page = (function PageClosure() {
var subtype = annotation.get('Subtype');
if (!isName(subtype))
continue;
var rect = annotation.get('Rect');
var item = {};
item.type = subtype.name;
item.rect = rect;
var rect = annotation.get('Rect');
item.rect = Util.normalizeRect(rect);
var includeAnnotation = true;
switch (subtype.name) {
case 'Link':
var a = annotation.get('A');
@ -316,6 +402,14 @@ var Page = (function PageClosure() {
var fieldType = getInheritableProperty(annotation, 'FT');
if (!isName(fieldType))
break;
// Do not display digital signatures since we do not currently
// validate them.
if (fieldType.name === 'Sig') {
includeAnnotation = false;
break;
}
item.fieldType = fieldType.name;
// Building the full field name by collecting the field and
// its ancestors 'T' properties and joining them using '.'.
@ -364,10 +458,18 @@ var Page = (function PageClosure() {
annotation.get('Name').name;
break;
default:
TODO('unimplemented annotation type: ' + subtype.name);
var appearance = getDefaultAnnotationAppearance(annotation);
if (!appearance) {
TODO('unimplemented annotation type: ' + subtype.name);
}
break;
}
items.push(item);
if (includeAnnotation) {
items.push({
item: item,
dict: annotation
});
}
}
return items;
}

View File

@ -19,7 +19,7 @@
IDENTITY_MATRIX, info, isArray, isCmd, isDict, isEOF, isName, isNum,
isStream, isString, JpegStream, Lexer, Metrics, Name, Parser,
Pattern, PDFImage, PDFJS, serifFonts, stdFontMap, symbolsFonts,
TilingPattern, TODO, warn */
TilingPattern, TODO, warn, Util */
'use strict';
@ -588,6 +588,73 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
return queue;
},
getAnnotationsOperatorList:
function PartialEvaluator_getAnnotationsOperatorList(annotations,
dependency) {
// 12.5.5: Algorithm: Appearance streams
function getTransformMatrix(rect, bbox, matrix) {
var bounds = Util.getAxialAlignedBoundingBox(bbox, matrix);
var minX = bounds[0];
var minY = bounds[1];
var maxX = bounds[2];
var maxY = bounds[3];
var width = rect[2] - rect[0];
var height = rect[3] - rect[1];
var xRatio = width / (maxX - minX);
var yRatio = height / (maxY - minY);
return [
xRatio,
0,
0,
yRatio,
rect[0] - minX * xRatio,
rect[1] - minY * yRatio
];
}
var fnArray = [];
var argsArray = [];
// deal with annotations
for (var i = 0, length = annotations.length; i < length; ++i) {
var annotation = annotations[i];
// check whether we can visualize annotation
if (!annotation ||
!annotation.annotationFlags ||
(annotation.annotationFlags & 0x0022) || // Hidden or NoView
!annotation.rect || // rectangle is nessessary
!annotation.appearance) { // appearance is nessessary
continue;
}
// apply rectangle
var rect = annotation.rect;
var bbox = annotation.bbox;
var matrix = annotation.matrix;
var transform = getTransformMatrix(rect, bbox, matrix);
var border = annotation.border;
fnArray.push('beginAnnotation');
argsArray.push([rect, transform, matrix, border]);
if (annotation.appearance) {
var list = this.getOperatorList(annotation.appearance,
annotation.resources, dependency);
Util.concatenateToArray(fnArray, list.fnArray);
Util.concatenateToArray(argsArray, list.argsArray);
}
fnArray.push('endAnnotation');
argsArray.push([]);
}
return {
fnArray: fnArray,
argsArray: argsArray
};
},
optimizeQueue: function PartialEvaluator_optimizeQueue(queue) {
var fnArray = queue.fnArray, argsArray = queue.argsArray;
// grouping paintInlineImageXObject's into paintInlineImageXObjectGroup

View File

@ -358,6 +358,10 @@ var Util = PDFJS.Util = (function UtilClosure() {
return num < 0 ? -1 : 1;
};
Util.concatenateToArray = function concatenateToArray(arr1, arr2) {
return Array.prototype.push.apply(arr1, arr2);
};
return Util;
})();

View File

@ -0,0 +1 @@
https://bug741239.bugzilla.mozilla.org/attachment.cgi?id=611315

View File

@ -924,6 +924,13 @@
"rounds": 1,
"type": "eq"
},
{ "id": "annotation-as",
"file": "pdfs/annotation-as.pdf",
"md5": "e51500c8adc9edcdcc8ebc6a575c90ab",
"rounds": 1,
"link": true,
"type": "eq"
},
{ "id": "javauninstall-7",
"file": "pdfs/javauninstall-7.pdf",
"md5": "c9eb59503923c9125b9660e348618675",