28 May 2025
Retrieving the text from 2d views

Introduction
If you're familiar with APS and Viewer, you're aware that you can retrieve the properties from elements. But what about the text available in your 2D views?
This is what we will cover in this blog post. We are going to build an extension that returns the text from one specific region in the Viewer and shows that in a custom panel.
How does it work?
The first thing we'll do is retrieve all the text from our model, and that is done using the Autodesk.StringExtractor
extension.
To leverage that extension, you need to pass one specific option (extendStringsFetching) when loading your models, just like this sample does in the snippet below:
viewer.loadDocumentNode(doc, viewables, {
extendStringsFetching: true
});
After that, we can use the Autodesk.StringExtractor
extension, just like done in the snippet below:
async onObjectTreeCreated(event){
this.viewer.loadExtension("Autodesk.StringExtractor")
.then(() => {
this.stringExtractor = this.viewer.getExtension('Autodesk.StringExtractor');
return this.stringExtractor.extractStringsFromModel(this.viewer.model);
})
.then(() => {
this._panel.text = Object.values(this.stringExtractor.documentStrings)[0].strings;
})
.catch((error) => {
console.error('Error loading or extracting strings:', error);
});
}
From this point, we have all of our strings available from our panel. Each string object has a structure similar to the one below:
{
angle: 0
boundingBox: THREE.Box2 {min: THREE.Vector2, max: THREE.Vector2}
string: "REV."
stringCharWidths: [722, 667, 667, 278]
stringHeight: 12.57294
stringPosition: [28.298333333333336, 3.998333333333333]
stringWidth: 29.345241959999996
}
Filtering the text
We'll use the boundingBox of the strings to filter those associated with the selected region in our view. To handle the selection, we can count on Autodesk.BoxSelection
extension in a way that every time the user selects one region, we compare this region's bounding box with the string's bounding box.
We can start by reacting to the mouse-up event every time the box selection tool is set to active:
onMouseUp(event) {
const boxSelectionTool = this.viewer.getExtension('Autodesk.BoxSelection').boxSelectionTool;
if (boxSelectionTool.isActive()) {
let boxStartPoint = this.viewer.clientToWorld(boxSelectionTool.startPoint.x, boxSelectionTool.startPoint.y);
let boxEndPoint = this.viewer.clientToWorld(boxSelectionTool.endPoint.x, boxSelectionTool.endPoint.y);
//convert to THREE.Vector2
const startPoint = new THREE.Vector2(Math.min(boxStartPoint.point.x,boxEndPoint.point.x), Math.min(boxStartPoint.point.y,boxEndPoint.point.y));
const endPoint = new THREE.Vector2(Math.max(boxStartPoint.point.x,boxEndPoint.point.x), Math.max(boxStartPoint.point.y,boxEndPoint.point.y));
const boundingbox = new THREE.Box2(startPoint,endPoint);
if (this._panel) {
this._panel.update(boundingbox);
}
}
}
And inside the panel update function, we perform the match:
async update(regionBox) {
this.removeAllProperties();
let stringCount = 1;
//Now we check which geometries intersect with the bounding box
for(const text of this.text) {
//You can also change to use regionBox.intersectsBox() or regionBox.containsPoint() or regionBox.containsBox() if you want to check for containment instead of intersection
if (regionBox.intersectsBox(text.boundingBox)) {
this.addProperty('String ' + stringCount.toString(), text.string, 'PDF Text');
stringCount++;
}
}
return true;
}
This comparison can leverage common bounding box methods, like intersectsBox
, containsPoint
, and containsBox
. Your choice will affect what the extension matches.
You can check the live demo and source code below: