cancel
Showing results for 
Search instead for 
Did you mean: 
cancel
Showing results for 
Search instead for 
Did you mean: 

Community Tip - Visit the PTCooler (the community lounge) to get to know your fellow community members and check out some of Dale's Friday Humor posts! X

3DLabel Wrap Text

BAR3887
5-Regular Member

3DLabel Wrap Text

I would like to be able to wrap text for a 3DLabel. My use case is if I can take a data field within a work instruction program to be able to be linked to a 3DLabel. This way it will show the most up to date work instructions that could could range from a couple sentences to a couple of paragraphs. Right now if I use a 3DLabel, it will take that text and keep it in the same line.

 

The workaround that I am using is putting the text into a 3D image so that way it is showing in the size and format fit best for the experience. But this does not allow that text to be configurable to latest instructions.

 

Thank you,

Brian

10 REPLIES 10
sdidier
17-Peridot
(To:BAR3887)

Hello,

 

Sadly, this is limitation in behavior of 3D Label Widget.

Please see this article :

https://www.ptc.com/en/support/article/CS307920

 

I can give you another workaround :

  1. In inkscape, create a SVG file with a text element and give a name id to it
  2. Install open source extensions of Vuforia Studio. Please see this article : https://www.ptc.com/en/support/article?n=CS321903
  3. In a Project, in 3D Canvas, add a 3D SVG Widget
  4. Please have a look to these articles and Projects attached to modify with JavaScript the text element in SVG file directly :

https://www.ptc.com/en/support/article/CS320480

https://www.ptc.com/en/support/article/CS321955

 

              As updated can take some times, I would recommand to do update of 3D SVG when opening the View or with a progress bar displayed during some seconds when clicking a button, for example.

 

Best regards,

Samuel

Hi @BAR3887 ,

 

so far I know , there is no functionality to do this directly with 3Dlabel widget because they do not understand any formatting characters e.g. new line etc. and 3Dlabel widget could not contain more lines text.

Possible options could be split the text to many 3DLabel as different text blocks,  which are aligned in rows and columns where different properties are applied e.g. / size /css style etc.

Another option to use a 3dImage widget instead. You can use a svg file format / also this could be generated by code dynamically and allows to use multiline text. Pease, see the post “Display SVG as 3D Image on Hololens”

Here is also a general info to the SVG format

Thanks for the help.

Based on your comments (and others), here's my attempt at dynamically creating an SVG for text. It works in "Preview" and on the HoloLens2. 

 

First, I added a "3D Image" to my Canvas named "3DImage-1". I set the source to a random SVG file that I found. 

Next I added the following functions to Home.js:

 

 

// This function accepts an image widget name (imgWidget, ex: '3DImage-1') 
//   and some text/HTML (imgText) to render as an SVG image on the widget. 
//   There are optional arguments for basic features like background color, 
//   font color, font size, etc.
$scope.renderSVG = function(imgWidget, imgText,  bgColor='#414141', fontColor='#FCFCFC', fontSize='xx-large') {
  // Simple 600x400 SVG image template with a foreignobject section to embed HTML (text). 
  let svgData = `<?xml version="1.0" encoding="UTF-8"?>
  <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
  <svg 
    id="svg1" 
    crossorigin="anonymous"
    xmlns="http://www.w3.org/2000/svg" 
    xmlns:xlink="http://www.w3.org/1999/xlink" 
    xmlns:xhtml="http://www.w3.org/1999/xhtml"
    width="600" height="400" >
    <foreignObject x="0" y="0" width="600" height="400">
        <xhtml:div 
          id='svgcontainer' 
          style="margin:5px;padding:5px;height:380px;display:flex;justify-content:center;align-content:center;flex-direction:column;font-family:Tahoma,sans-serif;font-size:${fontSize};text-align:center;background-color:${bgColor};color:${fontColor};">
          <xhtml:p 
            id='svgtext'>
            ${imgText}
          </xhtml:p>
        </xhtml:div>
    </foreignObject>
  </svg>`;
  // Base64 encode the SVG template defined above and create the SVG URL
  let svgURL = "data&colon;image/svg+xml;base64," + btoa(svgData);
  // Set the image widget to the SVG URL
  $scope.view.wdg[imgWidget].src=svgURL;
}


// Verify the renderSVG function works by setting a 3DImage widget to text. 
$scope.sampleSVG = function() {
  // Name of image widget to edit
  let sampleImgWidget = '3DImage-1';
  // Text to render on 3DImage widget
  let sampleText = 'Here is some <xhtml:b>TEXT</xhtml:b> that we can render inside an SVG and it should be centered and word-wrapped if everything worked out ok';
  // Render sample
  $scope.renderSVG(imgWidget=sampleImgWidget, imgText=sampleText);
}

// After the document has loaded, change the image to the sample SVG
angular.element(document).ready($scope.sampleSVG);

 

 

 

Preview OutputPreview Output

 

 

AndreaT
12-Amethyst
(To:JG_9770075)

Hi @JG_9770075 I'm using your code but I don't get the expected result.

My widget is called 3DImage-1 and the rest is exactly the same but I get nothing.

Of course I see it's trying to do something because It doesn't show the sampe svg.

Any clue? Do you need to see anything from my project?

 

Thanks

BR

Hi @AndreaT ,

I think the code is working fine but there is an issue in the format of the community editor.

the problem is that the editor always change this code - therefore I will add it as picture there:

2022-10-18_11-41-55.jpg

I think  the example from @JG_9770075 is working fine and was added with the correct code but unfortunately the editor corrected it so that it was not working in this context  . The picture on the bottom is the correct syntax

Awesome @RolandRaytchev , works like a charm!

Another curiosity.

Instead of hardcoding text in the JS window, is it possible to add text from an external file/s uploaded to the resources folder? What would be the best solution? In my case I'm also thinking about a resource that can be easily translated.

Like a menu in two langs and when I select the EN lang it picks up the text from resFile_en.??? and if I choose japanese it'd look for resFile_jp.???

This is more complex but it's also closer to my use case.

 

Thanks!

JG_9770075
4-Participant
(To:AndreaT)

I think you would just need to write a JS function that accepts a filename parameter (such as resFile_en.html or resFile_jp.html) and returns the contents. If you were using the function I wrote above, you could pass the text to the renderSVG function:

 

 

 

someText = readTextFile('res_en.hmtl');
$scope.renderSVG(imgWidget=sampleImgWidget, imgText=someText);

 

 

 

 

I have not tested this but maybe something similar to:

 

 

readTextFile = function(filename) {
  var txt;
  fetch('app/resources/Uploaded/' + filename)
    .then(function(response) {
      response.text().then(function(text) {
      txt = text;
      done();
    });
  });
  return txt;
} 

 

 

 

Also, note that if you use HTML tags, they all need to have the <xhtml:tag> prefix in the tag name or the html won't render. 

 

 

// SHOULD WORK TO BOLD THE TEXT:
<xhtml:b>Bold Text</xhtml:b>

// WONT WORK:
<b>Bold Text</b>

 

 

 

Hi @JG_9770075  and @AndreaT ,

I think the idea how to call file from e.g. upload folder is good but it will not work because the asynchronous call will not wait until data is received an at least as the code example is shown it will not work.

So what we can do is

  • to load the data on start via fetch and set it to some global variables and later with some specific  delay we can access these variables  e.g. :
    $scope.$on('$ionicView.afterEnter',function(){
    //event when view is loaded
    doMyFetch();
    $timeout(()=>{myFunction()},2000); // called after 2 secs delay
           //because there is sure that it is loaded​
    }
    of course this works but when we want to used the loaded data directly we need to call this in the then branch of the fetch event - so I think is for me the better solution.
  • here is an example which is verified that it worked (based on the code suggested by @JG_9770075 )
$scope.renderSVGxhtml = function(imgWidget, imgText,  bgColor , fontColor, fontSize='xx-large') {
  // Simple 600x400 SVG image template with a foreignobject section to embed HTML (text). 
  let svgData = `<?xml version="1.0" encoding="UTF-8"?>
  <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
  <svg 
    id="svg1" 
    crossorigin="anonymous"
    xmlns="http://www.w3.org/2000/svg" 
    xmlns:xlink="http://www.w3.org/1999/xlink" 
    xmlns:xhtml="http://www.w3.org/1999/xhtml"
    width="600" height="500" >
    <foreignObject x="0" y="0" width="600" height="500">
        <xhtml:div 
          id='svgcontainer' 
          style="margin:5px;padding:5px;height:480px;display:flex;justify-content:center;align-content:center;flex-direction:column;font-family:Tahoma,sans-serif;font-size:${fontSize};text-align:center;background-color:${bgColor};color:${fontColor};">
            ${imgText}
        </xhtml:div>
    </foreignObject>
  </svg>`;
  let svgURL = "data&colon;image/svg+xml;base64," + btoa(svgData);
  $scope.setWidgetProp(imgWidget,'src',svgURL)
}

//=========================================================================
 $scope.readTextFile =function(filename, functionCall) {
 
   fetch('app/resources/Uploaded/' + filename).then((response) => response.text())
    .then((text) => {
      let evalTxt=functionCall.replace("my_sample_text_here",text)
      eval(evalTxt)  
    });
    

}
//=========================================================================
//=========================================================================
// Verify the renderSVG function works by setting a 3DImage widget to text. 
$scope.sampleSVG3 = function() {
  // Name of image widget to edit
  let sampleImgWidget = '3DImage-3';
  let bgColor=   '#DE3163'  //'#414141' according https://htmlcolorcodes.com/
  let frontColor= '#40E0D0' //'#FCFCFC'
  
  let myFunctionCall ='$scope.renderSVGxhtml("'+sampleImgWidget+'", "my_sample_text_here","'+bgColor+'", "'+frontColor+'","xx-large");'

 $scope.readTextFile('testText1.txt', myFunctionCall)
  
}
//===========================================================================
// Verify the renderSVG function works by setting a 3DImage widget to text. 
//===========================================================================
$scope.sampleSVG4 = function() {
 // Name of image widget to edit
  let sampleImgWidget = '3DImage-4';
  // Render sample
  let bgColor=   '#FF7F50'  //'#414141' according https://htmlcolorcodes.com/
  let frontColor= '#DFFF00' //'#FCFCFC'
  
  let myFunctionCall ='$scope.renderSVGxhtml("'+sampleImgWidget+'", "my_sample_text_here","'+bgColor+'", "'+frontColor+'","xx-large");'
 
 $scope.readTextFile('testText2.txt', myFunctionCall)
}
//========================================================================
//========================================================================
$scope.$on('$ionicView.afterEnter',function(){$timeout(()=>{$scope.sampleSVG3();$scope.sampleSVG4();},0)})
//======================================================================================================

e.g. the one text file is something like this <testText2.txt> in the upload folder: 

<xhtml:small>Here is another (samll)<xhtml:hr/></xhtml:small><xhtml:b>(bold)Ii Still use The XHTML Syntax <xhtml:hr/></xhtml:b> <xhtml:br/>in  an SVG as <xhtml:b>fofeignObject svgcontainer</xhtml:b><xhtml:br/> <xhtml:i> (italic)which is  working OK! <xhtml:hr/></xhtml:i><xhtml:b>File testText2.txt <xhtml:hr/></xhtml:b>

and here the result in preview

2022-10-20_14-09-23.jpg

I attached the demo project here.

 

please, pay attention that editor changed the code and if a copy and paste is used it needs correction2022-10-20_14-23-19.jpg

or you can take the code from the example project

Top Tags