@ -2201,18 +2201,20 @@ var QueueOptimizer = (function QueueOptimizerClosure() {
@@ -2201,18 +2201,20 @@ var QueueOptimizer = (function QueueOptimizerClosure() {
state [ pattern [ pattern . length - 1 ] ] = fn ;
}
function handlePaintSolidColorImageMask ( index , count , fnArray , argsArray ) {
// Handles special case of mainly LaTeX documents which
// use image masks to draw lines with the current fill style.
function handlePaintSolidColorImageMask ( iFirstSave , count , fnArray ,
argsArray ) {
// Handles special case of mainly LaTeX documents which use image masks to
// draw lines with the current fill style.
// 'count' groups of (save, transform, paintImageMaskXObject, restore)+
// have been found at index.
// have been found at iFirstSave.
var iFirstPIMXO = iFirstSave + 2 ;
for ( var i = 0 ; i < count ; i ++ ) {
var arg = argsArray [ index + 4 * i + 2 ] ;
var arg = argsArray [ iFirstPIMXO + 4 * i ] ;
var imageMask = arg . length === 1 && arg [ 0 ] ;
if ( imageMask && imageMask . width === 1 && imageMask . height === 1 &&
( ! imageMask . data . length || ( imageMask . data . length === 1 &&
imageMask . data [ 0 ] === 0 ) ) ) {
fnArray [ index + 4 * i + 2 ] = OPS . paintSolidColorImageMask ;
( ! imageMask . data . length ||
( imageMask . data . length === 1 && imageMask . data [ 0 ] === 0 ) ) ) {
fnArray [ iFirstPIMXO + 4 * i ] = OPS . paintSolidColorImageMask ;
continue ;
}
break ;
@ -2222,26 +2224,43 @@ var QueueOptimizer = (function QueueOptimizerClosure() {
@@ -2222,26 +2224,43 @@ var QueueOptimizer = (function QueueOptimizerClosure() {
var InitialState = [ ] ;
// This replaces (save, transform, paintInlineImageXObject, restore)+
// sequences with one |paintInlineImageXObjectGroup| operation.
addState ( InitialState ,
[ OPS . save , OPS . transform , OPS . paintInlineImageXObject , OPS . restore ] ,
function foundInlineImageGroup ( context ) {
// grouping paintInlineImageXObject's into paintInlineImageXObjectGroup
// searching for (save, transform, paintInlineImageXObject, restore)+
var MIN _IMAGES _IN _INLINE _IMAGES _BLOCK = 10 ;
var MAX _IMAGES _IN _INLINE _IMAGES _BLOCK = 200 ;
var MAX _WIDTH = 1000 ;
var IMAGE _PADDING = 1 ;
var fnArray = context . fnArray , argsArray = context . argsArray ;
var j = context . currentOperation - 3 , i = j + 4 ;
var curr = context . iCurr ;
var iFirstSave = curr - 3 ;
var iFirstTransform = curr - 2 ;
var iFirstPIIXO = curr - 1 ;
// Look for the quartets.
var i = iFirstSave + 4 ;
var ii = fnArray . length ;
while ( i + 3 < ii ) {
if ( fnArray [ i ] !== OPS . save ||
fnArray [ i + 1 ] !== OPS . transform ||
fnArray [ i + 2 ] !== OPS . paintInlineImageXObject ||
fnArray [ i + 3 ] !== OPS . restore ) {
break ; // ops don't match
}
i += 4 ;
}
for ( ; i < ii && fnArray [ i - 4 ] === fnArray [ i ] ; i ++ ) { }
var count = Math . min ( ( i - j ) >> 2 , MAX _IMAGES _IN _INLINE _IMAGES _BLOCK ) ;
// At this point, i is the index of the first op past the last valid
// quartet.
var count = Math . min ( ( i - iFirstSave ) / 4 ,
MAX _IMAGES _IN _INLINE _IMAGES _BLOCK ) ;
if ( count < MIN _IMAGES _IN _INLINE _IMAGES _BLOCK ) {
context . currentOperation = i - 1 ;
return ;
return i ;
}
// assuming that heights of those image is too small (~1 pixel)
// packing as much as possible by lines
var maxX = 0 ;
@ -2249,8 +2268,8 @@ var QueueOptimizer = (function QueueOptimizerClosure() {
@@ -2249,8 +2268,8 @@ var QueueOptimizer = (function QueueOptimizerClosure() {
var currentX = IMAGE _PADDING , currentY = IMAGE _PADDING ;
var q ;
for ( q = 0 ; q < count ; q ++ ) {
var transform = argsArray [ j + ( q << 2 ) + 1 ] ;
var img = argsArray [ j + ( q << 2 ) + 2 ] [ 0 ] ;
var transform = argsArray [ iFirstTransform + ( q << 2 ) ] ;
var img = argsArray [ iFirstPIIXO + ( q << 2 ) ] [ 0 ] ;
if ( currentX + img . width > MAX _WIDTH ) {
// starting new line
maxX = Math . max ( maxX , currentX ) ;
@ -2271,8 +2290,8 @@ var QueueOptimizer = (function QueueOptimizerClosure() {
@@ -2271,8 +2290,8 @@ var QueueOptimizer = (function QueueOptimizerClosure() {
var imgData = new Uint8Array ( imgWidth * imgHeight * 4 ) ;
var imgRowSize = imgWidth << 2 ;
for ( q = 0 ; q < count ; q ++ ) {
var data = argsArray [ j + ( q << 2 ) + 2 ] [ 0 ] . data ;
// c opy image by lines and extends pixels into padding
var data = argsArray [ iFirstPIIXO + ( q << 2 ) ] [ 0 ] . data ;
// C opy image by lines and extends pixels into padding.
var rowSize = map [ q ] . w << 2 ;
var dataOffset = 0 ;
var offset = ( map [ q ] . x + map [ q ] . y * imgWidth ) << 2 ;
@ -2295,48 +2314,72 @@ var QueueOptimizer = (function QueueOptimizerClosure() {
@@ -2295,48 +2314,72 @@ var QueueOptimizer = (function QueueOptimizerClosure() {
offset -= imgRowSize ;
}
}
// replacing queue items
fnArray . splice ( j , count * 4 , OPS . paintInlineImageXObjectGroup ) ;
argsArray . splice ( j , count * 4 ,
// Replace queue items.
fnArray . splice ( iFirstSave , count * 4 , OPS . paintInlineImageXObjectGroup ) ;
argsArray . splice ( iFirstSave , count * 4 ,
[ { width : imgWidth , height : imgHeight , kind : ImageKind . RGBA _32BPP ,
data : imgData } , map ] ) ;
context . currentOperation = j ;
return iFirstSave + 1 ;
} ) ;
// This replaces (save, transform, paintImageMaskXObject, restore)+
// sequences with one |paintImageMaskXObjectGroup| or one
// |paintImageMaskXObjectRepeat| operation.
addState ( InitialState ,
[ OPS . save , OPS . transform , OPS . paintImageMaskXObject , OPS . restore ] ,
function foundImageMaskGroup ( context ) {
// grouping paintImageMaskXObject's into paintImageMaskXObjectGroup
// searching for (save, transform, paintImageMaskXObject, restore)+
var MIN _IMAGES _IN _MASKS _BLOCK = 10 ;
var MAX _IMAGES _IN _MASKS _BLOCK = 100 ;
var MAX _SAME _IMAGES _IN _MASKS _BLOCK = 1000 ;
var fnArray = context . fnArray , argsArray = context . argsArray ;
var j = context . currentOperation - 3 , i = j + 4 ;
var ii = fnArray . length , q ;
var curr = context . iCurr ;
var iFirstSave = curr - 3 ;
var iFirstTransform = curr - 2 ;
var iFirstPIMXO = curr - 1 ;
for ( ; i < ii && fnArray [ i - 4 ] === fnArray [ i ] ; i ++ ) { }
var count = ( i - j ) >> 2 ;
count = handlePaintSolidColorImageMask ( j , count , fnArray , argsArray ) ;
// Look for the quartets.
var i = iFirstSave + 4 ;
var ii = fnArray . length ;
while ( i + 3 < ii ) {
if ( fnArray [ i ] !== OPS . save ||
fnArray [ i + 1 ] !== OPS . transform ||
fnArray [ i + 2 ] !== OPS . paintImageMaskXObject ||
fnArray [ i + 3 ] !== OPS . restore ) {
break ; // ops don't match
}
i += 4 ;
}
// At this point, i is the index of the first op past the last valid
// quartet.
var count = ( i - iFirstSave ) / 4 ;
count = handlePaintSolidColorImageMask ( iFirstSave , count , fnArray ,
argsArray ) ;
if ( count < MIN _IMAGES _IN _MASKS _BLOCK ) {
context . currentOperation = i - 1 ;
return ;
return i ;
}
var q ;
var isSameImage = false ;
var transformArgs ;
if ( argsArray [ j + 1 ] [ 1 ] === 0 && argsArray [ j + 1 ] [ 2 ] === 0 ) {
i = j + 4 ;
var iTransform , transformArgs ;
var firstPIMXOArg0 = argsArray [ iFirstPIMXO ] [ 0 ] ;
if ( argsArray [ iFirstTransform ] [ 1 ] === 0 &&
argsArray [ iFirstTransform ] [ 2 ] === 0 ) {
isSameImage = true ;
for ( q = 1 ; q < count ; q ++ , i += 4 ) {
var prevTransformArgs = argsArray [ i - 3 ] ;
transformArgs = argsArray [ i + 1 ] ;
if ( argsArray [ i - 2 ] [ 0 ] !== argsArray [ i + 2 ] [ 0 ] ||
prevTransformArgs [ 0 ] !== transformArgs [ 0 ] ||
prevTransformArgs [ 1 ] !== transformArgs [ 1 ] ||
prevTransformArgs [ 2 ] !== transformArgs [ 2 ] ||
prevTransformArgs [ 3 ] !== transformArgs [ 3 ] ) {
var firstTransformArg0 = argsArray [ iFirstTransform ] [ 0 ] ;
var firstTransformArg3 = argsArray [ iFirstTransform ] [ 3 ] ;
iTransform = iFirstTransform + 4 ;
var iPIMXO = iFirstPIMXO + 4 ;
for ( q = 1 ; q < count ; q ++ , iTransform += 4 , iPIMXO += 4 ) {
transformArgs = argsArray [ iTransform ] ;
if ( argsArray [ iPIMXO ] [ 0 ] !== firstPIMXOArg0 ||
transformArgs [ 0 ] !== firstTransformArg0 ||
transformArgs [ 1 ] !== 0 ||
transformArgs [ 2 ] !== 0 ||
transformArgs [ 3 ] !== firstTransformArg3 ) {
if ( q < MIN _IMAGES _IN _MASKS _BLOCK ) {
isSameImage = false ;
} else {
@ -2350,39 +2393,39 @@ var QueueOptimizer = (function QueueOptimizerClosure() {
@@ -2350,39 +2393,39 @@ var QueueOptimizer = (function QueueOptimizerClosure() {
if ( isSameImage ) {
count = Math . min ( count , MAX _SAME _IMAGES _IN _MASKS _BLOCK ) ;
var positions = new Float32Array ( count * 2 ) ;
i = j + 1 ;
for ( q = 0 ; q < count ; q ++ ) {
transformArgs = argsArray [ i ] ;
iTransform = iFirstTransform ;
for ( q = 0 ; q < count ; q ++ , iTransform += 4 ) {
transformArgs = argsArray [ iTransform ] ;
positions [ ( q << 1 ) ] = transformArgs [ 4 ] ;
positions [ ( q << 1 ) + 1 ] = transformArgs [ 5 ] ;
i += 4 ;
}
// replacing queue items
fnArray . splice ( j , count * 4 , OPS . paintImageMaskXObjectRepeat ) ;
argsArray . splice ( j , count * 4 , [ argsArray [ j + 2 ] [ 0 ] ,
argsArray [ j + 1 ] [ 0 ] , argsArray [ j + 1 ] [ 3 ] , positions ] ) ;
context . currentOperation = j ;
// Replace queue items.
fnArray . splice ( iFirstSave , count * 4 , OPS . paintImageMaskXObjectRepeat ) ;
argsArray . splice ( iFirstSave , count * 4 ,
[ firstPIMXOArg0 , firstTransformArg0 , firstTransformArg3 , positions ] ) ;
} else {
count = Math . min ( count , MAX _IMAGES _IN _MASKS _BLOCK ) ;
var images = [ ] ;
for ( q = 0 ; q < count ; q ++ ) {
transformArgs = argsArray [ j + ( q << 2 ) + 1 ] ;
var maskParams = argsArray [ j + ( q << 2 ) + 2 ] [ 0 ] ;
transformArgs = argsArray [ iFirstTransform + ( q << 2 ) ] ;
var maskParams = argsArray [ iFirstPIMXO + ( q << 2 ) ] [ 0 ] ;
images . push ( { data : maskParams . data , width : maskParams . width ,
height : maskParams . height ,
transform : transformArgs } ) ;
}
// replacing queue items
fnArray . splice ( j , count * 4 , OPS . paintImageMaskXObjectGroup ) ;
argsArray . splice ( j , count * 4 , [ images ] ) ;
context . currentOperation = j ;
// Replace queue items.
fnArray . splice ( iFirstSave , count * 4 , OPS . paintImageMaskXObjectGroup ) ;
argsArray . splice ( iFirstSave , count * 4 , [ images ] ) ;
}
return iFirstSave + 1 ;
} ) ;
// This replaces (save, transform, paintImageXObject, restore)+ sequences
// with one paintImageXObjectRepeat operation, if the |transform| and
// |paintImageXObjectRepeat| ops are appropriate.
addState ( InitialState ,
[ OPS . save , OPS . transform , OPS . paintImageXObject , OPS . restore ] ,
function ( context ) {
@ -2390,103 +2433,135 @@ var QueueOptimizer = (function QueueOptimizerClosure() {
@@ -2390,103 +2433,135 @@ var QueueOptimizer = (function QueueOptimizerClosure() {
var MAX _IMAGES _IN _BLOCK = 1000 ;
var fnArray = context . fnArray , argsArray = context . argsArray ;
var j = context . currentOperation - 3 , i = j + 4 ;
if ( argsArray [ j + 1 ] [ 1 ] !== 0 || argsArray [ j + 1 ] [ 2 ] !== 0 ) {
return ;
}
var curr = context . iCurr ;
var iFirstSave = curr - 3 ;
var iFirstTransform = curr - 2 ;
var iFirstPIXO = curr - 1 ;
var iFirstRestore = curr ;
if ( argsArray [ iFirstTransform ] [ 1 ] !== 0 ||
argsArray [ iFirstTransform ] [ 2 ] !== 0 ) {
return iFirstRestore + 1 ; // transform has the wrong form
}
// Look for the quartets.
var firstPIXOArg0 = argsArray [ iFirstPIXO ] [ 0 ] ;
var firstTransformArg0 = argsArray [ iFirstTransform ] [ 0 ] ;
var firstTransformArg3 = argsArray [ iFirstTransform ] [ 3 ] ;
var i = iFirstSave + 4 ;
var ii = fnArray . length ;
var transformArgs ;
for ( ; i + 3 < ii && fnArray [ i - 4 ] === fnArray [ i ] ; i += 4 ) {
if ( fnArray [ i - 3 ] !== fnArray [ i + 1 ] ||
fnArray [ i - 2 ] !== fnArray [ i + 2 ] ||
fnArray [ i - 1 ] !== fnArray [ i + 3 ] ) {
break ;
while ( i + 3 < ii ) {
if ( fnArray [ i ] !== OPS . save ||
fnArray [ i + 1 ] !== OPS . transform ||
fnArray [ i + 2 ] !== OPS . paintImageXObject ||
fnArray [ i + 3 ] !== OPS . restore ) {
break ; // ops don't match
}
if ( argsArray [ i - 2 ] [ 0 ] !== argsArray [ i + 2 ] [ 0 ] ) {
break ; // different image
if ( argsArray [ i + 1 ] [ 0 ] !== firstTransformArg0 ||
argsArray [ i + 1 ] [ 1 ] !== 0 ||
argsArray [ i + 1 ] [ 2 ] !== 0 ||
argsArray [ i + 1 ] [ 3 ] !== firstTransformArg3 ) {
break ; // transforms don't match
}
var prevTransformArgs = argsArray [ i - 3 ] ;
transformArgs = argsArray [ i + 1 ] ;
if ( prevTransformArgs [ 0 ] !== transformArgs [ 0 ] ||
prevTransformArgs [ 1 ] !== transformArgs [ 1 ] ||
prevTransformArgs [ 2 ] !== transformArgs [ 2 ] ||
prevTransformArgs [ 3 ] !== transformArgs [ 3 ] ) {
break ; // different transform
if ( argsArray [ i + 2 ] [ 0 ] !== firstPIXOArg0 ) {
break ; // images don't match
}
i += 4 ;
}
var count = Math . min ( ( i - j ) >> 2 , MAX _IMAGES _IN _BLOCK ) ;
// At this point, i is the index of the first op past the last valid
// quartet.
var count = Math . min ( ( i - iFirstSave ) / 4 , MAX _IMAGES _IN _BLOCK ) ;
if ( count < MIN _IMAGES _IN _BLOCK ) {
context . currentOperation = i - 1 ;
return ;
return i ;
}
// Extract the (x,y) positions from all of the matching transforms.
var positions = new Float32Array ( count * 2 ) ;
i = j + 1 ;
for ( var q = 0 ; q < count ; q ++ ) {
transformArgs = argsArray [ i ] ;
var iTransform = iFirstTransform ;
for ( var q = 0 ; q < count ; q ++ , iTransform += 4 ) {
var transformArgs = argsArray [ iTransform ] ;
positions [ ( q << 1 ) ] = transformArgs [ 4 ] ;
positions [ ( q << 1 ) + 1 ] = transformArgs [ 5 ] ;
i += 4 ;
}
var args = [ argsArray [ j + 2 ] [ 0 ] , argsArray [ j + 1 ] [ 0 ] ,
argsArray [ j + 1 ] [ 3 ] , positions ] ;
// replacing queue items
fnArray . splice ( j , count * 4 , OPS . paintImageXObjectRepeat ) ;
argsArray . splice ( j , count * 4 , args ) ;
context . currentOperation = j ;
// Replace queue items.
var args = [ firstPIXOArg0 , firstTransformArg0 , firstTransformArg3 ,
positions ] ;
fnArray . splice ( iFirstSave , count * 4 , OPS . paintImageXObjectRepeat ) ;
argsArray . splice ( iFirstSave , count * 4 , args ) ;
return iFirstSave + 1 ;
} ) ;
// This replaces (beginText, setFont, setTextMatrix, showText, endText)+
// sequences with (beginText, setFont, (setTextMatrix, showText)+, endText)+
// sequences, if the font for each one is the same.
addState ( InitialState ,
[ OPS . beginText , OPS . setFont , OPS . setTextMatrix , OPS . showText , OPS . endText ] ,
function ( context ) {
// moving single chars with same font into beginText/endText groups
// searching for (beginText, setFont, setTextMatrix, showText, endText)+
var MIN _CHARS _IN _BLOCK = 3 ;
var MAX _CHARS _IN _BLOCK = 1000 ;
var fnArray = context . fnArray , argsArray = context . argsArray ;
var j = context . currentOperation - 4 , i = j + 5 ;
var curr = context . iCurr ;
var iFirstBeginText = curr - 4 ;
var iFirstSetFont = curr - 3 ;
var iFirstSetTextMatrix = curr - 2 ;
var iFirstShowText = curr - 1 ;
var iFirstEndText = curr ;
// Look for the quintets.
var firstSetFontArg0 = argsArray [ iFirstSetFont ] [ 0 ] ;
var firstSetFontArg1 = argsArray [ iFirstSetFont ] [ 1 ] ;
var i = iFirstBeginText + 5 ;
var ii = fnArray . length ;
for ( ; i < ii && fnArray [ i - 5 ] === fnArray [ i ] ; i ++ ) {
if ( fnArray [ i ] === OPS . setFont ) {
if ( argsArray [ i - 5 ] [ 0 ] !== argsArray [ i ] [ 0 ] ||
argsArray [ i - 5 ] [ 1 ] !== argsArray [ i ] [ 1 ] ) {
break ;
}
while ( i + 4 < ii ) {
if ( fnArray [ i ] !== OPS . beginText ||
fnArray [ i + 1 ] !== OPS . setFont ||
fnArray [ i + 2 ] !== OPS . setTextMatrix ||
fnArray [ i + 3 ] !== OPS . showText ||
fnArray [ i + 4 ] !== OPS . endText ) {
break ; // ops don't match
}
if ( argsArray [ i + 1 ] [ 0 ] !== firstSetFontArg0 ||
argsArray [ i + 1 ] [ 1 ] !== firstSetFontArg1 ) {
break ; // fonts don't match
}
i += 5 ;
}
var count = Math . min ( ( ( i - j ) / 5 ) | 0 , MAX _CHARS _IN _BLOCK ) ;
// At this point, i is the index of the first op past the last valid
// quintet.
var count = Math . min ( ( ( i - iFirstBeginText ) / 5 ) , MAX _CHARS _IN _BLOCK ) ;
if ( count < MIN _CHARS _IN _BLOCK ) {
context . currentOperation = i - 1 ;
return ;
}
if ( j >= 4 && fnArray [ j - 4 ] === fnArray [ j + 1 ] &&
fnArray [ j - 3 ] === fnArray [ j + 2 ] &&
fnArray [ j - 2 ] === fnArray [ j + 3 ] &&
fnArray [ j - 1 ] === fnArray [ j + 4 ] &&
argsArray [ j - 4 ] [ 0 ] === argsArray [ j + 1 ] [ 0 ] &&
argsArray [ j - 4 ] [ 1 ] === argsArray [ j + 1 ] [ 1 ] ) {
// extending one block ahead (very first block might have 'dependency')
return i ;
}
// If the preceding quintet is (<something>, setFont, setTextMatrix,
// showText, endText), include that as well. (E.g. <something> might be
// |dependency|.)
var iFirst = iFirstBeginText ;
if ( iFirstBeginText >= 4 &&
fnArray [ iFirstBeginText - 4 ] === fnArray [ iFirstSetFont ] &&
fnArray [ iFirstBeginText - 3 ] === fnArray [ iFirstSetTextMatrix ] &&
fnArray [ iFirstBeginText - 2 ] === fnArray [ iFirstShowText ] &&
fnArray [ iFirstBeginText - 1 ] === fnArray [ iFirstEndText ] &&
argsArray [ iFirstBeginText - 4 ] [ 0 ] === firstSetFontArg0 &&
argsArray [ iFirstBeginText - 4 ] [ 1 ] === firstSetFontArg1 ) {
count ++ ;
j -= 5 ;
iFirst -= 5 ;
}
var k = j + 7 ;
i = j + 4 ;
// Remove (endText, beginText, setFont) trios.
var iEndText = iFirst + 4 ;
for ( var q = 1 ; q < count ; q ++ ) {
fnArray [ i ] = fnArray [ k ] ;
argsArray [ i ] = argsArray [ k ] ;
fnArray [ i + 1 ] = fnArray [ k + 1 ] ;
argsArray [ i + 1 ] = argsArray [ k + 1 ] ;
i += 2 ;
k += 5 ;
}
var removed = ( count - 1 ) * 3 ;
fnArray . splice ( i , removed ) ;
argsArray . splice ( i , removed ) ;
context . currentOperation = i ;
fnArray . splice ( iEndText , 3 ) ;
argsArray . splice ( iEndText , 3 ) ;
iEndText += 2 ;
}
return iEndText + 1 ;
} ) ;
function QueueOptimizer ( ) { }
@ -2495,19 +2570,24 @@ var QueueOptimizer = (function QueueOptimizerClosure() {
@@ -2495,19 +2570,24 @@ var QueueOptimizer = (function QueueOptimizerClosure() {
optimize : function QueueOptimizer _optimize ( queue ) {
var fnArray = queue . fnArray , argsArray = queue . argsArray ;
var context = {
currentOperation : 0 ,
iCurr : 0 ,
fnArray : fnArray ,
argsArray : argsArray
} ;
var i , ii = argsArray . length ;
var state ;
for ( i = 0 ; i < ii ; i ++ ) {
var i = 0 , ii = fnArray . length ;
while ( i < ii ) {
state = ( state || InitialState ) [ fnArray [ i ] ] ;
if ( typeof state === 'function' ) { // we found some handler
context . currentOperation = i ;
state = state ( context ) ;
i = context . currentOperation ;
context . iCurr = i ;
// state() returns the index of the first non-matching op (if we
// didn't match) or the first op past the modified ops (if we did
// match and replace).
i = state ( context ) ;
state = undefined ; // reset the state machine
ii = context . fnArray . length ;
} else {
i ++ ;
}
}
}