@@ -173,6 +173,7 @@ class AnnotationElement {
173173 this . renderForms = parameters . renderForms ;
174174 this . svgFactory = parameters . svgFactory ;
175175 this . annotationStorage = parameters . annotationStorage ;
176+ this . enableComment = parameters . enableComment ;
176177 this . enableScripting = parameters . enableScripting ;
177178 this . hasJSActions = parameters . hasJSActions ;
178179 this . _fieldObjects = parameters . fieldObjects ;
@@ -198,6 +199,92 @@ class AnnotationElement {
198199 return AnnotationElement . _hasPopupData ( this . data ) ;
199200 }
200201
202+ get hasCommentButton ( ) {
203+ return this . enableComment && this . _isEditable && this . hasPopupElement ;
204+ }
205+
206+ get commentButtonPosition ( ) {
207+ const { quadPoints, rect } = this . data ;
208+ let maxX = - Infinity ;
209+ let maxY = - Infinity ;
210+ if ( quadPoints ?. length >= 8 ) {
211+ for ( let i = 0 ; i < quadPoints . length ; i += 8 ) {
212+ if ( quadPoints [ i + 1 ] > maxY ) {
213+ maxY = quadPoints [ i + 1 ] ;
214+ maxX = quadPoints [ i + 2 ] ;
215+ } else if ( quadPoints [ i + 1 ] === maxY ) {
216+ maxX = Math . max ( maxX , quadPoints [ i + 2 ] ) ;
217+ }
218+ }
219+ return [ maxX , maxY ] ;
220+ }
221+ if ( rect ) {
222+ return [ rect [ 2 ] , rect [ 3 ] ] ;
223+ }
224+ return null ;
225+ }
226+
227+ get commentButtonColor ( ) {
228+ if ( ! this . data . color ) {
229+ return null ;
230+ }
231+ const [ r , g , b ] = this . data . color ;
232+ const opacity = this . data . opacity ?? 1 ;
233+ const oppositeOpacity = 255 * ( 1 - opacity ) ;
234+
235+ return this . #changeLightness(
236+ Math . min ( r + oppositeOpacity , 255 ) ,
237+ Math . min ( g + oppositeOpacity , 255 ) ,
238+ Math . min ( b + oppositeOpacity , 255 )
239+ ) ;
240+ }
241+
242+ #changeLightness( r , g , b ) {
243+ r /= 255 ;
244+ g /= 255 ;
245+ b /= 255 ;
246+
247+ const max = Math . max ( r , g , b ) ;
248+ const min = Math . min ( r , g , b ) ;
249+ const l = ( max + min ) / 2 ;
250+ const newL = ( ( ( 1 + Math . sqrt ( l ) ) / 2 ) * 100 ) . toFixed ( 2 ) ;
251+
252+ if ( max === min ) {
253+ // gray
254+ return `hsl(0, 0%, ${ newL } %)` ;
255+ }
256+
257+ const d = max - min ;
258+
259+ // hue (branch on max only; avoids mod)
260+ let h ;
261+ if ( max === r ) {
262+ h = ( g - b ) / d + ( g < b ? 6 : 0 ) ;
263+ } else if ( max === g ) {
264+ h = ( b - r ) / d + 2 ;
265+ } else {
266+ // max === b
267+ h = ( r - g ) / d + 4 ;
268+ }
269+ h = ( h * 60 ) . toFixed ( 2 ) ;
270+ const s = ( ( d / ( 1 - Math . abs ( 2 * l - 1 ) ) ) * 100 ) . toFixed ( 2 ) ;
271+
272+ return `hsl(${ h } , ${ s } %, ${ newL } %)` ;
273+ }
274+
275+ _normalizePoint ( point ) {
276+ const {
277+ page : { view } ,
278+ viewport : {
279+ rawDims : { pageWidth, pageHeight, pageX, pageY } ,
280+ } ,
281+ } = this . parent ;
282+ point [ 1 ] = view [ 3 ] - point [ 1 ] + view [ 1 ] ;
283+ point [ 0 ] = ( 100 * ( point [ 0 ] - pageX ) ) / pageWidth ;
284+ point [ 1 ] = ( 100 * ( point [ 1 ] - pageY ) ) / pageHeight ;
285+ return point ;
286+ }
287+
201288 updateEdited ( params ) {
202289 if ( ! this . container ) {
203290 return ;
@@ -290,7 +377,9 @@ class AnnotationElement {
290377 // But if an annotation is above an other one, then we must draw it
291378 // after the other one whatever the order is in the DOM, hence the
292379 // use of the z-index.
293- style . zIndex = this . parent . zIndex ++ ;
380+ style . zIndex = this . parent . zIndex ;
381+ // Keep zIndex + 1 for stuff we want to add on top of this annotation.
382+ this . parent . zIndex += 2 ;
294383
295384 if ( data . alternativeText ) {
296385 container . title = data . alternativeText ;
@@ -2194,6 +2283,7 @@ class PopupAnnotationElement extends AnnotationElement {
21942283 parent : this . parent ,
21952284 elements : this . elements ,
21962285 open : this . data . open ,
2286+ eventBus : this . linkService . eventBus ,
21972287 } ) ) ;
21982288
21992289 const elementIds = [ ] ;
@@ -2232,6 +2322,8 @@ class PopupElement {
22322322
22332323 #elements = null ;
22342324
2325+ #eventBus = null ;
2326+
22352327 #parent = null ;
22362328
22372329 #parentRect = null ;
@@ -2244,6 +2336,12 @@ class PopupElement {
22442336
22452337 #position = null ;
22462338
2339+ #commentButton = null ;
2340+
2341+ #commentButtonPosition = null ;
2342+
2343+ #commentButtonColor = null ;
2344+
22472345 #rect = null ;
22482346
22492347 #richText = null ;
@@ -2266,6 +2364,7 @@ class PopupElement {
22662364 rect,
22672365 parentRect,
22682366 open,
2367+ eventBus = null ,
22692368 } ) {
22702369 this . #container = container ;
22712370 this . #titleObj = titleObj ;
@@ -2276,6 +2375,7 @@ class PopupElement {
22762375 this . #rect = rect ;
22772376 this . #parentRect = parentRect ;
22782377 this . #elements = elements ;
2378+ this . #eventBus = eventBus ;
22792379
22802380 // The modification date is shown in the popup instead of the creation
22812381 // date if it is available and can be parsed correctly, which is
@@ -2322,6 +2422,68 @@ class PopupElement {
23222422 signal,
23232423 } ) ;
23242424 }
2425+
2426+ this . #renderCommentButton( ) ;
2427+ }
2428+
2429+ #setCommentButtonPosition( ) {
2430+ const element = this . #elements. find ( e => e . hasCommentButton ) ;
2431+ if ( ! element ) {
2432+ return ;
2433+ }
2434+ this . #commentButtonPosition = element . _normalizePoint (
2435+ element . commentButtonPosition
2436+ ) ;
2437+ this . #commentButtonColor = element . commentButtonColor ;
2438+ }
2439+
2440+ #renderCommentButton( ) {
2441+ if ( this . #commentButton) {
2442+ return ;
2443+ }
2444+
2445+ if ( ! this . #commentButtonPosition) {
2446+ this . #setCommentButtonPosition( ) ;
2447+ }
2448+
2449+ if ( ! this . #commentButtonPosition) {
2450+ return ;
2451+ }
2452+
2453+ const button = ( this . #commentButton = document . createElement ( "button" ) ) ;
2454+ button . className = "annotationCommentButton" ;
2455+ const parentContainer = this . #elements[ 0 ] . container ;
2456+ button . style . zIndex = parentContainer . style . zIndex + 1 ;
2457+ button . tabIndex = 0 ;
2458+
2459+ const { signal } = this . #popupAbortController;
2460+ button . addEventListener ( "hover" , this . #boundToggle, { signal } ) ;
2461+ button . addEventListener ( "keydown" , this . #boundKeyDown, { signal } ) ;
2462+ button . addEventListener (
2463+ "click" ,
2464+ ( ) => {
2465+ const [
2466+ {
2467+ data : { id : editId } ,
2468+ annotationEditorType : mode ,
2469+ } ,
2470+ ] = this . #elements;
2471+ this . #eventBus?. dispatch ( "switchannotationeditormode" , {
2472+ source : this ,
2473+ editId,
2474+ mode,
2475+ editComment : true ,
2476+ } ) ;
2477+ } ,
2478+ { signal }
2479+ ) ;
2480+ const { style } = button ;
2481+ style . left = `calc(${ this . #commentButtonPosition[ 0 ] } % + var(--comment-button-offset))` ;
2482+ style . top = `calc(${ this . #commentButtonPosition[ 1 ] } % - var(--comment-button-dim) - var(--comment-button-offset))` ;
2483+ if ( this . #commentButtonColor) {
2484+ style . backgroundColor = this . #commentButtonColor;
2485+ }
2486+ parentContainer . after ( button ) ;
23252487 }
23262488
23272489 render ( ) {
@@ -3053,6 +3215,31 @@ class InkAnnotationElement extends AnnotationElement {
30533215 addHighlightArea ( ) {
30543216 this . container . classList . add ( "highlightArea" ) ;
30553217 }
3218+
3219+ get commentButtonPosition ( ) {
3220+ const { inkLists, rect } = this . data ;
3221+ if ( inkLists ?. length >= 1 ) {
3222+ let maxX = - Infinity ;
3223+ let maxY = - Infinity ;
3224+ for ( const inkList of inkLists ) {
3225+ for ( let i = 0 , ii = inkList . length ; i < ii ; i += 2 ) {
3226+ if ( inkList [ i + 1 ] > maxY ) {
3227+ maxY = inkList [ i + 1 ] ;
3228+ maxX = inkList [ i ] ;
3229+ } else if ( inkList [ i + 1 ] === maxY ) {
3230+ maxX = Math . max ( maxX , inkList [ i ] ) ;
3231+ }
3232+ }
3233+ }
3234+ if ( maxX !== Infinity ) {
3235+ return [ maxX , maxY ] ;
3236+ }
3237+ }
3238+ if ( rect ) {
3239+ return [ rect [ 2 ] , rect [ 3 ] ] ;
3240+ }
3241+ return null ;
3242+ }
30563243}
30573244
30583245class HighlightAnnotationElement extends AnnotationElement {
@@ -3391,6 +3578,7 @@ class AnnotationLayer {
33913578 renderForms : params . renderForms !== false ,
33923579 svgFactory : new DOMSVGFactory ( ) ,
33933580 annotationStorage : params . annotationStorage || new AnnotationStorage ( ) ,
3581+ enableComment : params . enableComment === true ,
33943582 enableScripting : params . enableScripting === true ,
33953583 hasJSActions : params . hasJSActions ,
33963584 fieldObjects : params . fieldObjects ,
0 commit comments