Doing some cool stuff with JavaScript in Bluebeam (Part 2)

In part 1 we added a single line of JavaScript, it was basic. But hopefully that single line of JavaScript provided some inspiration on how you could make other stamps you might create dynamic, and less specific.

In this post we’ll do another example, but this time I’ll show you how to use some data input by the user when you place a stamp to do some calculations using JavaScript.

Example 2 – Calculating bracing capacity for Gib walls with a Bluebeam stamp

In New Zealand in residential timber framed houses we brace them typically using gypsum board linings. The bracing capacity achieved is typically based on testing and documented as the number of Bracing Units (BU’s) that can be taken by a given wall type/construction based on a given performance objective. The test is called the P21 test which quantifies the bracing achieved.

The suppliers of these products publish these capacities so we as Engineers can use them. We use these capacities to design the lateral bracing systems more efficiently in houses, profit!

Sometimes Architects give it a go also, fixing this means minus profit!

Without the engineering knowledge when architects think they can do engineering, it’s a mixed bag to see what they interpret the requirements to be. They just don’t read code clauses the same way we do.

20 BU’s is defined as 1kN. A wall type with 100 BU/m capacity has effectively 5kN/m shear capacity. Wall capacities are based on standard 2.4m height walls. Higher walls are downrated accordingly, shorter walls are taken to have the same capacity as a 2.4m high wall.

We have non-specific engineering design codes that give you the total bracing demand for Wind and Earthquakes for a given type of house/location/claddings, etc. It’s up to the engineer and sometimes the architect to provide sufficient bracing capacity and satisfy the rules around distribution of these bracing elements. Follow the rules and the intent is your house doesn’t fall down under a code level wind or seismic event.

What we’ll demonstrate here is a stamp utilising some simple JavaScript code that allows you to calculate the Wind (W) and Earthquake (EQ) capacity for a given wall length, wall height, and lining type using the capacity data provided by a manufacturer. In this example we’ll use Gib systems published capacities, they are probably the most widely specified system in NZ.

The Bracing Unit concept isn’t limited to gypsum-based linings, it can equally apply to plywood shear walls, proprietary steel bracing frames. If you can put it through the P21 test, you can establish the BU’s capacity. The BU capacity establishes a similar level of performance irrespective of the type of system.

I find this stamp useful during the concept stage of a project but can also be used to document the final capacities achieved. Rather than running off to the manufacturers data and grabbing my calculator every time, simply dump the stamp on a PDF of the plan and enter the wall length, wall height as measured in Bluebeam. The embedded JavaScript code then completes a calculation and pops the results into the textboxes in the stamp. Neat!

A few temporary fields on the left for storing temporary calculation, and the stamp on the right

Most of the textboxes have a little code associated with them, we’ll go through that in a bit to give you a flavour for things. But place the stamp and you’re presented with a few input windows for the bracing element length, height, and type.

The result with the correct number of calculated BU’s is shown below, the W & EQ cells are the result of the calculation process we’ll code below.

The code

Look at the following image, we’ll name our form textbox regions as follows: –

It’s also of importance to recognise that there is sometimes an order to the code running that goes in order of the forms. Make sure you arrange as follows or rearrange until it works. Basically, I don’t f&*king know because Bluebeam doesn’t have an API for this shit.

This is the way you drive what code runs first and in what order. This is important if a subsequent calculation is driven by a previous answer. For example, you cannot calculate the number of BU’s until you know the length, height, and wall type which are driven by user inputs.

Let’s look at what code we need to put in place to getting this working. Don’t judge my JavaScript….

‘L’ code

event.value = (Math.round(app.response("Bracing Element Length")*100)/100).toFixed(2)

Here we are getting the users value for the bracing panel length and doing some roundy stuff, so we only display two fixed decimal places.

‘TYPE’ code

event.value = app.response("Bracing Element Type \n \nGIB Standard Bracing Systems \n \n1 = GS1-N \n2 = GS2-N \n3 = GS2-NOM \n4 = GSP-H \n \nGIB Braceline Bracing Systems \n \n5 = BL1-H \n6 = BLG-H \n7 = BLP-H \n \nECOPLY Barrier Bracing Systems \n \n8 = EPB1 \n9 = EP2 \n10 = EPBS \n11 = EPBG \n")
//Updated & checked May 2020
var temp
var max_bracing
//max bracing units per meter (150 for concrete and 120 for timber)
max_bracing = this.getField("max_limit").value

if (event.value == 1) {
    temp = "GS1-N"
    if (this.getField("L").value >= 1.2) {
        this.getField("W_temp").value = 70
        this.getField("EQ_temp").value = 60
    } else if (this.getField("L").value >= 0.4) {
        this.getField("W_temp").value = 50
        this.getField("EQ_temp").value = 55
    } else {
        this.getField("W_temp").value = 0
        this.getField("EQ_temp").value = 0
    }
}

if (event.value == 2) {
    temp = "GS2-N"
    if (this.getField("L").value >= 1.2) {
        this.getField("W_temp").value = 95
        this.getField("EQ_temp").value = 85
    } else if (this.getField("L").value >= 0.4) {
        this.getField("W_temp").value = 70
        this.getField("EQ_temp").value = 65
    } else {
        this.getField("W_temp").value = 0
        this.getField("EQ_temp").value = 0
    }
}

if (event.value == 3) {
    temp = "GS2-NOM"
    if (this.getField("L").value >= 0.4) {
        this.getField("W_temp").value = 50
        this.getField("EQ_temp").value = 50
    } else {
        this.getField("W_temp").value = 0
        this.getField("EQ_temp").value = 0
    }
}

if (event.value == 4) {
    temp = "GSP-H"
    if (this.getField("L").value >= 1.2) {
        this.getField("W_temp").value = 150
        this.getField("EQ_temp").value = 150
    } else if (this.getField("L").value >= 0.4) {
        this.getField("W_temp").value = 100
        this.getField("EQ_temp").value = 115
    } else {
        this.getField("W_temp").value = 0
        this.getField("EQ_temp").value = 0
    }
}

if (event.value == 5) {
    temp = "BL1-H"
    if (this.getField("L").value >= 1.2) {
        this.getField("W_temp").value = 125
        this.getField("EQ_temp").value = 105
    } else if (this.getField("L").value >= 0.4) {
        this.getField("W_temp").value = 90
        this.getField("EQ_temp").value = 100
    } else {
        this.getField("W_temp").value = 0
        this.getField("EQ_temp").value = 0
    }
}

if (event.value == 6) {
    temp = "BLG-H"
    if (this.getField("L").value >= 1.2) {
        this.getField("W_temp").value = 150
        this.getField("EQ_temp").value = 145
    } else if (this.getField("L").value >= 0.4) {
        this.getField("W_temp").value = 110
        this.getField("EQ_temp").value = 115
    } else {
        this.getField("W_temp").value = 0
        this.getField("EQ_temp").value = 0
    }
}

if (event.value == 7) {
    temp = "BLP-H"
    if (this.getField("L").value >= 1.2) {
        this.getField("W_temp").value = 150
        this.getField("EQ_temp").value = 150
    } else if (this.getField("L").value >= 0.4) {
        this.getField("W_temp").value = 120
        this.getField("EQ_temp").value = 135
    } else {
        this.getField("W_temp").value = 0
        this.getField("EQ_temp").value = 0
    }
}

if (event.value == 8) {
    temp = "EPB1"
    if (this.getField("L").value >= 1.2) {
        this.getField("W_temp").value = 120
        this.getField("EQ_temp").value = 135
    } else if (this.getField("L").value >= 0.6) {
        this.getField("W_temp").value = 95
        this.getField("EQ_temp").value = 105
    } else if (this.getField("L").value >= 0.4) {
        this.getField("W_temp").value = 80
        this.getField("EQ_temp").value = 95
    } else {
        this.getField("W_temp").value = 0
        this.getField("EQ_temp").value = 0
    }
}

if (event.value == 9) {
    temp = "EP2"
    if (this.getField("L").value >= 0.6) {
        this.getField("W_temp").value = 105
        this.getField("EQ_temp").value = 115
    } else {
        this.getField("W_temp").value = 0
        this.getField("EQ_temp").value = 0
    }
}

if (event.value == 10) {
    temp = "EPBS"
    if (this.getField("L").value >= 2.4) {
        this.getField("W_temp").value = 80
        this.getField("EQ_temp").value = 90
    } else if (this.getField("L").value >= 1.2) {
        this.getField("W_temp").value = 65
        this.getField("EQ_temp").value = 70
    } else if (this.getField("L").value >= 0.6) {
        this.getField("W_temp").value = 60
        this.getField("EQ_temp").value = 65
    } else if (this.getField("L").value >= 0.4) {
        this.getField("W_temp").value = 60
        this.getField("EQ_temp").value = 60
    } else {
        this.getField("W_temp").value = 0
        this.getField("EQ_temp").value = 0
    }
}

if (event.value == 11) {
    temp = "EPBG"
    if (this.getField("L").value >= 1.2) {
        this.getField("W_temp").value = 150
        this.getField("EQ_temp").value = 150
    } else if (this.getField("L").value >= 0.4) {
        this.getField("W_temp").value = 100
        this.getField("EQ_temp").value = 115
    } else {
        this.getField("W_temp").value = 0
        this.getField("EQ_temp").value = 0
    }
}
event.value = temp

Here we are getting the system type, and then establishing the BU’s per metre from suppliers’ tables like shown below. The code writes these values to some temporary textboxes so we can utilise these values for later calculations. Additionally, I forgot that I also had all the Ecoply values in there… Bonus for you.

‘HT’ code

event.value = (Math.round(app.response("Bracing Element Height")*100)/100).toFixed(2)

Same as ‘L’ really but for height.

‘W_temp’ & ‘EQ_temp’ code

Nothing to see here, no code required. These just temporarily store the BU’s per metre values from the ‘TYPE‘ code.

‘EQ’ code

event.value = Math.round(Math.min(max_bracing, this.getField("EQ_temp").value * Math.min(1, 2.4 / this.getField("HT").value)) * this.getField("L").value) + " BU";

if (this.getField("EQ_temp").value * Math.min(1, 2.4 / this.getField("HT").value) <= max_bracing) {
    event.value = event.value
} else {
    event.value = event.value + "*"
};

Here we are finally working out the capacity and checking it against the maximum value we have stored in the ‘max_limit‘ textbox. Basically, I just have two stamps, one for concrete floors, and one for timber floors with the different limits hard coded in there.

‘W’ code

event.value = Math.round(Math.min(max_bracing, this.getField("W_temp").value * Math.min(1, 2.4 / this.getField("HT").value)) * this.getField("L").value) + " BU"

if (this.getField("W_temp").value * Math.min(1, 2.4 / this.getField("HT").value) <= max_bracing) {
    event.value = event.value
} else {
    event.value = event.value + "*"
};

Rinse and repeat of ‘EQ‘ code.

‘EQ_limit’ code

if (this.getField("EQ_temp").value * Math.min(1, 2.4 / this.getField("HT").value) <= max_bracing) {
    event.value = " "
} else {
    event.value = "*" + max_bracing + " BU/m LIMIT"
};

This is a warning that we’ve been limited by the maximum BU per metre limit.

‘W_limit’ code

if (this.getField("W_temp").value * Math.min(1, 2.4 / this.getField("HT").value) <= max_bracing) {
    event.value = " "
} else {
    event.value = "*" + max_bracing + " BU/m LIMIT"
};

Rinse and repeat of ‘EQ_limit‘ code.

‘max_limit’ code

Manually fill out the textbox with the upper limit. As noted above make a stamp for timber floors and concrete floors using 120, and 150, respectively.

Conclusion

If you’ve followed along and I have not lost you, then you have your second dynamic stamp or at the very least I haven’t wasted my time and you can use similar concepts to create something completely different but unique to the way you work.

Your imagination is the limit. Do some design, like work out preliminary strength or deflection checks without breaking out your calculator…. profit!

Now if you were able to draw a dimension in conjunction with a stamp and use that dimension seamlessly in a calculation ….. oh, wait this is Bluebeam, we wouldn’t want the developers to make it insanely useful out of the box…. too sarcastic? Too bad…

PS….

I wish I had done this when I first wrote the code, I struggled with Bluebeam’s lacklustre editor and the JavaScript formatting. So just copy it into your favourite code editor and use the automatic formatting tools in there to make sure your code is formatted correctly. I use VS Code now. Trust me, if you are not familiar with JavaScript, this will help a lot to get the formatting nomenclature right. Because get it wrong and Bluebeam doesn’t tell you why…. aarrrghhh……..

10 Comments

  1. Hello,
    Do you know whether Bluebeam has a library to interact with VBA? Another question is whether I can use JS to crop pdf page?

    Thanks a lot.

    • Hi Austin
      As far as I know bluebeam only supports javascript.
      You could maybe see if this type of approach is supported and achieves what you are after regarding cropping. Not all pdf javascript commands are supported by bluebeam, just a subset. So you’ll have to try and see.

    • Hi Viraj, unfortunately I have not played around with these. Bluebeam used to have a few examples on their website for some review stamps.

  2. This was really good information to read about! Your post was able to help me get a little further in a Bluebeam endeavor I am currently trying. I’m trying to teach myself a little about scripting in Bluebeam, while also learning about Javascript again. By any chance could I pick your brain about what I am trying to do?

      • I am trying to come up with a way to update a custom column based on user input. I have some Bluebeam resources for their scripts but I haven’t had much luck yet. I made a post on it on Stack Overflow if you want to check it out (if you’re not familiar with it, it’s where people can post questions about all kinds of coding questions). No big deal if you don’t want to check it out, or are not able to help. I just appreciate that you responded lol 🙂
        https://stackoverflow.com/questions/71429797/bluebeam-scripting-custom-columns-markups-forms

        • I don’t know if there is a way to update a markup properties or text after it is placed. I’ve only used Javascript to populate form elements when you place a stamp or tool, to fill out some text or similar, but after being placed it is just dumb text. Additionally there is no way to run Javascript without it being envoked by some action like placing a stamp as far as I know.

          The scripting is more a way of automating some things, like placement of stamps on multiple pages at a certain location. Don’t think you can use it to run Javascript or anything like that or interact with properties.

          You can certainly request user input when you place a stamp that has “form” elements (need extreme version to access the form controls though), this is basically what this post is about. Basically the code runs when you place the stamp. But I’m not entirely sure if that’s what you are after or not. As you’d have to enter the text fields every time you placed the markup and as far as I’m aware it just turns into dumb text at that point as unsure if you can write to custom properties.

          Think I came across that same post with the API ages ago, unfortunately Bluebeam does not provide an explanation of those API items, which makes it hard to figure out anything useful. Like it says in that post, you best bet might be to see if there are any similar examples in Adobe to what you might be doing to see how the API might work.

  3. Please create a video for step-by-step – it would be great to follow the video.
    Thanks in advance

Leave a Reply to Glennie CoolidgeCancel Reply

Your email address will not be published. Required fields are marked *