Merge pull request #13243 from janpe2/ocg-ve
Implement visibility expressions for optional content
This commit is contained in:
		
						commit
						ae48d07582
					
				| @ -1306,6 +1306,43 @@ class PartialEvaluator { | |||||||
|     throw new FormatError(`Unknown PatternName: ${patternName}`); |     throw new FormatError(`Unknown PatternName: ${patternName}`); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|  |   _parseVisibilityExpression(array, nestingCounter, currentResult) { | ||||||
|  |     const MAX_NESTING = 10; | ||||||
|  |     if (++nestingCounter > MAX_NESTING) { | ||||||
|  |       warn("Visibility expression is too deeply nested"); | ||||||
|  |       return; | ||||||
|  |     } | ||||||
|  |     const length = array.length; | ||||||
|  |     const operator = this.xref.fetchIfRef(array[0]); | ||||||
|  |     if (length < 2 || !isName(operator)) { | ||||||
|  |       warn("Invalid visibility expression"); | ||||||
|  |       return; | ||||||
|  |     } | ||||||
|  |     switch (operator.name) { | ||||||
|  |       case "And": | ||||||
|  |       case "Or": | ||||||
|  |       case "Not": | ||||||
|  |         currentResult.push(operator.name); | ||||||
|  |         break; | ||||||
|  |       default: | ||||||
|  |         warn(`Invalid operator ${operator.name} in visibility expression`); | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  |     for (let i = 1; i < length; i++) { | ||||||
|  |       const raw = array[i]; | ||||||
|  |       const object = this.xref.fetchIfRef(raw); | ||||||
|  |       if (Array.isArray(object)) { | ||||||
|  |         const nestedResult = []; | ||||||
|  |         currentResult.push(nestedResult); | ||||||
|  |         // Recursively parse a subarray.
 | ||||||
|  |         this._parseVisibilityExpression(object, nestingCounter, nestedResult); | ||||||
|  |       } else if (isRef(raw)) { | ||||||
|  |         // Reference to an OCG dictionary.
 | ||||||
|  |         currentResult.push(raw.toString()); | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|   async parseMarkedContentProps(contentProperties, resources) { |   async parseMarkedContentProps(contentProperties, resources) { | ||||||
|     let optionalContent; |     let optionalContent; | ||||||
|     if (isName(contentProperties)) { |     if (isName(contentProperties)) { | ||||||
| @ -1324,6 +1361,18 @@ class PartialEvaluator { | |||||||
|         id: optionalContent.objId, |         id: optionalContent.objId, | ||||||
|       }; |       }; | ||||||
|     } else if (optionalContentType === "OCMD") { |     } else if (optionalContentType === "OCMD") { | ||||||
|  |       const expression = optionalContent.get("VE"); | ||||||
|  |       if (Array.isArray(expression)) { | ||||||
|  |         const result = []; | ||||||
|  |         this._parseVisibilityExpression(expression, 0, result); | ||||||
|  |         if (result.length > 0) { | ||||||
|  |           return { | ||||||
|  |             type: "OCMD", | ||||||
|  |             expression: result, | ||||||
|  |           }; | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  | 
 | ||||||
|       const optionalContentGroups = optionalContent.get("OCGs"); |       const optionalContentGroups = optionalContent.get("OCGs"); | ||||||
|       if ( |       if ( | ||||||
|         Array.isArray(optionalContentGroups) || |         Array.isArray(optionalContentGroups) || | ||||||
| @ -1339,19 +1388,13 @@ class PartialEvaluator { | |||||||
|           groupIds.push(optionalContentGroups.objId); |           groupIds.push(optionalContentGroups.objId); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         let expression = null; |  | ||||||
|         if (optionalContent.get("VE")) { |  | ||||||
|           // TODO support visibility expression.
 |  | ||||||
|           expression = true; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         return { |         return { | ||||||
|           type: optionalContentType, |           type: optionalContentType, | ||||||
|           ids: groupIds, |           ids: groupIds, | ||||||
|           policy: isName(optionalContent.get("P")) |           policy: isName(optionalContent.get("P")) | ||||||
|             ? optionalContent.get("P").name |             ? optionalContent.get("P").name | ||||||
|             : null, |             : null, | ||||||
|           expression, |           expression: null, | ||||||
|         }; |         }; | ||||||
|       } else if (isRef(optionalContentGroups)) { |       } else if (isRef(optionalContentGroups)) { | ||||||
|         return { |         return { | ||||||
|  | |||||||
| @ -57,6 +57,43 @@ class OptionalContentConfig { | |||||||
|     } |     } | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|  |   _evaluateVisibilityExpression(array) { | ||||||
|  |     const length = array.length; | ||||||
|  |     if (length < 2) { | ||||||
|  |       return true; | ||||||
|  |     } | ||||||
|  |     const operator = array[0]; | ||||||
|  |     for (let i = 1; i < length; i++) { | ||||||
|  |       const element = array[i]; | ||||||
|  |       let state; | ||||||
|  |       if (Array.isArray(element)) { | ||||||
|  |         state = this._evaluateVisibilityExpression(element); | ||||||
|  |       } else if (this._groups.has(element)) { | ||||||
|  |         state = this._groups.get(element).visible; | ||||||
|  |       } else { | ||||||
|  |         warn(`Optional content group not found: ${element}`); | ||||||
|  |         return true; | ||||||
|  |       } | ||||||
|  |       switch (operator) { | ||||||
|  |         case "And": | ||||||
|  |           if (!state) { | ||||||
|  |             return false; | ||||||
|  |           } | ||||||
|  |           break; | ||||||
|  |         case "Or": | ||||||
|  |           if (state) { | ||||||
|  |             return true; | ||||||
|  |           } | ||||||
|  |           break; | ||||||
|  |         case "Not": | ||||||
|  |           return !state; | ||||||
|  |         default: | ||||||
|  |           return true; | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |     return operator === "And"; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|   isVisible(group) { |   isVisible(group) { | ||||||
|     if (group.type === "OCG") { |     if (group.type === "OCG") { | ||||||
|       if (!this._groups.has(group.id)) { |       if (!this._groups.has(group.id)) { | ||||||
| @ -65,10 +102,9 @@ class OptionalContentConfig { | |||||||
|       } |       } | ||||||
|       return this._groups.get(group.id).visible; |       return this._groups.get(group.id).visible; | ||||||
|     } else if (group.type === "OCMD") { |     } else if (group.type === "OCMD") { | ||||||
|       // Per the spec, the expression should be preferred if available. Until
 |       // Per the spec, the expression should be preferred if available.
 | ||||||
|       // we implement this, just fallback to using the group policy for now.
 |  | ||||||
|       if (group.expression) { |       if (group.expression) { | ||||||
|         warn("Visibility expression not supported yet."); |         return this._evaluateVisibilityExpression(group.expression); | ||||||
|       } |       } | ||||||
|       if (!group.policy || group.policy === "AnyOn") { |       if (!group.policy || group.policy === "AnyOn") { | ||||||
|         // Default
 |         // Default
 | ||||||
|  | |||||||
							
								
								
									
										1
									
								
								test/pdfs/.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								test/pdfs/.gitignore
									
									
									
									
										vendored
									
									
								
							| @ -354,6 +354,7 @@ | |||||||
| !issue2128r.pdf | !issue2128r.pdf | ||||||
| !issue5540.pdf | !issue5540.pdf | ||||||
| !issue5549.pdf | !issue5549.pdf | ||||||
|  | !visibility_expressions.pdf | ||||||
| !issue5475.pdf | !issue5475.pdf | ||||||
| !issue10519_reduced.pdf | !issue10519_reduced.pdf | ||||||
| !annotation-border-styles.pdf | !annotation-border-styles.pdf | ||||||
|  | |||||||
							
								
								
									
										
											BIN
										
									
								
								test/pdfs/visibility_expressions.pdf
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								test/pdfs/visibility_expressions.pdf
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| @ -2635,6 +2635,12 @@ | |||||||
|        "link": false, |        "link": false, | ||||||
|        "type": "eq" |        "type": "eq" | ||||||
|     }, |     }, | ||||||
|  |     {  "id": "visibility_expressions", | ||||||
|  |        "file": "pdfs/visibility_expressions.pdf", | ||||||
|  |        "md5": "bc530d90984ddaa2cc7e0cd53fc2cf34", | ||||||
|  |        "rounds": 1, | ||||||
|  |        "type": "eq" | ||||||
|  |    }, | ||||||
|     {  "id": "issue7580-text", |     {  "id": "issue7580-text", | ||||||
|        "file": "pdfs/issue7580.pdf", |        "file": "pdfs/issue7580.pdf", | ||||||
|        "md5": "44dd5a9b4373fcab9890cf567722a766", |        "md5": "44dd5a9b4373fcab9890cf567722a766", | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user