diff --git a/README.md b/README.md index ba9ee6d8d..5b104e31c 100644 --- a/README.md +++ b/README.md @@ -233,7 +233,7 @@ slide.addText( | `align` | string | | `left` | alignment | `left` or `center` or `right` | | `autoFit` | boolean | | `false` | "Fit to Shape" | `true` or `false` | | `bold` | boolean | | `false` | bold text | `true` or `false` | -| `breakLine` | boolean | | `false` | adds a line break | `true` or `false` (only applies when used in text object options) Ex: `{text:'hi', options:{breakLine:true}}` | +| `breakLine` | boolean | | `false` | appends a line break | `true` or `false` (only applies when used in text object options) Ex: `{text:'hi', options:{breakLine:true}}` | | `bullet` | boolean | | `false` | bulleted text | `true` or `false` | | `color` | string | | | text color | hex color code. Ex: `{ color:'0088CC' }` | | `fill` | string | | | fill/bkgd color | hex color code. Ex: `{ color:'0088CC' }` | @@ -320,16 +320,23 @@ slide.addTable( [rows], {any Layout/Formatting OPTIONS} ); | `rowH` | array | inches | | row heights in order | Ex: Height for each of 5 rows `[1.0, 2.0, 2.5, 1.5, 1.0]` | ### Table Auto-Paging Options -| Option | Type | Unit | Default | Description | Possible Values | -| :----------- | :------ | :----- | :-------- | :--------------------- | :--------------------------------------- | -| `autoPage` | boolean | | `true` | auto-page table | `true` or `false` | -| `lineWeight` | float | | 0 | line weight | -1.0 to 1.0. Ex: {lineWeight:0.5} | +| Option | Type | Default | Description | Possible Values | +| :-------------- | :------ | :-------- | :--------------------- | :--------------------------------------- | +| `autoPage` | boolean | `true` | auto-page table | `true` or `false` | +| `lineWeight` | float | 0 | line weight value | -1.0 to 1.0. Ex: `{lineWeight:0.5}` | +| `newPageStartY` | object | | starting `y` value for tables on new Slides | 0-n OR 'n%'. Ex:`{newPageStartY:0.5}` | ### Table Auto-Paging Notes -* Table will auto-page by default (as table rows overflow the Slide, new Slides will be added as needed) -* Use `lineWeight` to adjust the calculated value of lines. If too much space is left under each table, -then add a higher lineWeight value. Conversely, if the tables are overflowing the bottom of the Slides, -reduce the lineWeight value. +Tables will auto-page by default and the table on new Slides will use the current Slide's top `margin` value as the starting point for `y`. +Tables will retain their existing `x`, `w`, and `colW` values as they are continued onto subsequent Slides. + +* `autoPage`: allows the auto-paging functionality (as table rows overflow the Slide, new Slides will be added) to be disabled. +* `lineWeight`: adjusts the calculated height of lines. If too much empty space is left under each table, +then increase lineWeight value. Conversely, if the tables are overflowing the bottom of the Slides, then +reduce the lineWeight value. Also helpful when using some fonts that do not have the usual golden ratio. +* `newPageStartY`: provides the ability to specify where new tables will be placed on new Slides. For example, +you may place a table halfway down a Slide, but you wouldn't that to be the starting location for subsequent +tables. Use this option to ensure there is no wasted space and to guarantee a professional look. ### Table Formatting Options | Option | Type | Unit | Default | Description | Possible Values | diff --git a/dist/pptxgen.js b/dist/pptxgen.js index 2f2d76cb7..1bd792909 100644 --- a/dist/pptxgen.js +++ b/dist/pptxgen.js @@ -62,7 +62,7 @@ if ( NODEJS ) { var PptxGenJS = function(){ // CONSTANTS var APP_VER = "1.2.1"; - var APP_REL = "20170221"; + var APP_REL = "20170226"; var LAYOUTS = { 'LAYOUT_4x3' : { name: 'screen4x3', width: 9144000, height: 6858000 }, 'LAYOUT_16x9' : { name: 'screen16x9', width: 9144000, height: 5143500 }, @@ -81,6 +81,7 @@ var PptxGenJS = function(){ var ONEPT = 12700; // One (1) point (pt) var CRLF = '\r\n'; var DEF_FONT_SIZE = 12; + var DEF_SLIDE_MARGIN_IN = [0.5, 0.5, 0.5, 0.5]; // TRBL-style // A: Create internal pptx object var gObjPptx = {}; @@ -398,9 +399,9 @@ var PptxGenJS = function(){ * Magic happens here */ function getSlidesForTableRows(inArrRows, opts) { - var LINEH_MODIFIER = 1.8; + var LINEH_MODIFIER = 1.9; var opts = opts || {}; - var arrInchMargins = [0.5, 0.5, 0.5, 0.5]; // TRBL-style + var arrInchMargins = DEF_SLIDE_MARGIN_IN; // (0.5" on all sides) var arrObjTabHeadRows = [], arrObjTabBodyRows = [], arrObjTabFootRows = []; var arrObjSlides = [], arrRows = [], currRow = []; var intTabW = 0, emuTabCurrH = 0; @@ -412,8 +413,8 @@ var PptxGenJS = function(){ if (opts.debug) console.log('opts.colW .......... = '+ (opts.colW||'').toString()); if (opts.debug) console.log('opts.slideMargin ... = '+ (opts.slideMargin||'').toString()); - // NOTE: Use default size of 1 as zero cell margin is causing our tables to be too large and touch bottom of slide! - if ( !opts.slideMargin && opts.slideMargin != 0 ) opts.slideMargin = 1; + // NOTE: Use default size as zero cell margin is causing our tables to be too large and touch bottom of slide! + if ( !opts.slideMargin && opts.slideMargin != 0 ) opts.slideMargin = DEF_SLIDE_MARGIN_IN[0]; // STEP 1: Calc margins/usable space if ( opts.slideMargin || opts.slideMargin == 0 ) { @@ -442,13 +443,10 @@ var PptxGenJS = function(){ else { opts.w = opts.colW * numCols } } - // STEP 2: Set table size now that we have usable space calc'd + // STEP 2: Calc usable space/table size now that we have usable space calc'd emuSlideTabW = ( opts.w ? inch2Emu(opts.w) : (gObjPptx.pptLayout.width - inch2Emu((opts.x || arrInchMargins[1]) + arrInchMargins[3])) ); - emuSlideTabH = ( opts.h ? inch2Emu(opts.h) : (gObjPptx.pptLayout.height - inch2Emu((opts.y || arrInchMargins[0]) + arrInchMargins[2])) ); - - if (opts.debug) console.log('gObjPptx.pptLayout.height (in) = '+ (gObjPptx.pptLayout.height/EMU) ); if (opts.debug) console.log('emuSlideTabW (in) ............ = '+ (emuSlideTabW/EMU).toFixed(1) ); - if (opts.debug) console.log('emuSlideTabH (in) ............ = '+ (emuSlideTabH/EMU).toFixed(1) ); + //if (opts.debug) console.log('emuSlideTabH (in) ............ = '+ (emuSlideTabH/EMU).toFixed(1) ); // STEP 3: Calc column widths if needed so we can subsequently calc lines (we need `emuSlideTabW`!) if ( !opts.colW || !Array.isArray(opts.colW) ) { @@ -467,13 +465,25 @@ var PptxGenJS = function(){ if (opts.debug) console.log('opts.colW..... = '+ opts.colW.toString()); - // STEP 4: Iterate over each line and perform magic - // ROBUST: inArrRows will be an array of {text:'', opts{}} whether from `addSlidesForTable()` or `.addTable()` + // STEP 4: Iterate over each line and perform magic ========================= + // NOTE: inArrRows will be an array of {text:'', opts{}} whether from `addSlidesForTable()` or `.addTable()` inArrRows.forEach(function(row,iRow){ // A: Reset ROW variables var arrCellsLines = [], arrCellsLineHeights = [], emuRowH = 0, intMaxLineCnt = 0, intMaxColIdx = 0; - // B: Parse and store each cell's text into line array (*MAGIC HAPPENS HERE*) + // B: Calc usable vertical space/table height + // NOTE: Use margins after the first Slide (dont re-use opt.y - it could've been halfway down the page!) (ISSUE#43,ISSUE#47,ISSUE#48) + if ( arrObjSlides.length > 0 ) { + emuSlideTabH = ( gObjPptx.pptLayout.height - inch2Emu( (opts.y/EMU < arrInchMargins[0] ? opts.y/EMU : arrInchMargins[0]) + arrInchMargins[2]) ); + // Use whichever is greater: area between margins or the table H provided (dont shrink usable area - the whole point of over-riding X on paging is to *increarse* usable space) + if ( emuSlideTabH < opts.h ) emuSlideTabH = opts.h; + } + else emuSlideTabH = ( opts.h ? opts.h : (gObjPptx.pptLayout.height - inch2Emu((opts.y/EMU || arrInchMargins[0]) + arrInchMargins[2])) ); + if (opts.debug) console.log('arrObjSlides.length ............ = '+ (arrObjSlides.length)); + if (opts.debug) console.log('gObjPptx.pptLayout.height (in).. = '+ (gObjPptx.pptLayout.height/EMU)); + if (opts.debug) console.log('emuSlideTabH (in) .............. = '+ (emuSlideTabH/EMU).toFixed(1)); + + // C: Parse and store each cell's text into line array (*MAGIC HAPPENS HERE*) row.forEach(function(cell,iCell){ // DESIGN: Cells are henceforth {objects} with `text` and `opts` @@ -508,10 +518,10 @@ var PptxGenJS = function(){ if ( Array.isArray(cell.opts.margin) && cell.opts.margin[0] ) lineHeight += cell.opts.margin[0] / intMaxLineCnt; if ( Array.isArray(cell.opts.margin) && cell.opts.margin[2] ) lineHeight += cell.opts.margin[2] / intMaxLineCnt; arrCellsLineHeights.push( Math.round(lineHeight) ); - if (opts.debug) console.log('lineHeight (in)....: '+ (lineHeight/EMU) + ' ... (intMaxLineCnt = '+intMaxLineCnt+')'); + if (opts.debug) console.log('lineHeight (in)....: '+ (lineHeight/EMU).toFixed(3) + ' ... (intMaxLineCnt = '+intMaxLineCnt+')'); }); - // C: AUTO-PAGING: Add text one-line-a-time to this row's cells until: lines are exhausted OR table H limit is hit + // D: AUTO-PAGING: Add text one-line-a-time to this row's cells until: lines are exhausted OR table H limit is hit for (var idx=0; idx + + + + + + + bullet 1 + + + + + + colored text + + + + * NOTES: + * - Lines are createing using

-aragraph's + * - Bullets are a paragprah-level formatting device + */ + function genXmlTextBody(slideObj) { + var strSlideXml = ''; + var bulletParaPropXml = ''; + var paragraphPropXml = ' 0 ) paragraphPropXml += ' lvl="' + slideObj.options.indentLevel + '"'; + + // OPTION: bullet + if ( slideObj.options.bullet ) paragraphPropXml += ' marL="228600" indent="-228600"'; + + // Close Paragraph Properties =========================== + paragraphPropXml += (slideObj.options.bullet ? '>' : '>'); + paragraphPropXml += ''; + } + + // STEP 2: Build paragraph(s) and its text props/runs + if ( slideObj.options.bullet + && typeof slideObj.text == 'string' && slideObj.text.split('\n').length > 0 + && slideObj.text.split('\n')[1] && slideObj.text.split('\n')[1].length > 0 + ) { + strSlideXml += genXmlBodyProperties(slideObj.options) + ''; + // IMPORTANT: Split text here (even though `genXmlTextRun` handles \n) b/c we need `outStyles` for bullets to build correctly + slideObj.text.toString().split('\n').forEach(function(line,idx){ + if ( idx > 0 ) strSlideXml += ''; + strSlideXml += '' + paragraphPropXml; + strSlideXml += genXmlTextRun(slideObj.options, line); + }); + } + else if ( typeof slideObj.text == 'string' || typeof slideObj.text == 'number' ) { + strSlideXml += genXmlBodyProperties(slideObj.options) + '' + paragraphPropXml; + strSlideXml += genXmlTextRun(slideObj.options, slideObj.text.toString()); + } + else if ( slideObj.text && Array.isArray(slideObj.text) ) { + // DESC: This is an array of text objects: [{},{}] + // EX: slide.addText([ {text:'hello', options:{color:'0088CC'}}, {text:'world', options:{color:'CC8800'}} ]); + strSlideXml += genXmlBodyProperties(slideObj.options) + ''; + slideObj.text.forEach(function(textObj,idx){ + textObj.options = textObj.options || {}; + textObj.options.lineIdx = idx; + + // Add a line break if prev line was bulleted and this one isnt, otherwise, this new line will continue on prev bullet line and users probably dont want that! + if ( idx > 0 && slideObj.text[idx-1].options.bullet && !textObj.options.bullet ) strSlideXml += ''; + + // Support bullets in text objects (create a paragraph for each {text} ojbect with bullet:true) + if ( textObj.options.bullet ) { + if ( idx > 0 ) strSlideXml += ''; + strSlideXml += '' + bulletParaPropXml; + } + else if ( idx == 0) { + strSlideXml += '' + paragraphPropXml; + } + + strSlideXml += genXmlTextRun((textObj.options || slideObj.options), textObj.text.toString()); + }); + } + + // STEP 3: Close paragraphProperties and the current open paragraph + if ( slideObj.text ) { + strSlideXml += ''; + strSlideXml += ''; + } + + // STEP 4: Close the textBody + strSlideXml += ''; + + // NOTE: If there was no text to format return nothing (or Shape wont render!) + return ( strSlideXml == '' ? '' : strSlideXml ); + } + + /** + + + + + + + + + + Misc font/color, size = 28 + + */ + function genXmlTextRun(opts, text_string) { + var xmlTextRun = ''; + var paraProp = (opts.align ? '' : ''); + var parsedText; + + // Build runProperties + var startInfo = ', so add them now before closing the runProperties tag + if ( opts.color || opts.font_face ) { + if ( opts.color ) startInfo += genXmlColorSelection( opts.color ); + if ( opts.font_face ) startInfo += ''; + } + startInfo += ''; + + // LINE-BREAKS/MULTI-LINE: Split text into multi-p: + parsedText = text_string.split("\n"); + if ( parsedText.length > 1 ) { + var outTextData = ''; + for ( var i = 0, total_size_i = parsedText.length; i < total_size_i; i++ ) { + // ISSUE #34: Text with linebreaks doesnt allow for align formatting + // IMPORTANT: Closing

is fine and all, but one option `align` isnt set at the un-level - its a `pPr` so do that here: + outTextData += ( (opts.align && (i > 0 || opts.lineIdx > 0)) ? paraProp : '') + '' + startInfo+ '' + decodeXmlEntities(parsedText[i]); + // Stop/Start

aragraph as long as there is more lines ahead (otherwise its closed at the end of this function) + if ( (i + 1) < total_size_i ) { + outTextData += ''+ ( opts.align ? '' : '') +''; + } + } + xmlTextRun = outTextData; + } + else { + // Handle cases where addText `text` was an array of objects - if a text object doesnt contain a '\n' it still need alignment! + // The first pPr-align is done in makeXml - use line countr to ensure we only add subsequently as needed + xmlTextRun = ( (opts.align && opts.lineIdx > 0) ? paraProp : '') + '' + startInfo+ '' + decodeXmlEntities(text_string); + } + + // Return paragraph with text run + return xmlTextRun + '' + (opts.breakLine ? '' : ''); + } + + /** + * DESC: Builds tag + */ function genXmlBodyProperties(objOptions) { var bodyProperties = '' + area_opt_data.rpr_info) : '/>') - + ''; - var endTag = ''; - var outData = '' + startInfo; - - if ( text_string.field ) { - endTag = ''; - var outTextField = pptxFields[text_string.field]; - if ( outTextField === null ) { - for ( var fieldIntName in pptxFields ) { - if ( pptxFields[fieldIntName] === text_string.field ) { - outTextField = text_string.field; - break; - } - } - - if ( outTextField === null ) outTextField = 'datetime'; - } - - outData = '' + startInfo; - outData += CreateFieldText( outTextField, slide_num ); - - } - else { - // LINE-BREAKS/MULTI-LINE: Split text into multi-p: - parsedText = text_string.split("\n"); - if ( parsedText.length > 1 ) { - var outTextData = ''; - for ( var i = 0, total_size_i = parsedText.length; i < total_size_i; i++ ) { - // ISSUE #34: Text with linebreaks doesnt allow for align formatting - // IMPORTANT: Closing

is fine and all, but one option `align` isnt set at the un-level - its a `pPr` so do that here: - var outData = ( (text_info.align && (i > 0 || text_info.lineIdx > 0)) ? '' : '') + '' + startInfo; - outTextData += outData + decodeXmlEntities(parsedText[i]); - // Stop/Start

aragraph as long as there is more lines ahead (otherwise its closed at the end of this function) - if ( (i + 1) < total_size_i ) { - outTextData += ''+ ( text_info.align ? '' : '') +''; - } - } - outData = outTextData; - } - else { - // Handle cases where addText `text` was an array of objects - if a text object doesnt contain a '\n' it still need alignment! - // The first pPr-align is done in makeXml - use line countr to ensure we only add subsequently as needed - var outData = ( (text_info.align && (i > 0 || text_info.lineIdx > 0)) ? '' : '') + '' + startInfo; - outData += decodeXmlEntities(text_string); - } - } - - // Return paragraph with text run - return outData + '' + endTag + (text_info.breakLine ? '' : ''); - } - - function genXmlTextData(text_info, slide_obj) { - var out_obj = {}; - - out_obj.font_size = ''; - out_obj.bold = ''; - out_obj.italic = ''; - out_obj.underline = ''; - out_obj.rpr_info = ''; - out_obj.char_spacing = ''; - - if ( typeof text_info == 'object' ) { - if ( text_info.bold ) { - out_obj.bold = ' b="1"'; - } - - if ( text_info.italic ) { - out_obj.italic = ' i="1"'; - } - - if ( text_info.underline ) { - out_obj.underline = ' u="sng"'; - } - - if ( text_info.font_size ) { - out_obj.font_size = ' sz="' + text_info.font_size + '00"'; - } - - if ( text_info.char_spacing ) { - out_obj.char_spacing = ' spc="' + (text_info.char_spacing * 100) + '"'; - // must also disable kerning; otherwise text won't actually expand - out_obj.char_spacing += ' kern="0"'; - } - - if ( text_info.color ) { - out_obj.rpr_info += genXmlColorSelection( text_info.color ); - } - else if ( slide_obj && slide_obj.color ) { - out_obj.rpr_info += genXmlColorSelection( slide_obj.color ); - } - - if ( text_info.font_face ) { - out_obj.rpr_info += ''; - } - } - else { - if ( slide_obj && slide_obj.color ) out_obj.rpr_info += genXmlColorSelection( slide_obj.color ); - } - - if ( out_obj.rpr_info != '' ) out_obj.rpr_info += ''; - - return out_obj; - } - function genXmlColorSelection(color_info, back_info) { var colorVal; var fillType = 'solid'; @@ -962,7 +1040,8 @@ var PptxGenJS = function(){ // STEP 5: Loop over all Slide.data objects and add them to this slide =============================== $.each(inSlide.data, function(idx,slideObj){ var x = 0, y = 0, cx = (EMU*10), cy = 0; - var moreStyles = '', moreStylesAttr = '', outStyles = '', styleData = '', locationAttr = ''; + //var moreStyles = '', moreStylesAttr = '', outStyles = '', styleData = '', + var locationAttr = ''; var shapeType = null; // A: Set option vars @@ -1260,7 +1339,7 @@ var PptxGenJS = function(){ var effectsList = ''; if ( shapeType == null ) shapeType = getShapeInfo(null); - // A: Start Shape + // A: Start SHAPE ======================================================= strSlideXml += ''; // B: The addition of the "txBox" attribute is the sole determiner of if an object is a Shape or Textbox @@ -1320,96 +1399,14 @@ var PptxGenJS = function(){ } */ - // B: Close Shape + // B: Close Shape Properties strSlideXml += ''; - // OPTION: align - if ( slideObj.options && - ( slideObj.options.align || (Array.isArray(slideObj.text) && slideObj.text[0].options && slideObj.text[0].options.align) ) - ) { - var align = (Array.isArray(slideObj.text) && slideObj.text[0].options && slideObj.text[0].options.align ? slideObj.text[0].options.align : slideObj.options.align); - - switch ( align ) { - case 'r': - case 'right': - moreStylesAttr += ' algn="r"'; - break; - case 'c': - case 'ctr': - case 'center': - moreStylesAttr += ' algn="ctr"'; - break; - case 'justify': - moreStylesAttr += ' algn="just"'; - break; - } - } + // Add formatted text + strSlideXml += genXmlTextBody(slideObj); - // OPTION: indent - if ( slideObj.options && slideObj.options.indentLevel > 0 ) { - moreStylesAttr += ' lvl="' + slideObj.options.indentLevel + '"'; - } - - // OPTION: bullet - if ( slideObj.options.bullet ) { - moreStylesAttr = ' marL="228600" indent="-228600"'; - moreStyles = ''; - } - - // Add properties - if ( moreStyles ) outStyles = '' + moreStyles + ''; - else if ( moreStylesAttr ) outStyles = ''; - - // Add style - if ( styleData ) strSlideXml += '' + styleData + ''; - - // NOTE: MULTI-LINE: SPEC: Each "line" needs a `p`-aragraph section - /* - - bullet one - */ - - if ( slideObj.options.bullet - && typeof slideObj.text == 'string' && slideObj.text.split('\n').length > 0 - && slideObj.text.split('\n')[1] && slideObj.text.split('\n')[1].length > 0 ) - { - strSlideXml += '' + genXmlBodyProperties( slideObj.options ) + ''; - // IMPORTANT: Split text here (even though `genXmlTextCommand` handles \n) b/c we need `outStyles` for bullets to build correctly - slideObj.text.toString().split('\n').forEach(function(line,i){ - if ( i > 0 ) strSlideXml += ''; - strSlideXml += '' + outStyles; - strSlideXml += genXmlTextCommand( slideObj.options, line, inSlide.slide, inSlide.slide.getPageNumber() ); - }); - } - else if ( typeof slideObj.text == 'string' || typeof slideObj.text == 'number' ) { - strSlideXml += '' + genXmlBodyProperties( slideObj.options ) + '' + outStyles; - strSlideXml += genXmlTextCommand( slideObj.options, slideObj.text.toString(), inSlide.slide, inSlide.slide.getPageNumber() ); - } - else if ( slideObj.text && Array.isArray(slideObj.text) ) { - // DESC: This is an array of text objects: [{},{}] - // EX: slide.addText([ {text:'hello', options:{color:'0088CC'}}, {text:'world', options:{color:'CC8800'}} ]); - strSlideXml += '' + genXmlBodyProperties( slideObj.options ) + '' + outStyles; - slideObj.text.forEach(function(textObj,idx){ - textObj.options = textObj.options || {}; - textObj.options.lineIdx = idx; - strSlideXml += genXmlTextCommand( (textObj.options || slideObj.options), textObj.text.toString(), inSlide.slide, inSlide.slide.getPageNumber() ); - }); - } - /* - else if ( slideObj.text && typeof slideObj.text == 'object' && slideObj.text.field ) { - strSlideXml += '' + genXmlBodyProperties( slideObj.options ) + '' + outStyles; - strSlideXml += genXmlTextCommand( slideObj.options, slideObj.text, inSlide.slide, inSlide.slide.getPageNumber() ); - } - */ - - // LAST: End of every paragraph - if ( slideObj.text ) { - strSlideXml += ''; - strSlideXml += ''; - strSlideXml += ''; - } - - strSlideXml += (slideObj.type == 'cxn') ? '' : ''; + // LAST: Close SHAPE ======================================================= + strSlideXml += ''; break; case 'image': @@ -2082,18 +2079,17 @@ var PptxGenJS = function(){ else { // STEP 5: Loop over rows and create one+ tables as needed (ISSUE#21) getSlidesForTableRows(arrRows,opt).forEach(function(arrRows,idx){ - // A: We've got a the current Slide already, so add first slides' worth of rows to that, - // BUT, create new ones going forward! + // A: We've got a the current Slide already, so add first slides' worth of rows to that, BUT, create new ones going forward! if ( !gObjPptx.slides[slideNum+idx] ) { gObjPptx.slides[slideNum+idx] = $.extend(true,{},gObjPptx.slides[slideNum]); gObjPptx.slides[slideNum+idx].data = []; } - // B: Grab Slide object count - var slideObjNum = gObjPptx.slides[slideNum+idx].data.length; + // B: Reset opt.y to option or margin on subsequent Slides (ISSUE#43, ISSUE#47, ISSUE#48) + if ( idx > 0 ) opt.y = inch2Emu( opt.newPageStartY || ( (opt.y/EMU) < DEF_SLIDE_MARGIN_IN[0] ? (opt.y/EMU) : DEF_SLIDE_MARGIN_IN[0] ) ); // C: Add data (NOTE: Use `extend` to avoid mutation) - gObjPptx.slides[slideNum+idx].data[slideObjNum] = { + gObjPptx.slides[slideNum+idx].data[gObjPptx.slides[slideNum+idx].data.length] = { type: 'table', arrTabRows: arrRows, options: $.extend(true,{},opt) @@ -2131,6 +2127,9 @@ var PptxGenJS = function(){ gObjPptx.slides[slideNum].data[slideObjNum].options.bodyProp.bIns = inch2Emu(opt.inset); } + // OPTIONS: Set color if needed (inherit from Slide, or default to black) + if ( !opt.color ) opt.color = (this.color || '000000'); + // ROBUST: Set rational values for some shadow props if needed if ( opt.shadow ) { // OPT: `type` diff --git a/examples/media/sample.avi b/examples/media/sample.avi deleted file mode 100644 index e455f8c4d..000000000 Binary files a/examples/media/sample.avi and /dev/null differ diff --git a/examples/pptxgenjs-demo.html b/examples/pptxgenjs-demo.html index ebb1ea428..352175302 100755 --- a/examples/pptxgenjs-demo.html +++ b/examples/pptxgenjs-demo.html @@ -83,7 +83,6 @@ function genSlides_Table(pptx) { // SLIDE 1: Table text alignment and cell styles - // ======== ----------------------------------------------------------------------------------- { var slide = pptx.addNewSlide(); slide.addTable( [ [{ text:'Table Examples 1', opts:optsTitle }] ], { x:0.5, y:0.13, w:12.5, h:0.3 } ); // `opts` = legacy test @@ -161,7 +160,6 @@ } // SLIDE 2: Table row/col-spans - // ======== ----------------------------------------------------------------------------------- { var slide = pptx.addNewSlide(); // 2: Slide title @@ -278,7 +276,6 @@ } // SLIDE 4: Cell Formatting / Cell Margins - // ======== ----------------------------------------------------------------------------------- { var slide = pptx.addNewSlide(); // 2: Slide title @@ -298,16 +295,16 @@ slide.addTable( [['margin:[80,5,5,10]']], { x:10.5,y:1.1, margin:[80,5,5,10], w:2.2, fill:'F1F1F1' } ); // Complex/Compound border - var optsSub = JSON.parse(JSON.stringify(optsSubTitle)); optsSub.y = 3.0; + var optsSub = JSON.parse(JSON.stringify(optsSubTitle)); optsSub.y = 2.6; slide.addText('Complex Cell Border:', optsSub); var arrBorder = [ {color:'FF0000',pt:1}, {color:'00ff00',pt:3}, {color:'0000ff',pt:5}, {color:'9e9e9e',pt:7} ]; - slide.addTable( [['Borders!']], { x:0.5, y:3.4, w:12.3, rowH:2.0, fill:'F5F5F5', color:'3D3D3D', font_size:18, border:arrBorder, align:'c', valign:'c' } ); + slide.addTable( [['Borders!']], { x:0.5, y:3.0, w:12.3, rowH:2.0, fill:'F5F5F5', color:'3D3D3D', font_size:18, border:arrBorder, align:'c', valign:'c' } ); // Invalid char check - var optsSub = JSON.parse(JSON.stringify(optsSubTitle)); optsSub.y = 6.1; + var optsSub = JSON.parse(JSON.stringify(optsSubTitle)); optsSub.y = 5.7; slide.addText('Escaped Invalid Chars:', optsSub); var arrTabRows3 = [['<', '>', '"', "'", '&', 'plain']]; - slide.addTable( arrTabRows3, { x:0.5, y:6.5, w:12.3, rowH:0.5, fill:'F5F5F5', color:'3D3D3D', border:'FFFFFF', align:'c', valign:'c' } ); + slide.addTable( arrTabRows3, { x:0.5, y:6.0, w:12.3, rowH:0.5, fill:'F5F5F5', color:'3D3D3D', border:'FFFFFF', align:'c', valign:'c' } ); } @@ -329,20 +326,28 @@ slide.addText( [{text:'Table Examples: ', options:textTitle},{text:'Smaller Table Area', options:textSubtt}], {x:0.5, y:0.13, w:'90%'} ); slide.addTable( arrRows, { x:3.0, y:0.6, colW:[0.75,1.75, 7], margin:5, border:'CFCFCF' } ); + var slide = pptx.addNewSlide(); + slide.addText( [{text:'Table Examples: ', options:textTitle},{text:'Test for correct starting Y location upon paging', options:textSubtt}], {x:0.5, y:0.13, w:'90%'} ); + slide.addTable( arrRows, { x:3.0, y:4.0, colW:[0.75,1.75, 7], margin:5, border:'CFCFCF' } ); + + var slide = pptx.addNewSlide(); + slide.addText( [{text:'Table Examples: ', options:textTitle},{text:'Test: `{ newPageStartY: 0.5 }`', options:textSubtt}], {x:0.5, y:0.13, w:'90%'} ); + slide.addTable( arrRows, { x:3.0, y:4.0, newPageStartY:0.5, colW:[0.75,1.75, 7], margin:5, border:'CFCFCF' } ); + var slide = pptx.addNewSlide(); slide.addText( [{text:'Table Examples: ', options:textTitle},{text:'Auto-Paging Disabled', options:textSubtt}], {x:0.5, y:0.13, w:'90%'} ); slide.addTable( arrRows, { x:1.0, y:0.6, colW:[0.75,1.75, 7], margin:5, border:'CFCFCF', autoPage:false } ); - // lineWeight samples + // lineWeight option demos var slide = pptx.addNewSlide(); slide.addText( [{text:'Table Examples: ', options:textTitle},{text:'lineWeight:0', options:textSubtt}], {x:0.5, y:0.13, w:3} ); - slide.addTable( arrText, { x:0.5, y:0.6, w:4, margin:5, border:'CFCFCF', autoPage:true } ); + slide.addTable( arrText, { x:0.50, y:0.6, w:4, margin:5, border:'CFCFCF', autoPage:true } ); slide.addText( [{text:'Table Examples: ', options:textTitle},{text:'lineWeight:0.5', options:textSubtt}], {x:5.0, y:0.13, w:3} ); - slide.addTable( arrText, { x:4.8, y:0.6, w:4, margin:5, border:'CFCFCF', autoPage:true, lineWeight:0.5 } ); + slide.addTable( arrText, { x:4.75, y:0.6, w:4, margin:5, border:'CFCFCF', autoPage:true, lineWeight:0.5 } ); slide.addText( [{text:'Table Examples: ', options:textTitle},{text:'lineWeight:-0.5', options:textSubtt}], {x:9.0, y:0.13, w:3} ); - slide.addTable( arrText, { x:9.1, y:0.6, w:4, margin:5, border:'CFCFCF', autoPage:true, lineWeight:-0.5 } ); + slide.addTable( arrText, { x:9.10, y:0.6, w:4, margin:5, border:'CFCFCF', autoPage:true, lineWeight:-0.5 } ); } } @@ -385,7 +390,7 @@ // SLIDE 1: Misc Shape Types (no text) // ======== ----------------------------------------------------------------------------------- var slide = pptx.addNewSlide(); - slide.addTable( [ [{ text:'Shape Examples: Misc Shape Types (no text)', options:optsTitle }] ], { x:0.5, y:0.13, cx:12.5 } ); + slide.addTable( [ [{ text:'Shape Examples 1: Misc Shape Types (no text)', options:optsTitle }] ], { x:0.5, y:0.13, cx:12.5 } ); //slide.addShape(pptx.shapes.RECTANGLE, { x:0.5, y:0.8, w:12.5,h:0.5, fill:'F9F9F9' }); slide.addShape(pptx.shapes.RECTANGLE, { x:0.5, y:0.8, w:1.5, h:3.0, fill:'FF0000' }); @@ -405,7 +410,7 @@ // SLIDE 2: Misc Shape Types with Text // ======== ----------------------------------------------------------------------------------- var slide = pptx.addNewSlide(); - slide.addTable( [ [{ text:'Shape Examples: Misc Shape Types (with text)', options:optsTitle }] ], { x:0.5, y:0.13, w:12.5 } ); + slide.addTable( [ [{ text:'Shape Examples 2: Misc Shape Types (with text)', options:optsTitle }] ], { x:0.5, y:0.13, w:12.5 } ); slide.addText('RECTANGLE', { shape:pptx.shapes.RECTANGLE, x:0.5, y:0.8, w:1.5, h:3.0, fill:'FF0000', align:'c', font_size:14 }); slide.addText('RECTANGLE (rotate:45)', { shape:pptx.shapes.RECTANGLE, x:3.0, y:0.7, w:1.5, h:3.0, fill:'F38E00', rotate:45, align:'c', font_size:14 }); @@ -423,96 +428,112 @@ function genSlides_Text(pptx) { // SLIDE 1: Font Size/Color line examples in various font pts and random colors - // ======== ----------------------------------------------------------------------------------- - var slide = pptx.addNewSlide(); - slide.addTable( [ [{ text:'Text Examples 1', options:optsTitle }] ], { x:0.5, y:0.13, cx:12.5 } ); + { + var slide = pptx.addNewSlide(); + slide.addTable( [ [{ text:'Text Examples 1', options:optsTitle }] ], { x:0.5, y:0.13, cx:12.5 } ); - // DEMO: Array of text fonts/colors in increasing size - [18,22,28,32].forEach(function(val,i){ - slide.addText( 'Misc font/color, size = '+val, - { - x:0.5, y:0.7+(i*0.7), cx:12.75, - font_size:val, font_face:fonts[Math.floor((Math.random()*4)+1)], color:colors[i] - } + // LEFT COLUMN ------------------------------------------------------------ + + // 1: Multi-Line Formatting + slide.addText("Line-Level Formatting:", { x:0.5, y:0.65, w:'40%', h:0.38, color:'0088CC' }); + slide.addText( + [ + { text:'1st line', options:{ font_size:24, font_face:'Courier New', color:'99ABCC', align:'r', breakLine:true } }, + { text:'2nd line', options:{ font_size:36, font_face:'Arial', color:'FFFF00', align:'c', breakLine:true } }, + { text:'3rd line', options:{ font_size:48, font_face:'Verdana', color:'0088CC', align:'l' } } + ], + { x:0.5, y:1.1, w:6, h:2.25, margin:0.1, fill:'232323' } ); - }); - // Bullet Test: Number - slide.addText(999, { x:9.0, y:0.6, w:'20%', h:1, color:'0000DE', bullet:true }); - // Bullet Test: Text test - slide.addText('Bullet (str here, number above)', { x:9.0, y:1.0, w:'30%', h:1, color:'00AA00', bullet:true }); + // 2: Line-Break Test + slide.addText("Line-Breaks:", { x:0.5, y:3.6, w:'40%', h:0.38, color:'0088CC' }); + slide.addText( + '***Line-Break/Multi-Line Test***\n\nFirst line\nSecond line\nThird line', + { x:0.5, y:4.0, w:6, h:1.75, valign:'middle', align:'ctr', color:'6c6c6c', font_size:16, fill:'F2F2F2' } + ); - /* - // TODO: bullets in text objects need help (20170215) - slide.addText( - [ - { text:'I should be bulleted!', options:{bullet:true} }, - { text:'as should I be bulleted!', options:{bullet:true} } - ], - { x:9.0, y:1.0, w:'30%', h:1, color:'00AA00' } - ); + // 3: Text Effects: Shadow + var shadowOpts = { type:'outer', color:'696969', blur:3, offset:10, angle:45, opacity:0.8 }; + slide.addText("Text Shadow:", { x:0.5, y:6.0, w:'40%', h:0.38, color:'0088CC' }); + slide.addText( + 'Outer Shadow (blur:3, offset:10, angle:45, opacity:80%)', + { x:0.5, y:6.4, w:12, h:0.6, font_size:32, color:'0088cc', shadow:shadowOpts } + ); - // Bullets can be applied to the all text en mass - slide.addText( - [ - { text: 'big red words... ', options:{font_size:24, color:'FF0000'} }, - { text: 'some green words.', options:{font_size:16, color:'0000FF'} } - ], - { x:0.75, y:1.5, w:5, h:0.5, margin:0.1, font_face:'Arial', bullet:true } - ); - // TODO: ^^^ use 2+ {text} objects, each bulleted - */ + // RIGHT COLUMN ------------------------------------------------------------ - // Multi-line test: bullet lines - slide.addText('Line 1\nLine 2\nLine 3', { x:9.0, y:2.0, w:'30%', h:1, color:'393939', bullet:true, fill:'F2F2F2' }); - // Multi-line test: 3 lines - slide.addText( - '***Line-Break/Multi-Line Test***\n\nFirst line\nSecond line\nThird line', - { x:0.4, y:4.0, w:5, h:2, valign:'middle', align:'ctr', color:'6c6c6c', font_size:16, fill:'F2F2F2' } - ); + // 4: Regular bullets + slide.addText("Bullets:", { x:7.5, y:0.65, w:'40%', h:0.38, color:'0088CC' }); + slide.addText(12345 , { x:8.0, y:1.1, w:'30%', h:0.5, color:'0000DE', font_face:"Courier New", bullet:true }); + slide.addText('String (number above)', { x:8.0, y:1.4, w:'30%', h:0.5, color:'00AA00', bullet:true }); + + // 5: Bullets: Text With Line-Breaks + slide.addText("Bullets with line-breaks:", { x:7.5, y:2.0, w:'40%', h:0.38, color:'0088CC' }); + slide.addText('Line 1\nLine 2\nLine 3', { x:8.0, y:2.4, w:'30%', h:1, color:'393939', font_size:16, fill:'F2F2F2', bullet:true }); + + // 6: Bullets: With group of {text} + slide.addText("Bullet with {text} objects:", { x:7.5, y:3.6, w:'40%', h:0.38, color:'0088CC' }); + slide.addText( + [ + { text: 'big red words... ', options:{font_size:24, color:'FF0000'} }, + { text: 'some green words.', options:{font_size:16, color:'0000FF'} } + ], + { x:8.0, y:4.0, w:5, h:0.5, margin:0.1, font_face:'Arial', bullet:true } + ); - // Effects > Shadow - var shadowOpts = { type:'outer', color:'696969', blur:3, offset:10, angle:45, opacity:0.8 }; - slide.addText('Outer Shadow (blur:3, offset:10, angle:45, opacity:80%)', { x:0.5, y:6.0, w:12, h:1, font_size:32, color:'0088cc', shadow:shadowOpts }); + // 7: Bullets: Within a {text} object + slide.addText("Bullet within {text} objects:", { x:7.5, y:4.6, w:'40%', h:0.38, color:'0088CC' }); + slide.addText( + [ + { text:'I am a text object with bullets.. ', options:{bullet:true, color:'CC0000'} }, + { text:'and i am the next text object!' , options:{bullet:true, color:'00CD00'} }, + { text:'Text object without bullet:true.. ', options:{font_size:12} }, + { text:'then this is a text object too!' , options:{font_size:12} }, + { text:'Final text object w/ bullet:true!!', options:{bullet:true, color:'0000AB'} } + ], + { x:8.0, y:5.0, w:'30%', h:1.4, color:'ABABAB', margin:1 } + ); + } // SLIDE 2: Misc mess - // ======== ----------------------------------------------------------------------------------- - var slide = pptx.addNewSlide(); - // Slide colors: bkgd/fore - slide.back = '030303'; - slide.color = '9F9F9F'; - // Title - slide.addTable( [ [{ text:'Text Examples 2', options:optsTitle }] ], { x:0.5, y:0.13, w:12.5 } ); + { + var slide = pptx.addNewSlide(); + // Slide colors: bkgd/fore + slide.back = '030303'; + slide.color = '9F9F9F'; + // Title + slide.addTable( [ [{ text:'Text Examples 2', options:optsTitle }] ], { x:0.5, y:0.13, w:12.5 } ); - // Actual Textbox shape (can have any Height, can wrap text, etc.) - slide.addText( 'Textbox (ctr/ctr)', { x:0.5, y:0.75, w:8.5, h:2.5, color:'FFFFFF', fill:'0000FF', valign:'c', align:'c', isTextBox:true } ); - slide.addText( 'Textbox (top/lft)', { x:10, y:0.75, w:3.0, h:1.0, color:'FFFFFF', fill:'00CC00', valign:'t', align:'l', isTextBox:true } ); - slide.addText( 'Textbox (btm/rgt)', { x:10, y:2.25, w:3.0, h:1.0, color:'FFFFFF', fill:'FF0000', valign:'b', align:'r', isTextBox:true } ); + // Actual Textbox shape (can have any Height, can wrap text, etc.) + slide.addText( 'Textbox (ctr/ctr)', { x:0.5, y:0.75, w:8.5, h:2.5, color:'FFFFFF', fill:'0000FF', valign:'c', align:'c', isTextBox:true } ); + slide.addText( 'Textbox (top/lft)', { x:10, y:0.75, w:3.0, h:1.0, color:'FFFFFF', fill:'00CC00', valign:'t', align:'l', isTextBox:true } ); + slide.addText( 'Textbox (btm/rgt)', { x:10, y:2.25, w:3.0, h:1.0, color:'FFFFFF', fill:'FF0000', valign:'b', align:'r', isTextBox:true } ); - slide.addText('Plain x/y coords', { x:10, y:3.5 }); + slide.addText('Plain x/y coords', { x:10, y:3.5 }); - slide.addText('Escaped chars: \' " & < >', { x:10, y:4.5 }); + slide.addText('Escaped chars: \' " & < >', { x:10, y:4.5 }); - slide.addText('^ (50%/50%)', {x:'50%', y:'50%', w:2}); + slide.addText('^ (50%/50%)', {x:'50%', y:'50%', w:2}); - // TEST: using {option}: Add text box with multiline options: - slide.addText( - [ - { text:'word-level\nformatting', options:{ font_size:36, font_face:'Courier New', color:'99ABCC', align:'r', breakLine:true } }, - { text:'...in the same textbox', options:{ font_size:48, font_face:'Arial', color:'FFFF00', align:'c' } } - ], - { x:0.5, y:4.1, w:8.5, h:2.0, margin:0.1, fill:'232323' } - ); + // TEST: using {option}: Add text box with multiline options: + slide.addText( + [ + { text:'word-level\nformatting', options:{ font_size:36, font_face:'Courier New', color:'99ABCC', align:'r', breakLine:true } }, + { text:'...in the same textbox', options:{ font_size:48, font_face:'Arial', color:'FFFF00', align:'c' } } + ], + { x:0.5, y:4.1, w:8.5, h:2.0, margin:0.1, fill:'232323' } + ); - var objOptions = { - x:0, y:6.25, w:'100%', h:0.5, align:'c', - font_face:'Arial', font_size:24, color:'00EC23', bold:true, italic:true, underline:true, margin:0, isTextBox:true - }; - slide.addText('Arial 32pt, green, bold, italic, underline, margin:0, ctr', objOptions); + var objOptions = { + x:0, y:6.25, w:'100%', h:0.5, align:'c', + font_face:'Arial', font_size:24, color:'00EC23', bold:true, italic:true, underline:true, margin:0, isTextBox:true + }; + slide.addText('Arial 32pt, green, bold, italic, underline, margin:0, ctr', objOptions); - slide.addText('Footer Bar: PptxGenJS version ' + pptx.getVersion() + ' (width:100%, valign:ctr)', - { x:0, y:6.75, w:'100%', h:0.75, fill:'f7f7f7', color:'666666', align:'center', valign:'middle' } - ); + slide.addText('Footer Bar: PptxGenJS version ' + pptx.getVersion() + ' (width:100%, valign:ctr)', + { x:0, y:6.75, w:'100%', h:0.75, fill:'f7f7f7', color:'666666', align:'center', valign:'middle' } + ); + } } function genSlides_Master(pptx) { @@ -736,6 +757,14 @@ + ")\n" + "\n" + "slide.addText(\n" + + " [\n" + + " { text:'I should be bulleted!', options:{bullet:true} },\n" + + " { text:'as should I be bulleted!', options:{bullet:true} }\n" + + " ],\n" + + " { x:9.0, y:1.0, w:'30%', h:1, color:'00AA00' }\n" + + ");\n" + + "\n" + + "slide.addText(\n" + " [\n" + " { text:'word-level\\nformatting', options:{ font_size:36, font_face:'Arial', color:'99ABCC', align:'r', breakLine:true } },\n" + " { text:'...in the same textbox', options:{ font_size:48, font_face:'Times', color:'FFFF00', align:'c' } }\n"