// jshint unused: true, undef: true, evil:true /* globals app, alert, LocationOptions, UndoModes, ColorSpace, UnitValue, AutoEnum, MeasurementUnits, AlternateGlyphForms, File, Folder, ConditionIndicatorMethod, ScriptLanguage, ChangeConditionsModes, Window, exit, $*/ ////////////////////////////////////////////////////////////////////////////////////////////////////////// // InDesign Thin Font Finder Version 1 // // By Trevor http://www.creative-scripts.com // // Created 27th March 2017 Last modified 30th March 2017 // // See http://www.creative-scripts.com/indesign-thin-font-finder for explanation, instructions and EULA // // Do not use without looking at and accepting the EULA there // // Please don't remove this comment block // ////////////////////////////////////////////////////////////////////////////////////////////////////////// if (!app.selection[0] || !app.selection[0].properties.baseline) { alert('Please select some text. The text of the entire story will be tested'); exit(); } var thinnys, highlight, csvReport, ui, goForIt; function getThinnys(minThickness, debug) { var doScriptWrapper; doScriptWrapper = function() { var bad, doc, font, debugFontLayer, fontName, getThickness, good, l, l1, l2, map, outline, page, pageAndChar, points, pointSize, ratio, result, setUpTestChar, story, testChar, testThickness, textRangeFonts, textRangePoints, thickness, ugly, value, x, green, red, blue; var jsonFolder, jsonFile, c, jsonArray, jsonToObject, json, jsonString; story = app.selection[0] && app.selection[0].properties.baseline && app.selection[0].parentStory; if (!story) { alert('Please select some text. The text of the entire story will be tested'); return 'No Valid Selection'; } doc = app.activeDocument; good = []; map = {}; bad = []; ugly = []; testThickness = minThickness; if (debug) { green = doc.swatches.itemByName('Green'); if (!green.isValid) green = doc.colors.add({ name: 'Green', colorValue: [0, 255, 0], space: ColorSpace.RGB }); red = doc.swatches.itemByName('Red'); if (!red.isValid) red = doc.colors.add({ name: 'Red', colorValue: [255, 0, 0], space: ColorSpace.RGB }); blue = doc.swatches.itemByName('Blue'); if (!blue.isValid) blue = doc.colors.add({ name: 'Blue', colorValue: [0, 0, 255], space: ColorSpace.RGB }); debugFontLayer = doc.layers.itemByName('Debug Font Layer'); if (!debugFontLayer.isValid) debugFontLayer = doc.layers.add({ name: 'Debug Font Layer' }); } // Convert the minThickness to points // Some units won't convert, ain't going to deal with those for free value = app.scriptPreferences.measurementUnit !== AutoEnum.AUTO_VALUE ? app.scriptPreferences.measurementUnit : app.activeDocument.viewPreferences.horizontalMeasurementUnits; if (value === MeasurementUnits.INCHES_DECIMAL) { value = MeasurementUnits.INCHES; } var ammount, unit; ammount = +(('' + minThickness).replace(/\D+$/, '')); unit = ('' + minThickness).match(/[^\d\.\s]+$/); minThickness = unit ? UnitValue(minThickness).as(MeasurementUnits.POINTS) : UnitValue(ammount, value).as(MeasurementUnits.POINTS); if (!minThickness) { return 'Twit'; } ////////////////////////////////////////////////////////////////////////////// // Prep work for later for matching sides of a T for measuring the vertical // ////////////////////////////////////////////////////////////////////////////// getThickness = function(points, outline, fontName, debugFontlayer) { var c, group, highestPoint, item, l, leftPoint, lowestPoint, overlappingBottom, overlappingBottomXSide1, overlappingBottomXSide2, overlappingTop, overlappingTopXSide1, overlappingTopXSide2, page, point, rightPoint, section, side1, side2, sideHorizontalReferencePoint, T, tGroup, thicknessBottom, thicknessTop; var high, low, left, right; var horizontalReferencePoint, verticalReferencePoint, horizontalReferencePointRatio, verticalReferencePointRatio; var side1Candidate, side2Candidate, point1, point2, distance; var s1, s2; var side1Candidates, side2Candidates; T = []; l = points.length; //////////////////////////////////////////////////////////////////////////////////// // Get the hight and lowest point of the T so we can split it vertically // // Once split we can match the 2 largest verticals on each side and get the width // // This method is not going to work with highly decorative or curvy fonts // // But will work pretty well with the cast majority of (English) fonts // // We are flattening the curves for simplicity // // The method is measuring the horizontal distances between the 2 top points // // and between the 2 bottom points of the matching verticals // // If the average distance is not less than our test distance the the fonts OK // // See the annotation below for more details on this // // The methods not going to be 100% // // but it's probably the best that extendScript can digest at a reasonable speed // // It should be able to handle most even long documents pretty quickly // //////////////////////////////////////////////////////////////////////////////////// highestPoint = { index: 0, y: 1e10 }; lowestPoint = { index: 0, y: -1e10 }; leftPoint = { index: 0, x: 1e10 }; rightPoint = { index: 0, x: -1e10 }; for (c = 0; c < l; c++) { point = points[c].length === 3 ? points[c][1] : points[c]; T[c] = { x: point[0], y: point[1] }; if (T[c].y > lowestPoint.y) { lowestPoint = { index: c, y: T[c].y }; } if (T[c].y < highestPoint.y) { highestPoint = { index: c, y: T[c].y }; } if (T[c].x > rightPoint.x) { rightPoint = { index: c, x: T[c].x }; } if (T[c].x < leftPoint.x) { leftPoint = { index: c, x: T[c].x }; } } high = highestPoint.y; low = lowestPoint.y; left = leftPoint.x; right = rightPoint.x; ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // we now have the hight and lowest points of the T so we can now split it into 2 arrays // // the one will be the left side the other the right side // // We don't know from what position point[0] of the T is // // so we need to make sure that the one side of the array contains all the points from the lowest to the highest // // and the other from the highest to the lowest // // Lest say the T has 10 points in it and the lowest is point 3 and the highest point 7 // // In such a case we can the one side to contain points 3, 4, 5, 6, 7 // // The other side should contain points 7, 8, 9, 0, 1, 2, 3 // // We then can calculate the largest vertical on each side // // once we have the largest verticals we can check that they more or less are on the same vertical level // // This part of the test is only need for very curvy fonts // // We will compare the mid y points of the 2 sides // // If they don't come close enough together then look for the next longest point and see if the verticals match better // // We're only going to compare the 2 longest parts of each side // // Once we find the matching sides we're calculate what the x position of the longer side will be // // for the matching y positions of the shorter side // // we then measure the horizontals between these 2 real and 2 imaginary points // // If the distance is to thin then add the font with it's side to the bad list // // If we were being paid for this we would use some Bezier maths to take into consideration the curves // // Sadly however, we are not being paid for this so we won't // ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Split the T vertically if (highestPoint.index > lowestPoint.index) { side1 = T.slice(lowestPoint.index, 1 + highestPoint.index); side2 = T.slice(highestPoint.index, l + 1).concat(T.slice(0, lowestPoint.index + 1)); } else { side1 = T.slice(highestPoint.index, 1 + lowestPoint.index); side2 = T.slice(lowestPoint.index, l + 1).concat(T.slice(0, highestPoint.index + 1)); } s1 = side1.slice(); s2 = side2.slice(); l = s1.length; while (l--) { s1[l] = [s1[l].x, s1[l].y]; } l = s2.length; while (l--) { s2[l] = [s2[l].x, s2[l].y]; } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // To adapt to non English scripts we would use different test characters, in Hebrew (5) a Vav, in Arabic (4) an Alef, Japaneses (1) a Shi // // So if one wanted to allow for other scripts one would change the T to the appropriate character and adjust the positions // // The script language is contained in the fonts writingScript property // // As a general rule quality non English font will also contain English letters in the font that are designed with the same font weight // // The cheaper fonts will only contain the language that the font was made to cater // ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// horizontalReferencePointRatio = 0.5; verticalReferencePointRatio = 0.6; horizontalReferencePoint = left + horizontalReferencePointRatio * (right - left); verticalReferencePoint = high + verticalReferencePointRatio * (low - high); side1Candidate = { segment: 0, distance: 1e10 }; side2Candidate = { segment: 0, distance: 1e10 }; side1Candidates = []; side2Candidates = []; ///////////////////////////////////////////////////////// // Start with the shorter side to help find it's match // ///////////////////////////////////////////////////////// l = side2.length - 1; for (c = 0; c < l; c++) { point1 = side2[c]; point2 = side2[c + 1]; if (point1.y <= point2.y) { point1.top = { x: point1.x, y: point1.y }; point1.bottom = { x: point2.x, y: point2.y }; } else { point1.top = { x: point2.x, y: point2.y }; point1.bottom = { x: point1.x, y: point1.y }; } if (point1.top.y <= verticalReferencePoint && point1.bottom.y >= verticalReferencePoint) { side2Candidates.push(point1); // We are measuring the distance of our candidates from the horizontal reference point // The one closest to that reference will be selected // The same will be done for the other side // The side which will be closest to the reference will itself become a new reference // The other side will be measured from the new reference with the closest one becoming the final candidate for the other side distance = 0.5 * (Math.abs(point1.bottom.x - horizontalReferencePoint) + Math.abs(point1.top.x - horizontalReferencePoint)); if (distance < side2Candidate.distance) { side2Candidate = { segment: point1, distance: distance }; } } } // Same again for the other side except that distance is measured from side2Candidate x points l = side1.length - 1; for (c = 0; c < l; c++) { point1 = side1[c]; point2 = side1[c + 1]; if (point1.y <= point2.y) { point1.top = { x: point1.x, y: point1.y }; point1.bottom = { x: point2.x, y: point2.y }; } else { point1.top = { x: point2.x, y: point2.y }; point1.bottom = { x: point1.x, y: point1.y }; } if (point1.top.y <= verticalReferencePoint && point1.bottom.y >= verticalReferencePoint) { side1Candidates.push(point1); distance = 0.5 * (Math.abs(point1.bottom.x - horizontalReferencePoint) + Math.abs(point1.top.x - horizontalReferencePoint)); if (distance < side1Candidate.distance) { side1Candidate = { segment: point1, distance: distance }; } } } l2 = side2Candidates.length; l1 = side1Candidates.length; //////////////////////////////////////////////////////////////////////////////////////////////////// // It could be that there's no point that goes through our chosen point // // We could mess around to fix this up but instead we'll wait for someone to pay for that feature // //////////////////////////////////////////////////////////////////////////////////////////////////// if (!l1 || !l2) { return 0; // { // min: 0, // max: 1e12 // }; } if (l2 < l1 || (l2 === l1 && side1Candidate.distance > side2Candidate.distance)) { sideHorizontalReferencePoint = 0.5 * (side2Candidate.segment.top.x + side2Candidate.segment.bottom.x); side1Candidate = { segment: 0, distance: 1e15 }; for (c = 0; c < l1; c++) { section = side1Candidates[c]; distance = 0.5 * (Math.abs(section.bottom.x - sideHorizontalReferencePoint) + Math.abs(section.top.x - sideHorizontalReferencePoint)); if (distance < side1Candidate.distance) { side1Candidate = { segment: section, distance: distance }; } } } else { sideHorizontalReferencePoint = 0.5 * (side1Candidate.segment.top.x + side1Candidate.segment.bottom.x); side2Candidate = { segment: 0, distance: 1e15 }; for (c = 0; c < l2; c++) { section = side2Candidates[c]; distance = 0.5 * (Math.abs(section.bottom.x - sideHorizontalReferencePoint) + Math.abs(section.top.x - sideHorizontalReferencePoint)); if (distance < side2Candidate.distance) { side2Candidate = { segment: section, distance: distance }; } else {} } } side2Candidate = side2Candidate.segment; side1Candidate = side1Candidate.segment; //////////////////////////////////////////////////////////////////////////////////// // Calculate the vertical extremes were side2Candidate and side1Candidate overlap // // Then calculate the horizontal positions at those 2 vertical points // // The difference in the horizontal positions will be the thickness // //////////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////// // Could have done this next bit quite a bit better but this will do // /////////////////////////////////////////////////////////////////////// overlappingTop = (side1Candidate.top.y > side2Candidate.top.y) ? side1Candidate.top.y : side2Candidate.top.y; overlappingBottom = (side1Candidate.bottom.y < side2Candidate.bottom.y) ? side1Candidate.bottom.y : side2Candidate.bottom.y; // Work out the width at the overlappingTop and overlappingBottom points overlappingTopXSide1 = side1Candidate.top.x + (side1Candidate.top.x - side1Candidate.bottom.x) * (side1Candidate.top.y - overlappingTop) / (side1Candidate.bottom.y - side1Candidate.top.y); overlappingBottomXSide1 = side1Candidate.top.x + (side1Candidate.top.x - side1Candidate.bottom.x) * (side1Candidate.top.y - overlappingBottom) / (side1Candidate.bottom.y - side1Candidate.top.y); overlappingTopXSide2 = side2Candidate.top.x + (side2Candidate.top.x - side2Candidate.bottom.x) * (side2Candidate.top.y - overlappingTop) / (side2Candidate.bottom.y - side2Candidate.top.y); overlappingBottomXSide2 = side2Candidate.top.x + (side2Candidate.top.x - side2Candidate.bottom.x) * (side2Candidate.top.y - overlappingBottom) / (side2Candidate.bottom.y - side2Candidate.top.y); thicknessTop = Math.abs(overlappingTopXSide2 - overlappingTopXSide1); thicknessBottom = Math.abs(overlappingBottomXSide2 - overlappingBottomXSide1); // By this point we have either rejected the tested font @ size to the ugly // or have the coordinates of the top and bottom points of the matching sides if (outline) { // Draw some lines for debug page = outline.parentPage; tGroup = [outline]; tGroup.push(item = page.graphicLines.add({ strokeWeight: '0.4pt', strokeColor: red, name: 'Side 1' })); item.paths[0].entirePath = s1; tGroup.push(item = page.graphicLines.add({ strokeWeight: '0.4pt', strokeColor: blue, name: 'Side 2' })); item.paths[0].entirePath = s2; // Top Horizontal tGroup.push(page.graphicLines.add({ geometricBounds: [overlappingTop, overlappingTopXSide1, overlappingTop, overlappingTopXSide2], strokeWeight: '0.4pt', strokeColor: green, name: 'Top' })); // Bottom Horizontal tGroup.push(page.graphicLines.add({ geometricBounds: [overlappingBottom, overlappingBottomXSide1, overlappingBottom, overlappingBottomXSide2], strokeWeight: '0.4pt', strokeColor: green, name: 'Bottom' })); // Side 1 Vertical tGroup.push(item = page.graphicLines.add({ strokeWeight: '0.4pt', strokeColor: green, name: 'Side 1 Diagonal' })); item.paths[0].entirePath = [ [side1Candidate.top.x, side1Candidate.top.y], [side1Candidate.bottom.x, side1Candidate.bottom.y] ]; // Side 2 Vertical tGroup.push(item = page.graphicLines.add({ strokeWeight: '0.4pt', strokeColor: green, name: 'Side 2 Diagonal' })); item.paths[0].entirePath = [ [side2Candidate.top.x, side2Candidate.top.y], [side2Candidate.bottom.x, side2Candidate.bottom.y] ]; tGroup.push(item = page.graphicLines.add({ geometricBounds: [verticalReferencePoint, left, verticalReferencePoint, right], strokeWeight: '0.05pt', strokeColor: "Black", name: 'horizontalReferencePoint' })); tGroup.push(item = page.graphicLines.add({ geometricBounds: [high, horizontalReferencePoint, low, horizontalReferencePoint], strokeWeight: '0.05pt', strokeColor: "Black", name: 'verticalReferencePoint' })); group = page.groups.add(tGroup); // group.fillTint = 15; group.name = fontName; if (debugFontlayer) { group.itemLayer = debugFontlayer; } } // end of drawing ///////////////////////////////////////////////////////// // If we wanted we could return the min and max values // // That could allow for some more testing // // But instead we're just return the average // // // // if (thicknessTop > thicknessBottom) { // // return { // // max: thicknessTop, // // min: thicknessBottom // // }; // // } // // return { // // min: thicknessTop, // // max: thicknessBottom // // }; // ///////////////////////////////////////////////////////// return 0.5 * (thicknessTop + thicknessBottom); }; // end of getThickness ///////////////////////// // Some json Prep Stuff // ///////////////////////// // We can store in an json the results of fonts when we process them // That way it will be much quicker to retrieve the data from the json than using the DOM processing // Json is way quicker than xml and doesn't need any escaping jsonToObject = function(jsonString) { // this is not a universal converter just one for this sort of structure var json, myFind, jsonReg; json = {}; jsonReg = /"(.+)":([^,]+)/g; myFind = jsonReg.exec(jsonString); while (myFind) { json[myFind[1]] = myFind[2]; myFind = jsonReg.exec(jsonString); } return json; }; jsonArray = []; jsonFolder = new Folder(Folder.userData + '/Creative Scripts/Font Ratios'); jsonFolder.create(); // we should check for success, maybe next time jsonFile = new File(jsonFolder + '/Font Ratios.json'); jsonFile.encoding = 'UTF-8'; jsonFile.open('e'); jsonString = jsonFile.read(); if (!jsonString.length) { jsonFile.writeln('// List of fonts and the thicknesses of their stems'); jsonFile.writeln('// The thickness is in points of a font of a 1pt pointSize'); jsonFile.writeln('// One can manually adjust the thicknesses if desired'); } json = jsonToObject(jsonString); jsonString = null; ///////////////////////////////////////////// // Build a map of the fonts and sizes used // ///////////////////////////////////////////// textRangeFonts = story.textStyleRanges.everyItem().appliedFont; textRangePoints = story.textStyleRanges.everyItem().pointSize; l = textRangeFonts.length; while (l--) { fontName = textRangeFonts[l].name; if (!map[fontName]) { map[fontName] = { font: textRangeFonts[l], pointSizes: {} }; } map[fontName].pointSizes[textRangePoints[l]] = textRangePoints[l]; // if you wanted to map another property like horizontalScale // you would use something like // if !... map[fontName].pointSizes[textRangePoints[l]][horizontalScale[l]] = {} // map[fontName].pointSizes[textRangePoints[l]][horizontalScale[l]] = horizontalScale[l]; } var simpleDims = function(a) { var left, right, top, bottom, p, i, x, y; left = top = 1e14; right = bottom = -1e14; i = a.length; while (i--) { p = a[i].length === 2 ? a[i] : a[i][1]; x = p[0]; y = p[1]; if (x > right) { right = x; } else if (x < left) { left = x; } if (y > bottom) { bottom = y; } else if (y < top) { top = y; } } return { geometricBounds: [top, left, bottom, right], top: top, left: left, right: right, bottom: bottom, height: bottom - top, width: right - left }; }; var most = function(object, prop, greaterThan) { // Lets not waist time for nothing. if (object.paths.length === 1) { return object.paths[0]; } var base, n, l; base = { index: 0 }; base[prop] = greaterThan ? -1e14 : 1e14; l = object.paths.length; if (greaterThan) { while (l--) { n = simpleDims(object.paths[l].entirePath)[prop]; if (n > base[prop]) { base.index = l; base[prop] = n; } } } else { while (l--) { n = simpleDims(object.paths[l].entirePath)[prop]; if (n < base[prop]) { base.index = l; base[prop] = n; } } } return object.paths[base.index]; }; // Prepare test character in the event its needed setUpTestChar = function() { var page, textFrame, testChar; page = doc.pages.add(LocationOptions.AT_END); // page = doc.pages[0]; // as strait as it comes textFrame = page.textFrames.add({ geometricBounds: [0, 0, "200mm", "200mm"], contents: "T", itemLayer: debug ? debugFontLayer : doc.activeLayer }); testChar = textFrame.characters[0]; // Some of these are stuck in to show potential factors to be considered in processing the final width // The most problematic would seem to be the Glyph scaling testChar.properties = { pointSize: 100, desiredGlyphScaling: 100, maximumGlyphScaling: 100, minimumGlyphScaling: 100, fillColor: "Black", fillTint: 15, dropCapCharacters: 0, glyphForm: AlternateGlyphForms.NONE, horizontalScale: 100, strokeWeight: 0 }; return { testChar: testChar, page: page, textFrame: textFrame }; }; pageAndChar = false; /////////////////////// // Do some measuring // /////////////////////// // var maxThickness = 1e13; // this is not yet implemented for (x in map) { ratio = json[x]; if (ratio !== undefined) { ratio = +ratio; } else { if (!pageAndChar) { pageAndChar = setUpTestChar(); testChar = pageAndChar.testChar; page = pageAndChar.page; } testChar.appliedFont = map[x].font; // If the T is split into bits then the lower path which is normally the stem of the T will have a higher index // But this is not always the case, so we're presume that the tallest path will be the stem of the T // This might not always be the case but in the vast majority of cases it should work try { outline = testChar.createOutlines(false); points = most(outline[0], 'height', true).entirePath.slice(); ratio = getThickness(points, debug && outline[0], x, debugFontLayer); ratio = UnitValue(ratio, value).as(MeasurementUnits.POINTS) / 100; jsonArray.push('"' + x + '":' + ratio + ','); } catch (e) { ugly.push(map[x]); ratio = -1; jsonArray.push('"' + x + '":-1,'); } } ////////////////////////////////////////////////////////////////////////////////////////////////////////// // We now have a "Ratio" for our font either retrieved from the xml or measured using the fonts outline // // The ratio will either be 0 for a font who we couldn't measure // // or a positive number which we can multiply by the pointSize to calculate the thickness // // The fonts can then be allocated to one of the following arrays // // good: Not to thin // // bad: too thin // // ugly: fonts we couldn't measure // ////////////////////////////////////////////////////////////////////////////////////////////////////////// font = map[x].font; for (c in map[x].pointSizes) { pointSize = map[x].pointSizes[c]; thickness = ratio * pointSize; if (ratio > 0) { if (thickness >= minThickness) { good.push({ font: font, pointSize: pointSize, thickness: thickness }); } else { bad.push({ font: font, pointSize: pointSize, thickness: thickness }); } } else { ugly.push({ font: map[x].font }); break; } } } // end of for(x in map) jsonFile.write(jsonArray.join('\n') + '\n'); jsonFile.close(); // jsonFile.execute(); if (pageAndChar) { if (debug) { pageAndChar.textFrame.remove(); // Bye } else { pageAndChar.page.remove(); // Bye Bye } } result = { bad: bad, ugly: ugly, good: good, thickness: testThickness, thicknessPt: minThickness }; return result.toSource(); // the doScript can't pass an object properly so we stringify it }; // end of doScriptWrapper var thinnys; thinnys = app.doScript(doScriptWrapper, ScriptLanguage.JAVASCRIPT, undefined, UndoModes.ENTIRE_SCRIPT, 'Find Thinnys'); return eval(thinnys); // convert the string back into the object } // end of getThinnys highlight = function(thinnys, doBad, doGood) { if (!(thinnys.hasOwnProperty('good')) || !(doGood || doBad)) { return; } var doScriptWrapper; doScriptWrapper = function() { var good, bad, ugly, goodCon, thinnyCon, uglyCon, story, doc, l; story = app.selection[0] && app.selection[0].properties.baseline && app.selection[0].parentStory; if (!story) { return 'No Valid Selection'; } doc = app.activeDocument; app.findTextPreferences = app.changeTextPreferences = null; // Take your pick // app.changeTextPreferences.changeConditionsMode = ChangeConditionsModes.ADD_TO; app.changeTextPreferences.changeConditionsMode = ChangeConditionsModes.REPLACE_WITH; if (doGood) { good = thinnys.good; goodCon = doc.conditions.itemByName('Fatty'); if (!goodCon.isValid) { goodCon = doc.conditions.add({ name: 'Fatty', indicatorColor: [204, 255, 204], indicatorMethod: ConditionIndicatorMethod.USE_HIGHLIGHT }); } l = good.length; app.changeTextPreferences.appliedConditions = [goodCon]; while (l--) { app.findTextPreferences.appliedFont = good[l].font; app.findTextPreferences.pointSize = good[l].pointSize; story.changeText(); } } if (doBad !== false) { thinnyCon = doc.conditions.itemByName('Thinny'); if (!thinnyCon.isValid) { thinnyCon = doc.conditions.add({ name: 'Thinny', indicatorColor: [255, 255, 0], indicatorMethod: ConditionIndicatorMethod.USE_HIGHLIGHT }); } uglyCon = doc.conditions.itemByName('Couldn\'t measure me'); if (!uglyCon.isValid) { uglyCon = doc.conditions.add({ name: 'Couldn\'t measure me', indicatorColor: [255, 0, 0], indicatorMethod: ConditionIndicatorMethod.USE_HIGHLIGHT }); } bad = thinnys.bad; ugly = thinnys.ugly; l = bad.length; app.changeTextPreferences.appliedConditions = [thinnyCon]; while (l--) { app.findTextPreferences.pointSize = bad[l].pointSize; app.findTextPreferences.appliedFont = bad[l].font; story.changeText(); } l = ugly.length; app.changeTextPreferences.appliedConditions = [uglyCon]; app.findTextPreferences = null; while (l--) { app.findTextPreferences.appliedFont = ugly[l].font; story.changeText(); } } return 'Done'; }; // end of doScriptWrapper return app.doScript(doScriptWrapper, ScriptLanguage.JAVASCRIPT, undefined, UndoModes.ENTIRE_SCRIPT, 'Highlight Thinnys'); }; csvReport = function() { var csvFile, c, l, arr = []; if (thinnys.hasOwnProperty('good')) { csvFile = new File(Folder.temp + '/' + (+(new Date()) + ".csv")); csvFile.encoding = 'UTF-8'; csvFile.open('w'); csvFile.writeln('Font thickness report produce by Trevor http://www.creative-scripts.com ' + new Date()); csvFile.writeln('Conditions of use: 1) Use at your own peril. 2) All legal proceedings will be adjudicated by my dad.'); csvFile.writeln('Thickness tested for,,,' + thinnys.thickness); csvFile.writeln('Thickness tested for in points,,,' + thinnys.thicknessPt + ',,Font Thickness,Min Thickness'); csvFile.writeln('Status,Font Name,,,Font Size,In Points,In Points'); if (thinnys.bad.length) { l = thinnys.bad.length; for (c = 0; c < l; c++) { arr[c] = 'Thinny,"' + thinnys.bad[c].font.name + '",,,' + thinnys.bad[c].pointSize + ',' + thinnys.bad[c].thickness + ',' + thinnys.thicknessPt; } csvFile.writeln(arr.join('\n')); } if (thinnys.ugly.length) { arr.length = 0; l = thinnys.ugly.length; if (l) csvFile.writeln('Fonts that couldn\'t be measured'); for (c = 0; c < l; c++) { arr[c] = 'Couldn\'t measure,"' + thinnys.ugly[c].font.name + '"'; } csvFile.writeln(arr.join('\n')); } if (thinnys.good.length) { arr.length = 0; l = thinnys.good.length; if (l) csvFile.writeln('Fonts that were not too thin'); for (c = 0; c < l; c++) { arr[c] = 'Good,"' + thinnys.good[c].font.name + '",,,' + thinnys.good[c].pointSize + ',' + thinnys.good[c].thickness + ',' + thinnys.thicknessPt; } csvFile.writeln(arr.join('\n')); } csvFile.execute(); arr.length = 0; } }; goForIt = false; //////////// // THE UI // //////////// ui = new Window('dialog', 'Find The Thinnys'); ui.alignChildren = 'left'; ui.site = ui.add('button', undefined, 'By Creative Scripts http://www.creative-scripts.com'); ui.site.onClick = function(){ if ($.os[0] === 'W'){ app.doScript('CreateObject("WScript.Shell").Run("http://www.creative-scripts.com")', ScriptLanguage.VISUAL_BASIC); } else { app.doScript('open location "http://www.creative-scripts.com"', ScriptLanguage.APPLESCRIPT_LANGUAGE); } }; ui.measureGRP = ui.add('group'); ui.measureGRP.add('staticText', undefined, 'Minimum font thickness'); ui.ThicknessET = ui.measureGRP.add('EditText', undefined, '0.01in'); ui.ThicknessET.characters = 15; ui.textGrp = ui.add('group'); ui.textCB = ui.textGrp.add('checkBox', undefined, 'Create a csv file report'); ui.textCB.value = true; ui.debugGRP = ui.add('group'); ui.debugCB = ui.debugGRP.add('checkBox', undefined, 'Show debug drawings (only works on 1st check of font)'); ui.condGRP = ui.add('group'); ui.badConCB = ui.condGRP.add('checkBox', undefined, 'Highlight failed texts'); ui.goodConCB = ui.condGRP.add('checkBox', undefined, 'Highlight passed texts'); // ui.badConCB.value = true; if (Folder(Folder.userData + '/Creative Scripts/Font Ratios').exists) { ui.fileGRP = ui.add('group'); ui.jsonBT = ui.fileGRP.add('button', undefined, 'Open json folder'); ui.jsonBT.onClick = function() { Folder(Folder.userData + '/Creative Scripts/Font Ratios').execute(); }; } ui.okGRP = ui.add('group'); ui.okBT = ui.okGRP.add('button', undefined, 'Go for it'); ui.okBT.onClick = function() { goForIt = true; ui.okBT.text = 'Please wait'; ui.close(); }; ui.cancelBT = ui.okGRP.add('button', undefined, 'Cancel'); ui.show(); ////////////////// // Do Something // ////////////////// if (goForIt) { //////////////////////////////////////////////////////////////////////////////////////////////////// // measure the fonts // // set to true if you want to see some pretty pictures // // Setting to true will slow down the script considerably // // the pretty pictures will only get draw if the fonts were not be previously measure by the user // // Alternatively you can delete the created json file. // //////////////////////////////////////////////////////////////////////////////////////////////////// thinnys = getThinnys(ui.ThicknessET.text, ui.debugCB.value); // make a csv file if (ui.textCB.value) { csvReport(); } // highlight the bad (too thin) and ugly (the ones we couldn't measure) // set to true to highlight also the good ones // you can then set the visibility of the good ones to false and see what's left // highlight(thinnys, ui.badConCB.value, ui.goodConCB.value); 'Done'; } else 'Canceled :-('; // jshint ignore: line