3 Mar 2024
Search Text in 2D Document by Autodesk.StringExtractor Extension
Most 2D documents includes text strings. When APS translates the document, the strings are also extracted. With the strings, it is feasible to implement the feature: Search/Find Text within pages.
The strings have been available with the derivatives of translation. By default, they are not downloaded in the viewer. To load them, the initial option extendStringsFetching is required. e.g. the code of loading document online or offline respectively.
//online
viewer.loadDocumentNode(doc, viewables, {extendStringsFetching: true}).then(() => {
console.log('the online document is loaded. extracting strings')
})
//local
viewer.loadModel(model, {extendStringsFetching: true}).then(() => {
console.log('local 2D document is loaded')
})
Autodesk.StringExtractor Extension of APS Viewer provides the method to extract the strings of the documents to build the map of models with strings array. It supports PDF, Leaflet or other 2D documents such as DWG.
- extractStringsFromModels(): extract the strings from all models loaded in the viewer.
- extractStringsFromModel(model): extract the strings from specific model loaded in the viewer.
- documentStrings: dictionary of the strings. In each element, there is an array of strings. It contains string text, position, bounding box etc.
By these data, you could compose a code logic to search text. This code demo below provides the ways to load document of online or local respectively (loadLocal2DDocument, loadOnlineModel). The function extractStringsFromDocuments will load the extension Autodesk.StringExtractor and store the array for searching. searchText is a demo function with the input text. It will try to find the string list and zoom to the first found string by its bounding-box.
In Autodesk Construction Cloud (ACC), this extension is also adopted, in which the default browser Find (ctrl+F) is overridden. you could also override to implement your own workflow.
<head>
<meta charset="utf-8" />
</head>
<body style="margin:0; overflow-y: hidden;">
<div id="apsViewer"></div>
</body>
<link rel="stylesheet" href="https://developer.api.autodesk.com/modelderivative/v2/viewers/style.min.css"
type="text/css">
<script language="JavaScript"
src="https://developer.api.autodesk.com/modelderivative/v2/viewers/viewer3D.min.js"></script>
<script>
let viewer, stringMapping
function loadLocal2DDocument(model) {
var initOptions = {
env: "Local",
useADP: false
}
Autodesk.Viewing.Initializer(initOptions, () => {
viewer = new Autodesk.Viewing.Private.GuiViewer3D(document.getElementById('apsViewer'))
viewer.setTheme("light-theme")
viewer.start()
//enable string extracting from model data.
const loadOptions = {
extendStringsFetching: true
};
viewer.loadModel(model, loadOptions).then(() => {
console.log('local 2D document is loaded')
extractStringsFromDocuments()
})
});
}
function loadOnlineModel(modelUrn) {
var initOptions = {
env: 'AutodeskProduction2',
api: 'streamingV2',
getAccessToken: getForgeToken
}
Autodesk.Viewing.Initializer(initOptions, () => {
viewer = new Autodesk.Viewing.GuiViewer3D(document.getElementById('apsViewer'))
viewer.start();
Autodesk.Viewing.Document.load(modelUrn, onDocumentLoadSuccess);
});
}
function onDocumentLoadSuccess(doc) {
var viewables = doc.getRoot().getDefaultGeometry();
//enable string extracting from model data.
const loadOptions = {
extendStringsFetching: true
};
viewer.loadDocumentNode(doc, viewables, loadOptions).then(() => {
console.log('the online document is loaded. extracting strings')
extractStringsFromDocuments()
}).catch((err) => { console.error(err) })
}
function extractStringsFromDocuments() {
//load StringExtractor extension
viewer.loadExtension('Autodesk.StringExtractor').then((ext) => {
console.log('StringExtractor extension is loaded')
//read strings from all modesl
ext.extractStringsFromModels()
//or single model
//ext.extractStringsFromModel(viewer.model)
//store the strings mapping for Search
stringMapping = ext.documentStrings
})
}
function getForgeToken(callback) {
fetch('/api/forge/oauth/token').then(res => {
res.json().then(data => {
callback(data.access_token, data.expires_in);
});
});
}
function searchText(t){
let allTexts = []
for (const [key, value] of Object.entries(stringMapping)) {
const textInOneDocument = value.strings.filter(i=>i.string == t)
if(textInOneDocument)
allTexts = allTexts.concat(textInOneDocument)
}
if(allTexts){
alert(`There are ${allTexts.length}(count) ${t} in the documents`)
//zoom to the first instance
const firstInstance = allTexts[0]
const bbox = firstInstance.boundingBox
const box3 = new THREE.Box3(
new THREE.Vector3(bbox.min.x,bbox.min.y,0),
new THREE.Vector3(bbox.max.x,bbox.max.y,0))
viewer.navigation.fitBounds(false, box3);
}
else{
alert(`text ${t} is not found in any documents`)
}
}
//load online document
//loadOnlineModel('urn:xxxx')
//load local document
//loadLocal2DDocument('mytest.pdf')
</script>