Implement rendering square/circle annotations without appearance stream
This commit is contained in:
		
							parent
							
								
									8e74278b65
								
							
						
					
					
						commit
						fa6cebf045
					
				@ -192,6 +192,33 @@ class AnnotationFactory {
 | 
				
			|||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function getRgbColor(color) {
 | 
				
			||||||
 | 
					  const rgbColor = new Uint8ClampedArray(3);
 | 
				
			||||||
 | 
					  if (!Array.isArray(color)) {
 | 
				
			||||||
 | 
					    return rgbColor;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  switch (color.length) {
 | 
				
			||||||
 | 
					    case 0: // Transparent, which we indicate with a null value
 | 
				
			||||||
 | 
					      return null;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    case 1: // Convert grayscale to RGB
 | 
				
			||||||
 | 
					      ColorSpace.singletons.gray.getRgbItem(color, 0, rgbColor, 0);
 | 
				
			||||||
 | 
					      return rgbColor;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    case 3: // Convert RGB percentages to RGB
 | 
				
			||||||
 | 
					      ColorSpace.singletons.rgb.getRgbItem(color, 0, rgbColor, 0);
 | 
				
			||||||
 | 
					      return rgbColor;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    case 4: // Convert CMYK to RGB
 | 
				
			||||||
 | 
					      ColorSpace.singletons.cmyk.getRgbItem(color, 0, rgbColor, 0);
 | 
				
			||||||
 | 
					      return rgbColor;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    default:
 | 
				
			||||||
 | 
					      return rgbColor;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function getQuadPoints(dict, rect) {
 | 
					function getQuadPoints(dict, rect) {
 | 
				
			||||||
  if (!dict.has("QuadPoints")) {
 | 
					  if (!dict.has("QuadPoints")) {
 | 
				
			||||||
    return null;
 | 
					    return null;
 | 
				
			||||||
@ -462,36 +489,7 @@ class Annotation {
 | 
				
			|||||||
   *                        4 (CMYK) elements
 | 
					   *                        4 (CMYK) elements
 | 
				
			||||||
   */
 | 
					   */
 | 
				
			||||||
  setColor(color) {
 | 
					  setColor(color) {
 | 
				
			||||||
    const rgbColor = new Uint8ClampedArray(3);
 | 
					    this.color = getRgbColor(color);
 | 
				
			||||||
    if (!Array.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;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /**
 | 
					  /**
 | 
				
			||||||
@ -929,7 +927,22 @@ class MarkupAnnotation extends Annotation {
 | 
				
			|||||||
      buffer.push(`${fillColor[0]} ${fillColor[1]} ${fillColor[2]} rg`);
 | 
					      buffer.push(`${fillColor[0]} ${fillColor[1]} ${fillColor[2]} rg`);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    for (const points of this.data.quadPoints) {
 | 
					    let pointsArray = this.data.quadPoints;
 | 
				
			||||||
 | 
					    if (!pointsArray) {
 | 
				
			||||||
 | 
					      // If there are no quadpoints, the rectangle should be used instead.
 | 
				
			||||||
 | 
					      // Convert the rectangle definition to a points array similar to how the
 | 
				
			||||||
 | 
					      // quadpoints are defined.
 | 
				
			||||||
 | 
					      pointsArray = [
 | 
				
			||||||
 | 
					        [
 | 
				
			||||||
 | 
					          { x: this.rectangle[0], y: this.rectangle[3] },
 | 
				
			||||||
 | 
					          { x: this.rectangle[2], y: this.rectangle[3] },
 | 
				
			||||||
 | 
					          { x: this.rectangle[0], y: this.rectangle[1] },
 | 
				
			||||||
 | 
					          { x: this.rectangle[2], y: this.rectangle[1] },
 | 
				
			||||||
 | 
					        ],
 | 
				
			||||||
 | 
					      ];
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    for (const points of pointsArray) {
 | 
				
			||||||
      const [mX, MX, mY, MY] = pointsCallback(buffer, points);
 | 
					      const [mX, MX, mY, MY] = pointsCallback(buffer, points);
 | 
				
			||||||
      minX = Math.min(minX, mX);
 | 
					      minX = Math.min(minX, mX);
 | 
				
			||||||
      maxX = Math.max(maxX, MX);
 | 
					      maxX = Math.max(maxX, MX);
 | 
				
			||||||
@ -2278,6 +2291,43 @@ class SquareAnnotation extends MarkupAnnotation {
 | 
				
			|||||||
    super(parameters);
 | 
					    super(parameters);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    this.data.annotationType = AnnotationType.SQUARE;
 | 
					    this.data.annotationType = AnnotationType.SQUARE;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (!this.appearance) {
 | 
				
			||||||
 | 
					      // The default stroke color is black.
 | 
				
			||||||
 | 
					      const strokeColor = this.color
 | 
				
			||||||
 | 
					        ? Array.from(this.color).map(c => c / 255)
 | 
				
			||||||
 | 
					        : [0, 0, 0];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      // The default fill color is transparent.
 | 
				
			||||||
 | 
					      let fillColor = null;
 | 
				
			||||||
 | 
					      let interiorColor = parameters.dict.getArray("IC");
 | 
				
			||||||
 | 
					      if (interiorColor) {
 | 
				
			||||||
 | 
					        interiorColor = getRgbColor(interiorColor);
 | 
				
			||||||
 | 
					        fillColor = interiorColor
 | 
				
			||||||
 | 
					          ? Array.from(interiorColor).map(c => c / 255)
 | 
				
			||||||
 | 
					          : null;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      this._setDefaultAppearance({
 | 
				
			||||||
 | 
					        xref: parameters.xref,
 | 
				
			||||||
 | 
					        extra: `${this.borderStyle.width} w`,
 | 
				
			||||||
 | 
					        strokeColor,
 | 
				
			||||||
 | 
					        fillColor,
 | 
				
			||||||
 | 
					        pointsCallback: (buffer, points) => {
 | 
				
			||||||
 | 
					          const x = points[2].x + this.borderStyle.width / 2;
 | 
				
			||||||
 | 
					          const y = points[2].y + this.borderStyle.width / 2;
 | 
				
			||||||
 | 
					          const width = points[3].x - points[2].x - this.borderStyle.width;
 | 
				
			||||||
 | 
					          const height = points[1].y - points[3].y - this.borderStyle.width;
 | 
				
			||||||
 | 
					          buffer.push(`${x} ${y} ${width} ${height} re`);
 | 
				
			||||||
 | 
					          if (fillColor) {
 | 
				
			||||||
 | 
					            buffer.push("B");
 | 
				
			||||||
 | 
					          } else {
 | 
				
			||||||
 | 
					            buffer.push("S");
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					          return [points[0].x, points[1].x, points[3].y, points[1].y];
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -2286,6 +2336,67 @@ class CircleAnnotation extends MarkupAnnotation {
 | 
				
			|||||||
    super(parameters);
 | 
					    super(parameters);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    this.data.annotationType = AnnotationType.CIRCLE;
 | 
					    this.data.annotationType = AnnotationType.CIRCLE;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (!this.appearance) {
 | 
				
			||||||
 | 
					      // The default stroke color is black.
 | 
				
			||||||
 | 
					      const strokeColor = this.color
 | 
				
			||||||
 | 
					        ? Array.from(this.color).map(c => c / 255)
 | 
				
			||||||
 | 
					        : [0, 0, 0];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      // The default fill color is transparent.
 | 
				
			||||||
 | 
					      let fillColor = null;
 | 
				
			||||||
 | 
					      let interiorColor = parameters.dict.getArray("IC");
 | 
				
			||||||
 | 
					      if (interiorColor) {
 | 
				
			||||||
 | 
					        interiorColor = getRgbColor(interiorColor);
 | 
				
			||||||
 | 
					        fillColor = interiorColor
 | 
				
			||||||
 | 
					          ? Array.from(interiorColor).map(c => c / 255)
 | 
				
			||||||
 | 
					          : null;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      // Circles are approximated by Bézier curves with four segments since
 | 
				
			||||||
 | 
					      // there is no circle primitive in the PDF specification. For the control
 | 
				
			||||||
 | 
					      // points distance, see https://stackoverflow.com/a/27863181.
 | 
				
			||||||
 | 
					      const controlPointsDistance = (4 / 3) * Math.tan(Math.PI / (2 * 4));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      this._setDefaultAppearance({
 | 
				
			||||||
 | 
					        xref: parameters.xref,
 | 
				
			||||||
 | 
					        extra: `${this.borderStyle.width} w`,
 | 
				
			||||||
 | 
					        strokeColor,
 | 
				
			||||||
 | 
					        fillColor,
 | 
				
			||||||
 | 
					        pointsCallback: (buffer, points) => {
 | 
				
			||||||
 | 
					          const x0 = points[0].x + this.borderStyle.width / 2;
 | 
				
			||||||
 | 
					          const y0 = points[0].y - this.borderStyle.width / 2;
 | 
				
			||||||
 | 
					          const x1 = points[3].x - this.borderStyle.width / 2;
 | 
				
			||||||
 | 
					          const y1 = points[3].y + this.borderStyle.width / 2;
 | 
				
			||||||
 | 
					          const xMid = x0 + (x1 - x0) / 2;
 | 
				
			||||||
 | 
					          const yMid = y0 + (y1 - y0) / 2;
 | 
				
			||||||
 | 
					          const xOffset = ((x1 - x0) / 2) * controlPointsDistance;
 | 
				
			||||||
 | 
					          const yOffset = ((y1 - y0) / 2) * controlPointsDistance;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					          buffer.push(`${xMid} ${y1} m`);
 | 
				
			||||||
 | 
					          buffer.push(
 | 
				
			||||||
 | 
					            `${xMid + xOffset} ${y1} ${x1} ${yMid + yOffset} ${x1} ${yMid} c`
 | 
				
			||||||
 | 
					          );
 | 
				
			||||||
 | 
					          buffer.push(
 | 
				
			||||||
 | 
					            `${x1} ${yMid - yOffset} ${xMid + xOffset} ${y0} ${xMid} ${y0} c`
 | 
				
			||||||
 | 
					          );
 | 
				
			||||||
 | 
					          buffer.push(
 | 
				
			||||||
 | 
					            `${xMid - xOffset} ${y0} ${x0} ${yMid - yOffset} ${x0} ${yMid} c`
 | 
				
			||||||
 | 
					          );
 | 
				
			||||||
 | 
					          buffer.push(
 | 
				
			||||||
 | 
					            `${x0} ${yMid + yOffset} ${xMid - xOffset} ${y1} ${xMid} ${y1} c`
 | 
				
			||||||
 | 
					          );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					          buffer.push("h");
 | 
				
			||||||
 | 
					          if (fillColor) {
 | 
				
			||||||
 | 
					            buffer.push("B");
 | 
				
			||||||
 | 
					          } else {
 | 
				
			||||||
 | 
					            buffer.push("S");
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					          return [points[0].x, points[1].x, points[3].y, points[1].y];
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										1
									
								
								test/pdfs/.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								test/pdfs/.gitignore
									
									
									
									
										vendored
									
									
								
							@ -395,6 +395,7 @@
 | 
				
			|||||||
!annotation-line.pdf
 | 
					!annotation-line.pdf
 | 
				
			||||||
!bug1669099.pdf
 | 
					!bug1669099.pdf
 | 
				
			||||||
!annotation-square-circle.pdf
 | 
					!annotation-square-circle.pdf
 | 
				
			||||||
 | 
					!annotation-square-circle-without-appearance.pdf
 | 
				
			||||||
!annotation-stamp.pdf
 | 
					!annotation-stamp.pdf
 | 
				
			||||||
!annotation-fileattachment.pdf
 | 
					!annotation-fileattachment.pdf
 | 
				
			||||||
!annotation-text-widget.pdf
 | 
					!annotation-text-widget.pdf
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										
											BIN
										
									
								
								test/pdfs/annotation-square-circle-without-appearance.pdf
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								test/pdfs/annotation-square-circle-without-appearance.pdf
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							@ -4724,6 +4724,13 @@
 | 
				
			|||||||
       "type": "eq",
 | 
					       "type": "eq",
 | 
				
			||||||
       "annotations": true
 | 
					       "annotations": true
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
 | 
					    {  "id": "annotation-square-circle-without-appearance",
 | 
				
			||||||
 | 
					       "file": "pdfs/annotation-square-circle-without-appearance.pdf",
 | 
				
			||||||
 | 
					       "md5": "5b2a5e1e918137993d26a0cd8b0947f6",
 | 
				
			||||||
 | 
					       "rounds": 1,
 | 
				
			||||||
 | 
					       "annotations": true,
 | 
				
			||||||
 | 
					       "type": "eq"
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
    {  "id": "annotation-stamp",
 | 
					    {  "id": "annotation-stamp",
 | 
				
			||||||
       "file": "pdfs/annotation-stamp.pdf",
 | 
					       "file": "pdfs/annotation-stamp.pdf",
 | 
				
			||||||
       "md5": "0a04d7ce1ad103cb3c033d26855d6ec7",
 | 
					       "md5": "0a04d7ce1ad103cb3c033d26855d6ec7",
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user