Split Long Filename string in two Lines with Adobe Javascript - javascript

I am newbie using Javascript in Adobe. I have written script that catch the file name and add it as a title in the document, but problem is it doesn't split in 2 lines long file names. So long text file names are out of the page border.
For example file name is
"This is a text that I need to split in two lines How to write the script in order to split the long text and fit in the page"
This is just a sample. What I need to edit? And is it possible?
var trFileName = this.documentFileName.replace(/.*\/|\.pdf$/ig,""); //remove .pdf extension
var pageAmount = this.numPages; //define how many pages are there
this.addWatermarkFromText({
cText: trFileName,
nStart: 0,
nEnd: pageAmount,
nFontSize: 18,
aColor: color.blue,
cFont: "Helvetica-Bold",
nTextAlign: app.constants.align.center,
nHorizAlign: app.constants.align.center,
nVertAlign: app.constants.align.top,
//nHorizValue: -25,
nVertValue: -87.87
})

So looking at the documentation for Acrobat the quick way to do this is to add and use the bPercentage: -1 property which ought to scale the watermark to fit the page with a max of 100%. Obviously, the longer the text, the smaller the the typesize, but it ought to only scale when needed.
The big problem here is "how big is too big?" Since most fonts are not fixed-width. So if you do not have some function to check the size of the type in that font, you would need to set up a temp image, set the type, then measure the dimensions of the image, then decide how much to break the text.
The worst case for English though is probably going to be an endless string of W, so if you want to hard code the font size and some reasonable character limit, you can get a reasonable max character count by counting how many Ws it takes to go out of bounds.
Once you have that number you can split the string.
One way to split a string is to use substring:
var cDisplayText = trFilename;
var nMaxChars = 10;
var cAdobeNewline = '\r';
var cLineBreakChar = '-';
if (cDisplayText.length > nMaxChars && cDisplayText.length > 0) {
cDisplayText = cDisplayText.substring(0, nMaxChars)
+ cLineBreakChar
+ cAdobeNewline
+ cDisplayText.substring(nMaxChars);
}
And then replace cText: trFileName, with cText: cDisplayText,
I have not tested this and my javascript has atrophied a little, but the idea is to test the string length and insert a hyphen and a line break.
This (obviously) does not account for case where there is more than 1 line break required.

Related

Can you force a string to split into 'n' lines?

On a website I'm designing, I have a page with rows of categories thumbnails and their titles.
The problem is : some category titles are long, other are shorts... depending on the monitor width some will take 2 lines of text, or even 3, but not the others, creating ugly staircases.
I've managed to identify the highest title and min-height every other one accordingly with javascript.
But, it's still ugly. To make it better, I'd want every title to break into as many lines than the longest one.
That's where I come short... I couldn't find a way to do that so far.
Here's how it is now :
Here's the intended result :
Any idea how I could accomplish this ?
If you want to do that with a javascript solution you can count characters or words and add <br> after n amount. Here is a sample that will add a line break after every 20 characters:
var string = document.getElementById('text');
string.innerHTML = string.textContent.replace(/(.{20})/g, "$1<be>");
Then you can create a function with a loop for each element and do some if statement logic there. For example - add a break if the character count is more than...

Precise width of a monospace character

I'm working on a code editor with syntax highlighting and I need to know the precise width of a monospace character. I use this value to calculate the position of a character on a line, which I need to know so I can place various GUI elements, such as text cursors (there can be multiple), selection rectangles, warning tooltips, etc. Up until now I've been using the following function:
function getCharacterWidth(char, fontFamily, fontSize) {
let span = document.createElement("span");
span.style.fontFamily = fontFamily;
span.style.fontSize = fontSize;
span.style.position = "absolute";
span.style.visibility = "hidden";
span.style.width = "auto";
span.style.whiteSpace = "nowrap";
span.style.padding = "0";
span.style.margin = "0";
span.style.letterSpacing = "0px";
span.style.wordSpacing = "0px";
span.innerText = char;
document.body.appendChild(span);
let width = span.getBoundingClientRect().width;
span.remove();
return width;
}
It has been working great, but then I noticed a problem on Google Chrome. When my text editor is rendering a large line, with thousands of characters, the character position is not being properly calculated because of rounding issues. It seems that on Google Chrome, the width returned by getBoundingClientRect() has a precision of at most 5 decimal places, which is not ideal for my use case. On Firefox, the precision seems to be much higher, going up to 15 decimal places, which is why I never had this problem there.
After some digging, I heard about this idea of calculating the width of a character based on the width of a span containing thousands of that character (https://stackoverflow.com/a/56379770/2197150). So, in my original function I replaced span.innerText = char with span.innerText = char.repeat(10000) and returned width / 10000. It helped, but the calculation is still perceptibly off when I'm dealing with large lines.
So here I am. How can I calculate the width of a character with high precision, like Firefox does, in other browsers?
This is not a clean solution, but I suspect there is no really clean solution.
You could keep a "column map", based on the syntax formatting <span>s you already have flying around. Say your highlighter gives you:
<div class="line">
var long_line_of_variables = <span class="number">123</span>;
</div>
you could measure this span's left offset + the column it appears in (= length of textContent of the siblings before it = 29) and arrive at
colums = {
'29': 290.456
}
Now you can interpolate that column 14 is at 290.456*14/29=140.22 pixels.
The more spans we add, the better we can guess:
colums = {
'29': 290.456,
'2900': 28997.000 // whoops, not what we would have calculated!
}
This method is heuristic, so you'll need to find a strategy, which will work across browsers, zoom/font-scale settings etc., including
adding more and more spans to this "map"
but maybe "cleaning" it once in a while?
keep a global map or one per line?
be smart about interpolation: pick the nearest map entry (in the best interval between entries)
add more "span probes"
split long lines without any formatting into chunks of N characters, wrapping them in spans, or inserting empty spans in between
maybe just a probe span right at the end of each line?
Having worked on similar problems and heuristics, my advice would be: don't. :)
It involves lots of tweaking and testing, and probably is a cross-platform nightmare (e.g. compare font rendering and rounding in Firefox on Win vs Linux vs MacOS vs iOS). Instead, try to attach anything to a localized span. I understand that this is probably way harder -- lots of text editors struggle with long lines, esp. when it comes to MB-size compiled JS...
I came up with a solution that has been working well for my use case. The idea is to calculate the width of a character based on the largest line of my editor. My line elements look something like this:
<div class="line"><span class="keyword">let</span><span class="space"> </span><span class="identifier">foo</span></div>
If we want to calculate the width of a character based on that line, here is what we do:
let lineElement = document.querySelector(".line");
let lineWidth = lineElement.getBoundingClientRect().width;
let charWidth = lineWidth / lineElement.textContent.length;
I keep track of the largest line I've rendered in my editor. Whenever I render a new largest line, I update charWidth based on that new line.
This allows me to calculate the position of a character at any given column within a line by doing a simple multiplication column * charWidth. It has been working pretty well so far, even for lines with 100000+ characters.
There is, however, one last thing I had to do to handle even larger lines. It seems that there is a bug on Google Chrome where, when you try to render a line with a huge text node, e.g. <span>many many characters...</span>, it will not give you the correct width of the element (see Inaccurate width of large element on Chrome). To overcome this issue, whenever I have to render a huge text, I split it into multiple spans, e.g. <span>many many </span><span>characters...</span>.
Now I can render 500000 characters long lines with font size 48 with no problems. Above that, things start to get weird again, with miscalculations and other weird browser behaviors. So I decided to set a hard limit of 500000 characters rendered in a line. Everything beyond the 500000nth characters is hidden from the user.

Image file size from data URI in JavaScript

I am not sure it's even possible but - can I get the image file size from data URI?
For example, let's say there is an IMG element where src goes:
src="data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD...
Based on the src, can I get the image file size by using plain JavaScript? (without server request)
If you want file size, simply decode your base64 string and check the length.
var src ="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP/// yH5BAEAAAAALAAAAAABAAEAAAIBRAA7";
var base64str = src.substr(22);
var decoded = atob(base64str);
console.log("FileSize: " + decoded.length);
If you're okay with a (very good) estimate, the file size is 75% of the size of the base64 string. The true size is no larger than this estimate, and, at most, two bytes smaller.
If you want to write one line and be done with it, use atob() and check the length, as in the other answers.
If you want an exact answer with maximum performance (in the case of gigantic files or millions of files or both), use the estimate but account for the padding to get the exact size:
let base64Length = src.length - (src.indexOf(',') + 1);
let padding = (src.charAt(src.length - 2) === '=') ? 2 : ((src.charAt(src.length - 1) === '=') ? 1 : 0);
let fileSize = base64Length * 0.75 - padding;
This avoids parsing the entire string, and is entirely overkill unless you're hunting for microoptimizations or are short on memory.
Your best option is to calculate the length of the base64 string itself.
What is a base64 length in bytes?
You have to convert the base64 string to a normal string using atob() and then check it length, it will return a value that you can say is close to the actual size of the image. Also you don't need the data:image/jpeg;base64, part from the data URI to check the size.
This is a universal solution for all types of base64 strings based on Daniel Trans's code.
var src ="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP/// yH5BAEAAAAALAAAAAABAAEAAAIBRAA7";
var base64str = src.split('base64,')[1];
var decoded = atob(base64str);
console.log("FileSize: " + decoded.length);
The other solutions make use of atob, which has now been deprecated. Here is an up-to-date example, using Buffer instead.
const src="data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD...";
const base64str = src.split('base64,')[1]; //remove the image type metadata.
const imageFile = Buffer.from(base64str, 'base64'); //encode image into bytes
console.log('FileSize: ' + imageFile.length);

How to get the number of lines in a text file without opening it?

I'm developing this code that, after the user selects a directory, it display a table of the files contained in that location with their details (name, type, size ...).
A directory may contain a lot of files.
I succeeded to achieve that. But, my problem is that I want to display the number of lines in each file. I can get the number of lines using this JavaScript code :
var reader = new FileReader();
var textFile = $("#file").get(0).files[0];
reader.readAsText(textFile);
$(reader).on('load', processFile);
/*And in processFile() i use this line to get the number of lines :*/
nbLines = (file.split("\n")).length;
The above code work as expected and it give me what I want, but it may a heavy process if there is so many files in the selected directory!
The Question : Is there a way to get the number of lines in a text file without reading it?
Regards!
You can't count the number of lines in a file without reading it. The operating systems your code runs on do not store the number of lines as some kind of metadata. They don't even generally distinguish between binary and text files! You just have to read the file and count the newlines.
However, you can probably do this faster than you are doing it now, if your files have a large number of lines.
This line of code is what I'm worried about:
nbLines = (file.split("\n")).length;
Calling split here creates a large number of memory allocations, one for each line in the file.
My hunch is that it would be faster to count the newlines directly in a for loop:
function lineCount( text ) {
var nLines = 0;
for( var i = 0, n = text.length; i < n; ++i ) {
if( text[i] === '\n' ) {
++nLines;
}
}
return nLines;
}
This counts the newline characters without any memory allocations, and most JavaScript engines should do a good job of optimizing this code.
You may also want to adjust the final count slightly depending on whether the file ends with a newline or not, according to how you want to interpret that. But don't do that inside the loop, do it afterward.
There is not way to know the number of lines without opening the document. Regarding the performance issues that you are having it comes from the .split() most probably.
You are loading the file as a string in memory and then generating as many strings as lines are in this files.
If a file contains 1000 lines of code the resulting ram usage will be
1 String (whole files)
1000 Strings (1 string per line)
I would recommend chaging this for a evaluation using RegEx. Here's an example
var file = ("this\nis a string\n with new\nlines");
var match = file.match(/\r?\n/g);
alert(match.length);
Keep in mind that a different regex might be required depending on your files.
This will surely improve the performance.
Update for 2021:
Reading the file as text is always a bad idea. With current optimised languages, almost all languages are super fast in processing loops, so looping will always be faster than loading the text into memory and splitting.
for NodeJS, please see ReadLine. Although not recommended to do such operations in node, being Single Threaded, I can read Big CSVs pretty fast using ReadLine.
A text file usually contains an operation line at the bottom of the screen which allows you to place the cursor on the screen and shows the line and location of the character it is located. In which case if the cursor is at the last character the total lines would be indicated.

Setting a Regex Cursor

I want to set a cursor in javascript so when I use regex, it recognizes what data it has already parsed, and continues from that point. Below is a snippet of code that shows what the file is like.
# vtk DataFile Version 4.0
-3659.0757 3746.6780 3628.1143
-3659.6714 3746.2517 3627.9539
-3660.1450 3745.8142 3627.9270
-3660.4631 3745.3735 3628.0605
-3660.6931 3745.0708 3628.1416
LINES 207 31529
581 0 1 2 3 4 ... 579 580
Currently I pick up the float float float pattern correctly, and I want my code to continue to LINES 207 31529 and then 581. This code picks up LINES, but instead of going to the 581 it goes back to the top of the file and takes 4 for numLines.
var LINES = /(LINES)[ ]+[\d]+[ ]+[\d]+[\n]/;
var recogLines = /[\d]+/;
var numLines = parseInt(recogLines.exec(data));
I saw something online about \G, but I don't think javascript recognizes that (or I'm just not using it correctly). How do I keep a cursor so the same data isn't iterated over and over again? Thanks!
I would suggest something along the lines of (may not be exactly syntactically correct):
var LINES = '/(LINES)[ ]+([\d]+)[ ]+([\d]+)\n/';
var data = <input>;
var output = new Array();
result = LINES.exec(data);
while(result) {
output.push([result[1], result[2], result[3]]);
result = LINES.exec(data.substring(result.index + result[0].length);
}
but at that point, I would use the global modifier:
var LINES = '/^(LINES)[ ]+([\d]+)[ ]+([\d]+)$/gm';
result = LINES.exec(data);
would give you an array:
array[0][2] // first LINE, first set of numbers
array[0][3] // first LINE, second set of numbers
array[1][2] // second LINE, first set of numbers
array[1][3] // second LINE, second set of numbers
Just my 2 cents.
Edit:
If you are writing a parse, I could see the reason for the first. Just add your code to parse the lines (and keep track of your cursor position) before running your pattern match again, and pass that to the substring instead of the index of the previous match plus the length of the match.

Categories

Resources