// ai2html is a script for Adobe Illustrator that converts your Illustrator document into html and css. // Copyright (c) 2011-2018 The New York Times Company // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this library except in compliance with the License. // You may obtain a copy of the License at // http://www.apache.org/licenses/LICENSE-2.0 // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // ===================================== // How to install ai2html // ===================================== // - Move the ai2html.js file into the Illustrator folder where scripts are located. // - For example, on Mac OS X running Adobe Illustrator CC 2014, the path would be: // Adobe Illustrator CC 2014/Presets/en_US/Scripts/ai2html.jsx // ===================================== // How to use ai2html // ===================================== // - Create your Illustrator artwork. // - Size the artboard to the dimensions that you want the div to appear on the web page. // - Make sure your Document Color Mode is set to RGB. // - Use Arial or Georgia unless you have added your own fonts to the fonts array in the script. // - Run the script by choosing: File > Scripts > ai2html // - Go to the folder containing your Illustrator file. Inside will be a folder called ai2html-output. // - Open the html files in your browser to preview your output. function main() { // Enclosing scripts in a named function (and not an anonymous, self-executing // function) has been recommended as a way to minimise intermittent "MRAP" errors. // (This advice may be superstitious, need more evidence to decide.) // See (for example) https://forums.adobe.com/thread/1810764 and // http://wwwimages.adobe.com/content/dam/Adobe/en/devnet/pdf/illustrator/scripting/Readme.txt // How to update the version number: // - Increment middle digit for new functionality or breaking changes // or increment final digit for simple bug fixes or other minor changes. // - Update the version number in package.json // - Add an entry to CHANGELOG.md // - Run 'npm publish' to create a new GitHub release var scriptVersion = '0.123.4'; // ================================================ // ai2html and config settings // ================================================ // These are base settings that are overridden by text block settings in // the .ai document and settings contained in ai2html-config.json files var defaultSettings = { "namespace": "g-", "settings_version": scriptVersion, "create_promo_image": false, "promo_image_width": 1024, "image_format": ["auto"], // Options: auto, png, png24, jpg, svg "write_image_files": true, "responsiveness": "fixed", // Options: fixed, dynamic "text_responsiveness": "dynamic", // Options: fixed, dynamic "max_width": "", "output": "one-file", // Options: one-file, multiple-files "project_name": "", // Defaults to the name of the AI file "html_output_path": "ai2html-output/", "html_output_extension": ".html", "image_output_path": "ai2html-output/", "image_source_path": "", "image_alt_text": "", // Generally, use alt_text instead "cache_bust_token": null, // Append a token to the url of image urls: ?v= "create_config_file": false, "create_settings_block": true, // Create a text block in the AI doc with common settings "config_file_path": "", "local_preview_template": "", "png_transparent": false, "png_number_of_colors": 128, // Number of colors in 8-bit PNG image (1-256) "jpg_quality": 60, "center_html_output": true, "use_2x_images_if_possible": true, "use_lazy_loader": true, "include_resizer_widths": true, "include_resizer_css": true, // container-query resizing "include_resizer_script": false, "inline_svg": false, // Embed background image SVG in HTML instead of loading a file "svg_id_prefix": "", // Prefix SVG ids with a string to disambiguate from other ids on the page "svg_embed_images": false, "render_text_as": "html", // Options: html, image "render_rotated_skewed_text_as": "html", // Options: html, image "testing_mode": false, // Render text in both bg image and HTML to test HTML text placement "show_completion_dialog_box": true, "clickable_link": "", // Add a URL to make the entire graphic a clickable link "last_updated_text": "", "headline": "", "leadin": "", "summary": "", "notes": "", "sources": "", "credit": "", // List of settings to include in the "ai2html-settings" text block "settings_block": [ "settings_version", "image_format", "responsiveness", "include_resizer_css", // "use_lazy_loader", "output", "html_output_path", // "html_output_extension", // removed from settings block in v0.115.6 "image_output_path", "image_source_path", "local_preview_template", "png_number_of_colors", "jpg_quality", "headline", "leadin", "notes", "sources", "credit" ], // list of settings to include in the config.yml file "config_file": [ "headline", "leadin", "summary", "notes", "sources", "credit" ] }; // These settings override the default settings in NYT preview/birdkit projects var nytOverrideSettings = { "image_source_path": "_assets/", // path for "include_resizer_script": false, "include_resizer_css": true, "min_width": 280, // added as workaround for a scoop bug affecting ai2html-type graphics "accessibility": true, "settings_block": [ "settings_version", "responsiveness", "include_resizer_css", // adding to text block until we make it the default "image_format", // "write_image_files", // "max_width", "png_transparent", "png_number_of_colors", "jpg_quality", "inline_svg", "output" // "clickable_link" // "use_lazy_loader" ], "config_file": [] }; var nytPreviewSettings = { "project_type": "nyt-preview", "html_output_path": "../src/", "image_output_path": "../public/_assets/" }; var nytBirdkitSettings = { "project_type": "freebird", "html_output_path": "../src/lib/graphics/", "image_output_path": "../public/_assets/" }; var nytBirdkitEmbedSettings = { "project_type": "ai2html", "html_output_path": "../public/", "image_output_path": "../public/_assets/", "dark_mode_compatible": false, "create_json_config_files": true, "create_promo_image": false, "credit": "The New York Times", "aria_role": "img", "alt_text": "", "page_template": "vi-article-embed", "display_for_promotion_only": false, "section": "", "size": "full", // changed from "medium" to "full" "settings_block": [ "settings_version", "responsiveness", "alt_text", "max_width", "image_format", "png_number_of_colors", "jpg_quality", "last_updated_text", "section", "headline", "leadin", "summary", "notes", "sources", "credit", "display_for_promotion_only", "dark_mode_compatible", "size" ], "config_file": [ "last_updated_text", "alt_text", "section", "headline", "leadin", "summary", "notes", "sources", "credit", "page_template", "display_for_promotion_only", "size" ] }; // Override settings for simple NYT Preview ai2html embed graphics var nytPreviewEmbedSettings = { "project_type": "ai2html", "html_output_path": "../public/", "image_output_path": "../public/_assets/", "dark_mode_compatible": false, "create_config_file": true, "config_file_path": "../config.yml", "create_promo_image": true, "credit": "The New York Times", "aria_role": "img", "alt_text": "", "publish_system": "scoop", "page_template": "vi-article-embed", "environment": "production", "show_in_compatible_apps": true, "display_for_promotion_only": false, "constrain_width_to_text_column": false, "compatibility": "inline", "size": "full", // changed from "medium" to "full" "scoop_publish_fields": true, "scoop_asset_id": "", "scoop_username": "", "scoop_slug": "", "scoop_external_edit_key": "", "settings_block": [ "settings_version", "responsiveness", "alt_text", "max_width", "image_format", // "write_image_files", // "output", "png_number_of_colors", "jpg_quality", // "use_lazy_loader", // "show_completion_dialog_box", "last_updated_text", "headline", "leadin", "summary", "notes", "sources", "credit", "show_in_compatible_apps", "display_for_promotion_only", "constrain_width_to_text_column", "dark_mode_compatible", "size", "scoop_asset_id", "scoop_username", "scoop_slug", "scoop_external_edit_key" ], "config_file": [ "last_updated_text", "headline", "leadin", "summary", "notes", "sources", "credit", "page_template", "publish_system", "environment", "show_in_compatible_apps", "display_for_promotion_only", "constrain_width_to_text_column", "compatibility", "size", "scoop_publish_fields", "scoop_asset_id", "scoop_username", "scoop_slug", "scoop_external_edit_key" ] }; // Rules for converting AI fonts to CSS // vshift shifts text vertically, to compensate for vertical misalignment caused // by a difference between vertical placement in Illustrator (of a system font) and // browsers (of the web font equivalent). vshift values are percentage of font size. Positive // values correspond to a downward shift. var fonts = [ {"aifont":"ArialMT","family":"arial,helvetica,sans-serif","weight":"","style":""}, {"aifont":"Arial-BoldMT","family":"arial,helvetica,sans-serif","weight":"bold","style":""}, {"aifont":"Arial-ItalicMT","family":"arial,helvetica,sans-serif","weight":"","style":"italic"}, {"aifont":"Arial-BoldItalicMT","family":"arial,helvetica,sans-serif","weight":"bold","style":"italic"}, {"aifont":"Georgia","family":"georgia,'times new roman',times,serif","weight":"","style":""}, {"aifont":"Georgia-Bold","family":"georgia,'times new roman',times,serif","weight":"bold","style":""}, {"aifont":"Georgia-Italic","family":"georgia,'times new roman',times,serif","weight":"","style":"italic"}, {"aifont":"Georgia-BoldItalic","family":"georgia,'times new roman',times,serif","weight":"bold","style":"italic"}, // NYT fonts {"aifont":"NYTFranklin-Light","family":"nyt-franklin,arial,helvetica,sans-serif","weight":"300","style":"", "vshift": "8%"}, {"aifont":"NYTFranklin-Medium","family":"nyt-franklin,arial,helvetica,sans-serif","weight":"500","style":"", "vshift": "8%"}, {"aifont":"NYTFranklin-SemiBold","family":"nyt-franklin,arial,helvetica,sans-serif","weight":"600","style":"", "vshift": "8%"}, {"aifont":"NYTFranklin-Semibold","family":"nyt-franklin,arial,helvetica,sans-serif","weight":"600","style":"", "vshift": "8%"}, {"aifont":"NYTFranklinSemiBold-Regular","family":"nyt-franklin,arial,helvetica,sans-serif","weight":"600","style":"", "vshift": "8%"}, {"aifont":"NYTFranklin-SemiboldItalic","family":"nyt-franklin,arial,helvetica,sans-serif","weight":"600","style":"italic", "vshift": "8%"}, {"aifont":"NYTFranklin-Bold","family":"nyt-franklin,arial,helvetica,sans-serif","weight":"700","style":"", "vshift": "8%"}, {"aifont":"NYTFranklin-LightItalic","family":"nyt-franklin,arial,helvetica,sans-serif","weight":"300","style":"italic", "vshift": "8%"}, {"aifont":"NYTFranklin-MediumItalic","family":"nyt-franklin,arial,helvetica,sans-serif","weight":"500","style":"italic", "vshift": "8%"}, {"aifont":"NYTFranklin-BoldItalic","family":"nyt-franklin,arial,helvetica,sans-serif","weight":"700","style":"italic", "vshift": "8%"}, {"aifont":"NYTFranklin-ExtraBold","family":"nyt-franklin,arial,helvetica,sans-serif","weight":"800","style":"", "vshift": "8%"}, {"aifont":"NYTFranklin-ExtraBoldItalic","family":"nyt-franklin,arial,helvetica,sans-serif","weight":"800","style":"italic", "vshift": "8%"}, {"aifont":"NYTFranklin-Headline","family":"nyt-franklin,arial,helvetica,sans-serif","weight":"bold","style":"", "vshift": "8%"}, {"aifont":"NYTFranklin-HeadlineItalic","family":"nyt-franklin,arial,helvetica,sans-serif","weight":"bold","style":"italic", "vshift": "8%"}, // Chelt. {"aifont":"NYTCheltenham-ExtraLight","family":"nyt-cheltenham,georgia,serif","weight":"200","style":""}, {"aifont":"NYTCheltenhamExtLt-Regular","family":"nyt-cheltenham,georgia,serif","weight":"200","style":""}, {"aifont":"NYTCheltenham-Light","family":"nyt-cheltenham,georgia,serif","weight":"300","style":""}, {"aifont":"NYTCheltenhamLt-Regular","family":"nyt-cheltenham,georgia,serif","weight":"300","style":""}, {"aifont":"NYTCheltenham-LightSC","family":"nyt-cheltenham,georgia,serif","weight":"300","style":""}, {"aifont":"NYTCheltenham-Book","family":"nyt-cheltenham,georgia,serif","weight":"400","style":""}, {"aifont":"NYTCheltenhamBook-Regular","family":"nyt-cheltenham,georgia,serif","weight":"400","style":""}, {"aifont":"NYTCheltenham-Wide","family":"nyt-cheltenham,georgia,serif","weight":"","style":""}, {"aifont":"NYTCheltenhamMedium-Regular","family":"nyt-cheltenham,georgia,serif","weight":"500","style":""}, {"aifont":"NYTCheltenham-Medium","family":"nyt-cheltenham,georgia,serif","weight":"500","style":""}, {"aifont":"NYTCheltenham-Bold","family":"nyt-cheltenham,georgia,serif","weight":"700","style":""}, {"aifont":"NYTCheltenham-BoldCond","family":"nyt-cheltenham,georgia,serif","weight":"bold","style":""}, {"aifont":"NYTCheltenhamCond-BoldXC","family":"nyt-cheltenham-extra-cn-bd,georgia,serif","weight":"bold","style":""}, {"aifont":"NYTCheltenham-BoldExtraCond","family":"nyt-cheltenham,georgia,serif","weight":"bold","style":""}, {"aifont":"NYTCheltenham-ExtraBold","family":"nyt-cheltenham,georgia,serif","weight":"bold","style":""}, {"aifont":"NYTCheltenham-ExtraLightIt","family":"nyt-cheltenham,georgia,serif","weight":"","style":"italic"}, {"aifont":"NYTCheltenham-ExtraLightItal","family":"nyt-cheltenham,georgia,serif","weight":"","style":"italic"}, {"aifont":"NYTCheltenham-LightItalic","family":"nyt-cheltenham,georgia,serif","weight":"","style":"italic"}, {"aifont":"NYTCheltenham-BookItalic","family":"nyt-cheltenham,georgia,serif","weight":"","style":"italic"}, {"aifont":"NYTCheltenham-WideItalic","family":"nyt-cheltenham,georgia,serif","weight":"","style":"italic"}, {"aifont":"NYTCheltenham-MediumItalic","family":"nyt-cheltenham,georgia,serif","weight":"","style":"italic"}, {"aifont":"NYTCheltenham-BoldItalic","family":"nyt-cheltenham,georgia,serif","weight":"700","style":"italic"}, {"aifont":"NYTCheltenham-ExtraBoldItal","family":"nyt-cheltenham,georgia,serif","weight":"bold","style":"italic"}, {"aifont":"NYTCheltenham-ExtraBoldItalic","family":"nyt-cheltenham,georgia,serif","weight":"bold","style":"italic"}, {"aifont":"NYTCheltenhamSH-Regular","family":"nyt-cheltenham-sh,nyt-cheltenham,georgia,serif","weight":"400","style":""}, {"aifont":"NYTCheltenhamSH-Italic","family":"nyt-cheltenham-sh,nyt-cheltenham,georgia,serif","weight":"400","style":"italic"}, {"aifont":"NYTCheltenhamSH-Bold","family":"nyt-cheltenham-sh,nyt-cheltenham,georgia,serif","weight":"700","style":""}, {"aifont":"NYTCheltenhamSH-BoldItalic","family":"nyt-cheltenham-sh,nyt-cheltenham,georgia,serif","weight":"700","style":"italic"}, {"aifont":"NYTCheltenhamWide-Regular","family":"nyt-cheltenham,georgia,serif","weight":"500","style":""}, {"aifont":"NYTCheltenhamWide-Italic","family":"nyt-cheltenham,georgia,serif","weight":"500","style":"italic"}, // Imperial {"aifont":"NYTImperial-Regular","family":"nyt-imperial,georgia,serif","weight":"400","style":""}, {"aifont":"NYTImperial-Italic","family":"nyt-imperial,georgia,serif","weight":"400","style":"italic"}, {"aifont":"NYTImperial-Semibold","family":"nyt-imperial,georgia,serif","weight":"600","style":""}, {"aifont":"NYTImperial-SemiboldItalic","family":"nyt-imperial,georgia,serif","weight":"600","style":"italic"}, {"aifont":"NYTImperial-Bold","family":"nyt-imperial,georgia,serif","weight":"700","style":""}, {"aifont":"NYTImperial-BoldItalic","family":"nyt-imperial,georgia,serif","weight":"700","style":"italic"}, // Others {"aifont":"NYTKarnakText-Regular","family":"nyt-karnak-display-130124,georgia,serif","weight":"400","style":""}, {"aifont":"NYTKarnakDisplay-Regular","family":"nyt-karnak-display-130124,georgia,serif","weight":"400","style":""}, {"aifont":"NYTStymieLight-Regular","family":"nyt-stymie,arial,helvetica,sans-serif","weight":"300","style":""}, {"aifont":"NYTStymieMedium-Regular","family":"nyt-stymie,arial,helvetica,sans-serif","weight":"500","style":""}, {"aifont":"StymieNYT-Light","family":"nyt-stymie,arial,helvetica,sans-serif","weight":"300","style":""}, {"aifont":"StymieNYT-LightPhoenetic","family":"nyt-stymie,arial,helvetica,sans-serif","weight":"300","style":""}, {"aifont":"StymieNYT-Lightitalic","family":"nyt-stymie,arial,helvetica,sans-serif","weight":"300","style":"italic"}, {"aifont":"StymieNYT-Medium","family":"nyt-stymie,arial,helvetica,sans-serif","weight":"500","style":""}, {"aifont":"StymieNYT-MediumItalic","family":"nyt-stymie,arial,helvetica,sans-serif","weight":"500","style":"italic"}, {"aifont":"StymieNYT-Bold","family":"nyt-stymie,arial,helvetica,sans-serif","weight":"700","style":""}, {"aifont":"StymieNYT-BoldItalic","family":"nyt-stymie,arial,helvetica,sans-serif","weight":"700","style":"italic"}, {"aifont":"StymieNYT-ExtraBold","family":"nyt-stymie,arial,helvetica,sans-serif","weight":"700","style":""}, {"aifont":"StymieNYT-ExtraBoldText","family":"nyt-stymie,arial,helvetica,sans-serif","weight":"700","style":""}, {"aifont":"StymieNYT-ExtraBoldTextItal","family":"nyt-stymie,arial,helvetica,sans-serif","weight":"700","style":"italic"}, {"aifont":"StymieNYTBlack-Regular","family":"nyt-stymie,arial,helvetica,sans-serif","weight":"700","style":""}, {"aifont":"StymieBT-ExtraBold","family":"nyt-stymie,arial,helvetica,sans-serif","weight":"700","style":""}, {"aifont":"Stymie-Thin","family":"nyt-stymie,arial,helvetica,sans-serif","weight":"300","style":""}, {"aifont":"Stymie-UltraLight","family":"nyt-stymie,arial,helvetica,sans-serif","weight":"300","style":""}, {"aifont":"NYTMagSans-Regular","family":"'nyt-magsans',arial,helvetica,sans-serif","weight":"500","style":""}, {"aifont":"NYTMagSans-Bold","family":"'nyt-magsans',arial,helvetica,sans-serif","weight":"700","style":""} ]; // ================================================ // Constant data // ================================================ // html entity substitution var basicCharacterReplacements = [["\x26","&"], ["\x22","""], ["\x3C","<"], ["\x3E",">"]]; var extraCharacterReplacements = [["\xA0"," "], ["\xA1","¡"], ["\xA2","¢"], ["\xA3","£"], ["\xA4","¤"], ["\xA5","¥"], ["\xA6","¦"], ["\xA7","§"], ["\xA8","¨"], ["\xA9","©"], ["\xAA","ª"], ["\xAB","«"], ["\xAC","¬"], ["\xAD","­"], ["\xAE","®"], ["\xAF","¯"], ["\xB0","°"], ["\xB1","±"], ["\xB2","²"], ["\xB3","³"], ["\xB4","´"], ["\xB5","µ"], ["\xB6","¶"], ["\xB7","·"], ["\xB8","¸"], ["\xB9","¹"], ["\xBA","º"], ["\xBB","»"], ["\xBC","¼"], ["\xBD","½"], ["\xBE","¾"], ["\xBF","¿"], ["\xD7","×"], ["\xF7","÷"], ["\u0192","ƒ"], ["\u02C6","ˆ"], ["\u02DC","˜"], ["\u2002"," "], ["\u2003"," "], ["\u2009"," "], ["\u200C","‌"], ["\u200D","‍"], ["\u200E","‎"], ["\u200F","‏"], ["\u2013","–"], ["\u2014","—"], ["\u2018","‘"], ["\u2019","’"], ["\u201A","‚"], ["\u201C","“"], ["\u201D","”"], ["\u201E","„"], ["\u2020","†"], ["\u2021","‡"], ["\u2022","•"], ["\u2026","…"], ["\u2030","‰"], ["\u2032","′"], ["\u2033","″"], ["\u2039","‹"], ["\u203A","›"], ["\u203E","‾"], ["\u2044","⁄"], ["\u20AC","€"], ["\u2111","ℑ"], ["\u2113",""], ["\u2116",""], ["\u2118","℘"], ["\u211C","ℜ"], ["\u2122","™"], ["\u2135","ℵ"], ["\u2190","←"], ["\u2191","↑"], ["\u2192","→"], ["\u2193","↓"], ["\u2194","↔"], ["\u21B5","↵"], ["\u21D0","⇐"], ["\u21D1","⇑"], ["\u21D2","⇒"], ["\u21D3","⇓"], ["\u21D4","⇔"], ["\u2200","∀"], ["\u2202","∂"], ["\u2203","∃"], ["\u2205","∅"], ["\u2207","∇"], ["\u2208","∈"], ["\u2209","∉"], ["\u220B","∋"], ["\u220F","∏"], ["\u2211","∑"], ["\u2212","−"], ["\u2217","∗"], ["\u221A","√"], ["\u221D","∝"], ["\u221E","∞"], ["\u2220","∠"], ["\u2227","∧"], ["\u2228","∨"], ["\u2229","∩"], ["\u222A","∪"], ["\u222B","∫"], ["\u2234","∴"], ["\u223C","∼"], ["\u2245","≅"], ["\u2248","≈"], ["\u2260","≠"], ["\u2261","≡"], ["\u2264","≤"], ["\u2265","≥"], ["\u2282","⊂"], ["\u2283","⊃"], ["\u2284","⊄"], ["\u2286","⊆"], ["\u2287","⊇"], ["\u2295","⊕"], ["\u2297","⊗"], ["\u22A5","⊥"], ["\u22C5","⋅"], ["\u2308","⌈"], ["\u2309","⌉"], ["\u230A","⌊"], ["\u230B","⌋"], ["\u2329","⟨"], ["\u232A","⟩"], ["\u25CA","◊"], ["\u2660","♠"], ["\u2663","♣"], ["\u2665","♥"], ["\u2666","♦"]]; // CSS text-transform equivalents var caps = [ {"ai":"FontCapsOption.NORMALCAPS","html":"none"}, {"ai":"FontCapsOption.ALLCAPS","html":"uppercase"}, {"ai":"FontCapsOption.SMALLCAPS","html":"uppercase"} ]; // CSS text-align equivalents var align = [ {"ai":"Justification.LEFT","html":"left"}, {"ai":"Justification.RIGHT","html":"right"}, {"ai":"Justification.CENTER","html":"center"}, {"ai":"Justification.FULLJUSTIFY","html":"justify"}, {"ai":"Justification.FULLJUSTIFYLASTLINELEFT","html":"justify"}, {"ai":"Justification.FULLJUSTIFYLASTLINECENTER","html":"justify"}, {"ai":"Justification.FULLJUSTIFYLASTLINERIGHT","html":"justify"} ]; var blendModes = [ {ai: "BlendModes.MULTIPLY", html: "multiply"} ]; // list of CSS properties used for translating AI text styles // (used for creating a unique identifier for each style) var cssTextStyleProperties = [ //'top' // used with vshift; not independent of other properties 'position', 'font-family', 'font-size', 'font-weight', 'font-style', 'color', 'line-height', 'height', // used for point-type paragraph styles 'letter-spacing', 'opacity', 'padding-top', 'padding-bottom', 'text-align', 'text-transform', 'mix-blend-mode', 'vertical-align' // for superscript ]; var cssPrecision = 4; // ================================ // Global variable declarations // ================================ // This can be overridden by settings var nameSpace = 'g-'; // vars to hold warnings and informational messages at the end var feedback = []; var warnings = []; var errors = []; var oneTimeWarnings = []; var startTime = +new Date(); var textFramesToUnhide = []; var objectsToRelock = []; var docSettings; var textBlockData; var doc, docPath, docSlug, docIsSaved; var progressBar; var JSON; initJSON(); // Simple interface to help find performance bottlenecks. Usage: // T.start('