@@ -191,6 +191,10 @@ function mirrorContextOperations(ctx, destCtx) {
191
191
}
192
192
193
193
function addContextCurrentTransform ( ctx ) {
194
+ if ( ctx . _transformStack ) {
195
+ // Reset the transform stack.
196
+ ctx . _transformStack = [ ] ;
197
+ }
194
198
// If the context doesn't expose a `mozCurrentTransform`, add a JS based one.
195
199
if ( ctx . mozCurrentTransform ) {
196
200
return ;
@@ -265,6 +269,9 @@ function addContextCurrentTransform(ctx) {
265
269
} ;
266
270
267
271
ctx . restore = function ctxRestore ( ) {
272
+ if ( this . _transformStack . length === 0 ) {
273
+ warn ( "Tried to restore a ctx when the stack was already empty." ) ;
274
+ }
268
275
const prev = this . _transformStack . pop ( ) ;
269
276
if ( prev ) {
270
277
this . _transformMatrix = prev ;
@@ -1242,7 +1249,7 @@ class CanvasGraphics {
1242
1249
1243
1250
endDrawing ( ) {
1244
1251
// Finishing all opened operations such as SMask group painting.
1245
- while ( this . stateStack . length || this . current . activeSMask !== null ) {
1252
+ while ( this . stateStack . length || this . inSMaskMode ) {
1246
1253
this . restore ( ) ;
1247
1254
}
1248
1255
@@ -1503,8 +1510,12 @@ class CanvasGraphics {
1503
1510
}
1504
1511
}
1505
1512
1513
+ get inSMaskMode ( ) {
1514
+ return ! ! this . suspendedCtx ;
1515
+ }
1516
+
1506
1517
checkSMaskState ( ) {
1507
- const inSMaskMode = ! ! this . suspendedCtx ;
1518
+ const inSMaskMode = this . inSMaskMode ;
1508
1519
if ( this . current . activeSMask && ! inSMaskMode ) {
1509
1520
this . beginSMaskMode ( ) ;
1510
1521
} else if ( ! this . current . activeSMask && inSMaskMode ) {
@@ -1523,7 +1534,7 @@ class CanvasGraphics {
1523
1534
* the right order on the canvas' graphics state stack.
1524
1535
*/
1525
1536
beginSMaskMode ( ) {
1526
- if ( this . suspendedCtx ) {
1537
+ if ( this . inSMaskMode ) {
1527
1538
throw new Error ( "beginSMaskMode called while already in smask mode" ) ;
1528
1539
}
1529
1540
const drawnWidth = this . ctx . canvas . width ;
@@ -1550,7 +1561,7 @@ class CanvasGraphics {
1550
1561
}
1551
1562
1552
1563
endSMaskMode ( ) {
1553
- if ( ! this . suspendedCtx ) {
1564
+ if ( ! this . inSMaskMode ) {
1554
1565
throw new Error ( "endSMaskMode called while not in smask mode" ) ;
1555
1566
}
1556
1567
// The soft mask is done, now restore the suspended canvas as the main
@@ -1559,7 +1570,6 @@ class CanvasGraphics {
1559
1570
copyCtxState ( this . ctx , this . suspendedCtx ) ;
1560
1571
this . ctx = this . suspendedCtx ;
1561
1572
1562
- this . current . activeSMask = null ;
1563
1573
this . suspendedCtx = null ;
1564
1574
}
1565
1575
@@ -1589,20 +1599,36 @@ class CanvasGraphics {
1589
1599
}
1590
1600
1591
1601
save ( ) {
1592
- this . ctx . save ( ) ;
1602
+ if ( this . inSMaskMode ) {
1603
+ // SMask mode may be turned on/off causing us to lose graphics state.
1604
+ // Copy the temporary canvas state to the main(suspended) canvas to keep
1605
+ // it in sync.
1606
+ copyCtxState ( this . ctx , this . suspendedCtx ) ;
1607
+ // Don't bother calling save on the temporary canvas since state is not
1608
+ // saved there.
1609
+ this . suspendedCtx . save ( ) ;
1610
+ } else {
1611
+ this . ctx . save ( ) ;
1612
+ }
1593
1613
const old = this . current ;
1594
1614
this . stateStack . push ( old ) ;
1595
1615
this . current = old . clone ( ) ;
1596
1616
}
1597
1617
1598
1618
restore ( ) {
1599
- if ( this . stateStack . length === 0 && this . current . activeSMask ) {
1619
+ if ( this . stateStack . length === 0 && this . inSMaskMode ) {
1600
1620
this . endSMaskMode ( ) ;
1601
1621
}
1602
-
1603
1622
if ( this . stateStack . length !== 0 ) {
1604
1623
this . current = this . stateStack . pop ( ) ;
1605
- this . ctx . restore ( ) ;
1624
+ if ( this . inSMaskMode ) {
1625
+ // Graphics state is stored on the main(suspended) canvas. Restore its
1626
+ // state then copy it over to the temporary canvas.
1627
+ this . suspendedCtx . restore ( ) ;
1628
+ copyCtxState ( this . suspendedCtx , this . ctx ) ;
1629
+ } else {
1630
+ this . ctx . restore ( ) ;
1631
+ }
1606
1632
this . checkSMaskState ( ) ;
1607
1633
1608
1634
// Ensure that the clipping path is reset (fixes issue6413.pdf).
@@ -2525,9 +2551,8 @@ class CanvasGraphics {
2525
2551
this . save ( ) ;
2526
2552
// If there's an active soft mask we don't want it enabled for the group, so
2527
2553
// clear it out. The mask and suspended canvas will be restored in endGroup.
2528
- const suspendedCtx = this . suspendedCtx ;
2529
- if ( this . current . activeSMask ) {
2530
- this . suspendedCtx = null ;
2554
+ if ( this . inSMaskMode ) {
2555
+ this . endSMaskMode ( ) ;
2531
2556
this . current . activeSMask = null ;
2532
2557
}
2533
2558
@@ -2646,10 +2671,7 @@ class CanvasGraphics {
2646
2671
[ "ca" , 1 ] ,
2647
2672
[ "CA" , 1 ] ,
2648
2673
] ) ;
2649
- this . groupStack . push ( {
2650
- ctx : currentCtx ,
2651
- suspendedCtx,
2652
- } ) ;
2674
+ this . groupStack . push ( currentCtx ) ;
2653
2675
this . groupLevel ++ ;
2654
2676
}
2655
2677
@@ -2659,16 +2681,12 @@ class CanvasGraphics {
2659
2681
}
2660
2682
this . groupLevel -- ;
2661
2683
const groupCtx = this . ctx ;
2662
- const { ctx, suspendedCtx } = this . groupStack . pop ( ) ;
2684
+ const ctx = this . groupStack . pop ( ) ;
2663
2685
this . ctx = ctx ;
2664
2686
// Turn off image smoothing to avoid sub pixel interpolation which can
2665
2687
// look kind of blurry for some pdfs.
2666
2688
this . ctx . imageSmoothingEnabled = false ;
2667
2689
2668
- if ( suspendedCtx ) {
2669
- this . suspendedCtx = suspendedCtx ;
2670
- }
2671
-
2672
2690
if ( group . smask ) {
2673
2691
this . tempSMask = this . smaskStack . pop ( ) ;
2674
2692
this . restore ( ) ;
0 commit comments