Auto Place Text Frames Below Each Other (JavaScript) - javascript

I am trying to automate inDesign to create text frames from a JSON file, where every record shall be placed in a text frame, which is right below the foregoing record.
var tweets_json = '[ {"name":"record_1"}, {"name":"record_2"}, ... ]'; // I have not yet figured out how to import external JSON files, so tor testing this is my current way to go
var tweets = eval("(" + tweets_json + ")");
for ( var t = 0; t < tweets.length; t++ ) {
var newTextFrame = page.textFrames.add();
newTextFrame.textFramePreferences.autoSizingType = AutoSizingTypeEnum.HEIGHT_ONLY;
var tweet = tweets[ t ];
newTextFrame.parentStory.texts.item(0).applyParagraphStyle(styleContent, true);
newTextFrame.geometricBounds = [y1,x1,y2,x2]; // Ignore values
newTextFrame.contents = tweet.name;
}
I started inDesign scripting a few days ago and the documentation is hard to find the solution i am looking for, especially with my basic JavaScript knowledge, so any help is appreciated! Thank You!

It could be something like this:
var doc = app.activeDocument;
var page = doc.pages[0];
// get JSON data (just as an example)
var tweets_json = '[ {"name":"line1 line2"}, {"name":"line3"}, {"name":"line4, line5"}]';
var tweets = eval("(" + tweets_json + ")");
// set geometric bounds for the first text frame
var x = 0, y = 0; // coordinates of top a left corner of the text frame
var width = 15; // width of the text frame
var min_heigth = 20; // minimal heigth of the text frame
var bottom = min_heigth; // bottom edge of the text frame
var bounds = [y, x, bottom, width];
for (var t = 0; t < tweets.length; t++) {
var tweet = tweets[t];
// create the a new text frame and set its geometric bounds
var newTextFrame = page.textFrames.add();
newTextFrame.geometricBounds = bounds;
// fill the box with a text and apply the style
newTextFrame.contents = tweet.name;
newTextFrame.parentStory.texts[0].applyParagraphStyle(styleContent, true);
// resize the created text box with auto sizing
newTextFrame.textFramePreferences.autoSizingReferencePoint = AutoSizingReferenceEnum.TOP_CENTER_POINT;
newTextFrame.textFramePreferences.autoSizingType = AutoSizingTypeEnum.HEIGHT_ONLY;
// set the new geometric bounds for the next text frame
bounds = newTextFrame.geometricBounds; // get the bounds of the created text frame
bottom = bounds[2]; // get the new bottom edge
bounds[0] = bottom; // set the top edge for the next text frame
bounds[2] = bounds[2] + min_heigth; // set the bottom edge for the next text frame
}
Result:
The code a bit verbose, for educational purposes. Actually it can be way shorter.

Related

Given the X, Y, width and, height variables How do I find the start and end values of a shape in Google App Script?

So I was able to grab the width, hight, X, and y value of a frame in Figma. However, Google App Script is asking for start and the end of the the index of the shape in Google App script. how do I manipulate the values of X and Y to get the start and end of the the index I'm not even sure what the start and the end of the box is and how to find it.
How do I work with these the width and the height variables to find the start and end value of a shape in Google App Script
I am using Figma API and Google App Script
My end goal is to make Ann exporter from Figma to Google Slides
JSON File
absoluteBoundingBox: {
x: -460,
y: -333,
width: 586,
height: 586
},
Google App Script
var frameJSON.x = {};
var frameJSON.y = {};
var frameJSON.width = {};
var frameJSON.height = {};
var subRange = textRange.getRange();
Logger.log(
"Sub-range Start: " +
subRange.getStartIndex() +
"; Sub-range End: " +
subRange.getEndIndex() +
"; Sub-range Content: " +
subRange.asString()
);
When you insert a shape with SlidesApp you can getText to retrieve the TextRange object in it.
You can then manipulate this object to insert the text you want into your shape.
Here is some sample code:
function insertShapeWithText() {
//sample shape data
var x = 300;
var y = 250;
var width = 100;
var height = 3000;
var text = "Bla bla bla";
var pre = SlidesApp.getActivePresentation();
var slide = pre.getSlides()[0];
var shape = slide.insertShape(SlidesApp.ShapeType.DIAMOND, x, y, width, height);
shape.getText().setText(text);
}

Photoshop Scripting: Creating a selection, then fitting a text box inside

Using Javascript for Photoshop (ExtendScript) I am trying make a dynamic text box.
My end goal is a script that the user writes their own text into a new window pop up, then that text is positioned in the bottom 10% of the image, scaled dynamically to fit the bottom 10% of an image.
So, whatever the dimensions of the lower 10% (created by resizeCanvas argument) the text always fits within it comfortably. So when the user adds a few words it does not look too small and when they add a longer sentence it does not overspill.
Question originally asked here (with pictures) https://forums.adobe.com/message/10952562#10952562
Example Start Image:
start image
Window Pop up :
custom PopUp
Small Text Outcome:
outcome1
Long Text Outcome:
outcome 2
This is where I am currently:
#target Photoshop
app.preferences.rulerUnits = Units.PIXELS;
app.preferences.typeUnits = TypeUnits.PIXELS;
var doc = app.activeDocument;
var hgt10 = doc.height / 10;
var FillColor = new SolidColor;
FillColor.rgb.hexValue = 'ff0000';
var newHgt = doc.height + hgt10
doc.resizeCanvas(doc.width, newHgt, AnchorPosition.TOPCENTER);
var win = new Window('dialog', "Custom Text");
var grp1 = win.add('group');
grp1.alignChildren = "top";
var txt = grp1.add('edittext', [0, 0, 300, 150], "Write your message", {multiline: true, scrolling: true});
txt.active = true;
var grp2 = win.add('group');
var goBtn = grp2.add('button', undefined, "Run");
grp2.add('button', undefined, "Cancel");
var newLyr = doc.artLayers.add();
newLyr.kind = LayerKind.TEXT;
// CONTENTS
var txtLyr = newLyr.textItem
txtLyr.font = "ArialMT"
txtLyr.contents = txt.text
var uV = new UnitValue(20,'mm')
txtLyr.size = UnitValue(25,'mm')
txtLyr.color = FillColor;
txtLyr.position = [25, 2200] // x, y - Need to fix position to fit in the lower 10%
goBtn.onClick = function() {
win.close();
}
win.show();
So the areas I would like some assistance with is:
1) Getting the text to be positioned in the new area (created by the resizeCanvas argument extending by 10%)
2) Adding line breaks and automatically sizing to fit a variety of length text files into the field.
Kind Regards,
To add line breaks, replace all \ns with \rs (at least for Windows, I have a feeling that this Mac version may require something else, but I'm not sure):
txtLyr.contents = txt.text.replace(/\n/gm, '\r')
To position text you need to know the size (that'll depend on lines number) and leading.
var lines = txt.text.split('\n'); //getting the number of lines
var maxSize = hgt10 / lines.length; //calculating max size for a line (including the leading) = 10% of doc height devided by lines number
newLyr.textItem.useAutoLeading = true // making sure that text uses autoleading
var leading = newLyr.textItem.autoLeadingAmount; //autoleading value is in % of text size
maxSize = maxSize / leading * 100; // recalculating text size based on leading in pixels
txtLyr.size = maxSize;
txtLyr.position = [25, originalDocHeight + maxSize]
Result with one line and with several lines:

PaperJS Rotate function displaying item two times

when i am trying to rotate the path it is displaying same item two times. can some one help me to prevent the item to display twice.
var center1 = ball1.position;
var center2 = ball2.position;
// Create a triangle shaped path
var triangle = new Path.RegularPolygon(new Point(80, 70), 3, 12);
triangle.id = Math.random();
triangle.fillColor = '#fff';
//triangle.selected = true;
triangle.strokeColor = '#00304A';
triangle.strokeWidth = 2;
triangle.position = path.position; **
var arrowAngle = 90 + ((center2.subtract(center1)).angle); ** triangle.rotation = arrowAngle;
I'm not sure why you're getting two triangles (there isn't enough code for me to see what the reason for that is) but the main problem is that rotation is a read-only property - you cannot set it. Try using rotate().
http://paperjs.org/reference/path/#rotate-angle

Keeping a portion of an image always visible on the canvas

I want to make sure that my instance of fabric.Image is always visible.
It doesn't need to be 100% visible, but a portion of it should always be seen. I've seen a few other questions that are similar and I've based my solution on them, but I haven't found anything that completely solves the problem.
My current solution works when no rotation (angles) have been applied to the image. If the angle is at 0 then this solution is perfect, but once the angle start changing I'm having problems.
this.canvas.on('object:moving', function (e) {
var obj = e.target;
obj.setCoords();
var boundingRect = obj.getBoundingRect();
var max_pad_left_over_width = 50;//obj.currentWidth * .08;
var max_pad_left_over_height = 50;//obj.currentHeight * .08;
var max_top_pos = -(obj.currentHeight - max_pad_left_over_height);
var max_bottom_pos = obj.canvas.height - max_pad_left_over_height;
var max_left_pos = -(obj.currentWidth - max_pad_left_over_width);
var max_right_pos = obj.canvas.width - max_pad_left_over_width;
if(boundingRect.top < max_top_pos) {
obj.setTop(max_top_pos);
}
if(boundingRect.left < max_left_pos){
obj.setLeft(max_left_pos);
}
if(boundingRect.top > max_bottom_pos) {
obj.setTop(max_bottom_pos);
}
if(boundingRect.left > max_right_pos) {
obj.setLeft(max_right_pos);
}
});
I've created an example: https://jsfiddle.net/krio/wg0aL8ef/24/
Notice how when no rotation (angle) is applied you cannot force the image to leave the visible canvas. Now, add some rotation to the image and move it around. How can I get the rotated image to also always stay within view? Thanks.
Fabric.js provides with two object properties isContainedWithinRect and intersectsWithRect which we can use to solve the problem.
An object should not be allowed to go outside the canvas boundaries. This can be achieved if we somehow manage to make sure that the object is either inside the canvas, or at least intersects with the canvas boundaries.
For this case, since you want some portion of the image inside the canvas, we will take a rect smaller than the canvas instead of canvas boundaries to be the containing rect of the image.
Using the properties mentioned to make sure that the above two conditions are satisfied after any movement. Here's how the code looks like:
var canvas = new fabric.Canvas('c');
var imgElement = document.getElementById('my-img');
var imgInstance = new fabric.Image(imgElement, {
top: 0,
left: 0
});
imgInstance.setScaleX(0.6);
imgInstance.setScaleY(0.6);
canvas.add(imgInstance);
var prevLeft = 0,
prevTop = 0;
var max_pad_left_over_width = imgInstance.currentWidth * .08;
var max_pad_left_over_height = imgInstance.currentHeight * .08;
var max_top_pos = max_pad_left_over_height;
var max_bottom_pos = imgInstance.canvas.height - max_pad_left_over_height;
var max_left_pos = max_pad_left_over_width;
var max_right_pos = imgInstance.canvas.width - max_pad_left_over_width;
var pointTL = new fabric.Point(max_left_pos, max_top_pos);
var pointBR = new fabric.Point(max_right_pos, max_bottom_pos);
canvas.on('object:moving', function(e) {
var obj = e.target;
obj.setCoords();
if (!obj.intersectsWithRect(pointTL, pointBR) && !obj.isContainedWithinRect(pointTL, pointBR)) {
obj.setTop(prevTop);
obj.setLeft(prevLeft);
}
prevLeft = obj.left;
prevTop = obj.top;
});
Here's the link to the fiddle: https://jsfiddle.net/1ghvjxbk/ and documentation where these properties are mentioned.

Update DIV content on click without directly knowing its ID

I am building 2D tile game map. Each tile is a div in 2D array (var tiles = []). Below is the function which builds a tile based on some arguments defined somewhere else:
function Tile(rnd, px, py, nid) {
var self = this;
var _types = ["grass","forest","hills","swamp","forest", "mountains"];
var height = 60;
var width = 60;
var tileID = nid // new numeric tile id
var id = "tile_"+px+py+rnd; // old tile ID
var x = px;
var y = py;
var type = _types[rnd];
var img = 'img/maptiles/'+type+'.png';
this.Draw = function() {
var div = $("<div class='tile'></div>");
div.attr('id',tileID).data('type',type).data('x',x).data('y',y);
div.get(0).tile = self;
div.css({top:height*y, left:width*x});
div.css({"background":"url('"+img+"')"});
div.appendTo('#map-content');
};
this.Alert = function() {
alert("Tile type: "+type+". Tile ID: "+tileID+" ");
};
this.Move = function(){ // moves player to available tile, in this case if player stands to a tile before the clicked one
alert("start move! Tile type: "+type+". Tile ID: "+tileID+" ");
if (playerPosition === tileID - 1) {
$("#player").remove();
$("#????")").append('<img id="player" src="Pictures/maptiles/player.png" />');
playerPosition = tileID;
alert("Player position now: " + playerPosition);
}
};
}
As a result I end up with m x n map made of divs each with a unique numeric ID starting with 1. I know that using numeric IDs for elements is(was?) frowned upon, though HTML5 specs do not actually prohibit this anymore.
Now what I want to do is to place a player icon (player.png) depending on player's position. Starting position is 1 and I am using tiles (aka divs) numeric IDs to calculate legal moves (can move only to bordering tiles).
When a tile is clicked this.Move is called. Its purpose is to check if player can move on a clicked tile (just one IF statement for now to test) and must remove player.png and redraw it on the clicked div.
Here I run into a problem since I need to somehow use the tileID (which is equal to Div ID) to tell browser to append the DIV which is belong clicked (as I obviously do not write a function for every div on the field). I think that since I can get the DIV id on click I can use it somehow.
I tried to experiment with this.div.id:eq("+tileID+") but with no luck.
UPD:
Adding click handler as requested. Note this is within var Map, which is responsible for building the map, rendering and handling some user input:
var Map = new function() {
var maxHorz = 20;
var maxVert = 5;
var tiles = [];
this.init = function() {
for(var i=0; i<maxVert; i++) {
tiles[i] = [];
for(var j=0; j<maxHorz; j++) {
tiles[i][j] = new Tile(Math.random()*6|0, j, i, tileID++);
}
}
Render();
Setup();
};
this.GetMap = function() {
return tiles;
};
var Render = function() {
$.each(tiles, function(k,v){
$.each(v, function(k,t){
t.Draw();
});
});
};
var Setup = function(){
$('#map-content').on('click','div.tile', function(e){
//var tile = tiles[$(this).data('y')][$(this).data('x')];
this.tile.Move();
});
}
this.Redraw = function(x,y) {
};
}
Map.init();
The answer is found, finally :)
$('#player').detach().appendTo($('#'+tileID))

Categories