diff --git a/.changeset/famous-lions-cheat.md b/.changeset/famous-lions-cheat.md new file mode 100644 index 00000000..eb7b83bb --- /dev/null +++ b/.changeset/famous-lions-cheat.md @@ -0,0 +1,5 @@ +--- +"penpot-exporter": minor +--- + +Added text basic properties diff --git a/plugin-src/transformers/partials/transformTextStyle.ts b/plugin-src/transformers/partials/transformTextStyle.ts index 1a28052c..3b9c9961 100644 --- a/plugin-src/transformers/partials/transformTextStyle.ts +++ b/plugin-src/transformers/partials/transformTextStyle.ts @@ -1,9 +1,20 @@ -import { translateTextDecoration, translateTextTransform } from '@plugin/translators'; +import slugify from 'slugify'; + +import { + translateFontStyle, + translateFontVariantId, + translateHorizontalAlign, + translateLetterSpacing, + translateLineHeight, + translateTextDecoration, + translateTextTransform +} from '@plugin/translators'; import { TextStyle } from '@ui/lib/types/text/textContent'; export const transformTextStyle = ( - node: Pick< + node: TextNode, + segment: Pick< StyledTextSegment, | 'characters' | 'start' @@ -19,11 +30,16 @@ export const transformTextStyle = ( > ): Partial => { return { - fontFamily: node.fontName.family, - fontSize: node.fontSize.toString(), - fontStyle: node.fontName.style, - fontWeight: node.fontWeight.toString(), - textDecoration: translateTextDecoration(node), - textTransform: translateTextTransform(node) + fontFamily: segment.fontName.family, + fontId: `gfont-${slugify(segment.fontName.family.toLowerCase())}`, + fontSize: segment.fontSize.toString(), + fontStyle: translateFontStyle(segment.fontName.style), + fontWeight: segment.fontWeight.toString(), + fontVariantId: translateFontVariantId(segment.fontName.style), + textAlign: translateHorizontalAlign(node.textAlignHorizontal), + textDecoration: translateTextDecoration(segment), + textTransform: translateTextTransform(segment), + letterSpacing: translateLetterSpacing(segment), + lineHeight: translateLineHeight(segment) }; }; diff --git a/plugin-src/transformers/transformTextNode.ts b/plugin-src/transformers/transformTextNode.ts index 952218a1..7490d018 100644 --- a/plugin-src/transformers/transformTextNode.ts +++ b/plugin-src/transformers/transformTextNode.ts @@ -5,9 +5,14 @@ import { transformFills, transformProportion, transformSceneNode, + transformStrokes, transformTextStyle } from '@plugin/transformers/partials'; -import { translateStyledTextSegments } from '@plugin/translators'; +import { + translateGrowType, + translateStyledTextSegments, + translateVerticalAlign +} from '@plugin/translators'; import { TextShape } from '@ui/lib/types/text/textShape'; @@ -28,24 +33,27 @@ export const transformTextNode = (node: TextNode, baseX: number, baseY: number): name: node.name, content: { type: 'root', + verticalAlign: translateVerticalAlign(node.textAlignVertical), children: [ { type: 'paragraph-set', children: [ { type: 'paragraph', - children: translateStyledTextSegments(styledTextSegments, node.width, node.height), - ...(styledTextSegments.length ? transformTextStyle(styledTextSegments[0]) : {}), + children: translateStyledTextSegments(node, styledTextSegments), + ...(styledTextSegments.length ? transformTextStyle(node, styledTextSegments[0]) : {}), ...transformFills(node) } ] } ] }, + growType: translateGrowType(node), ...transformDimensionAndPosition(node, baseX, baseY), ...transformEffects(node), ...transformSceneNode(node), ...transformBlend(node), - ...transformProportion(node) + ...transformProportion(node), + ...transformStrokes(node) }; }; diff --git a/plugin-src/translators/index.ts b/plugin-src/translators/index.ts index e6acf142..26d7a0d8 100644 --- a/plugin-src/translators/index.ts +++ b/plugin-src/translators/index.ts @@ -1,8 +1,15 @@ export * from './translateBlendMode'; export * from './translateShadowEffects'; export * from './translateFills'; +export * from './translateFontStyle'; +export * from './translateFontVariantId'; +export * from './translateGrowType'; +export * from './translateHorizontalAlign'; +export * from './translateLetterSpacing'; +export * from './translateLineHeight'; export * from './translateStrokes'; export * from './translateStyledTextSegments'; export * from './translateTextDecoration'; export * from './translateTextTransform'; export * from './translateVectorPaths'; +export * from './translateVerticalAlign'; diff --git a/plugin-src/translators/translateFontStyle.ts b/plugin-src/translators/translateFontStyle.ts new file mode 100644 index 00000000..b229208e --- /dev/null +++ b/plugin-src/translators/translateFontStyle.ts @@ -0,0 +1,9 @@ +import { TextFontStyle } from '@ui/lib/types/text/textContent'; + +export const translateFontStyle = (style: string): TextFontStyle => { + if (style.toLowerCase().includes('italic')) { + return 'italic'; + } + + return 'normal'; +}; diff --git a/plugin-src/translators/translateFontVariantId.ts b/plugin-src/translators/translateFontVariantId.ts new file mode 100644 index 00000000..6ccbd440 --- /dev/null +++ b/plugin-src/translators/translateFontVariantId.ts @@ -0,0 +1,3 @@ +export const translateFontVariantId = (style: string) => { + return style.toLowerCase().replace(/\s/g, ''); +}; diff --git a/plugin-src/translators/translateGrowType.ts b/plugin-src/translators/translateGrowType.ts new file mode 100644 index 00000000..669f4a06 --- /dev/null +++ b/plugin-src/translators/translateGrowType.ts @@ -0,0 +1,17 @@ +import { GrowType } from '@ui/lib/types/shape/shapeAttributes'; + +export const translateGrowType = (node: TextNode): GrowType => { + if (node.leadingTrim === 'CAP_HEIGHT') { + return 'fixed'; + } + + switch (node.textAutoResize) { + case 'WIDTH_AND_HEIGHT': + return 'auto-width'; + case 'HEIGHT': + return 'auto-height'; + case 'TRUNCATE': + default: + return 'fixed'; + } +}; diff --git a/plugin-src/translators/translateHorizontalAlign.ts b/plugin-src/translators/translateHorizontalAlign.ts new file mode 100644 index 00000000..04462b2a --- /dev/null +++ b/plugin-src/translators/translateHorizontalAlign.ts @@ -0,0 +1,16 @@ +import { TextHorizontalAlign } from '@ui/lib/types/text/textContent'; + +export const translateHorizontalAlign = ( + align: 'LEFT' | 'CENTER' | 'RIGHT' | 'JUSTIFIED' +): TextHorizontalAlign => { + switch (align) { + case 'RIGHT': + return 'right'; + case 'CENTER': + return 'center'; + case 'JUSTIFIED': + return 'justify'; + default: + return 'left'; + } +}; diff --git a/plugin-src/translators/translateLetterSpacing.ts b/plugin-src/translators/translateLetterSpacing.ts new file mode 100644 index 00000000..a2dbc713 --- /dev/null +++ b/plugin-src/translators/translateLetterSpacing.ts @@ -0,0 +1,12 @@ +export const translateLetterSpacing = ( + segment: Pick +): number => { + switch (segment.letterSpacing.unit) { + case 'PIXELS': + return segment.letterSpacing.value; + case 'PERCENT': + return (segment.fontSize * segment.letterSpacing.value) / 100; + default: + return 0; + } +}; diff --git a/plugin-src/translators/translateLineHeight.ts b/plugin-src/translators/translateLineHeight.ts new file mode 100644 index 00000000..ee8bc6c1 --- /dev/null +++ b/plugin-src/translators/translateLineHeight.ts @@ -0,0 +1,10 @@ +export const translateLineHeight = ( + segment: Pick +): number | undefined => { + switch (segment.lineHeight.unit) { + case 'PIXELS': + return segment.lineHeight.value / segment.fontSize; + case 'PERCENT': + return segment.lineHeight.value / 100; + } +}; diff --git a/plugin-src/translators/translateStyledTextSegments.ts b/plugin-src/translators/translateStyledTextSegments.ts index 2caf1e62..43bae5dd 100644 --- a/plugin-src/translators/translateStyledTextSegments.ts +++ b/plugin-src/translators/translateStyledTextSegments.ts @@ -1,9 +1,10 @@ import { transformTextStyle } from '@plugin/transformers/partials'; import { translateFills } from '@plugin/translators/translateFills'; -import { TextNode } from '@ui/lib/types/text/textContent'; +import { TextNode as PenpotTextNode } from '@ui/lib/types/text/textContent'; export const translateStyledTextSegments = ( + node: TextNode, segments: Pick< StyledTextSegment, | 'characters' @@ -17,17 +18,15 @@ export const translateStyledTextSegments = ( | 'textCase' | 'textDecoration' | 'fills' - >[], - width: number, - height: number -): TextNode[] => { + >[] +): PenpotTextNode[] => { return segments.map(segment => { figma.ui.postMessage({ type: 'FONT_NAME', data: segment.fontName.family }); return { - fills: translateFills(segment.fills, width, height), + fills: translateFills(segment.fills, node.width, node.height), text: segment.characters, - ...transformTextStyle(segment) + ...transformTextStyle(node, segment) }; }); }; diff --git a/plugin-src/translators/translateVerticalAlign.ts b/plugin-src/translators/translateVerticalAlign.ts new file mode 100644 index 00000000..87677168 --- /dev/null +++ b/plugin-src/translators/translateVerticalAlign.ts @@ -0,0 +1,12 @@ +import { TextVerticalAlign } from '@ui/lib/types/text/textContent'; + +export const translateVerticalAlign = (align: 'TOP' | 'CENTER' | 'BOTTOM'): TextVerticalAlign => { + switch (align) { + case 'BOTTOM': + return 'bottom'; + case 'CENTER': + return 'center'; + default: + return 'top'; + } +}; diff --git a/ui-src/lib/penpot.js b/ui-src/lib/penpot.js index 2c73ffc3..7a61ff48 100644 --- a/ui-src/lib/penpot.js +++ b/ui-src/lib/penpot.js @@ -1,99 +1,37 @@ -var $jscomp=$jscomp||{};$jscomp.scope={};$jscomp.arrayIteratorImpl=function(a){var b=0;return function(){return b>>0,$jscomp.propertyToPolyfillSymbol[e]=$jscomp.IS_SYMBOL_NATIVE? -$jscomp.global.Symbol(e):$jscomp.POLYFILL_PREFIX+c+"$"+e),$jscomp.defineProperty(d,$jscomp.propertyToPolyfillSymbol[e],{configurable:!0,writable:!0,value:b})))};$jscomp.underscoreProtoCanBeSet=function(){var a={a:!0},b={};try{return b.__proto__=a,b.a}catch(c){}return!1}; -$jscomp.setPrototypeOf=$jscomp.TRUST_ES6_POLYFILLS&&"function"==typeof Object.setPrototypeOf?Object.setPrototypeOf:$jscomp.underscoreProtoCanBeSet()?function(a,b){a.__proto__=b;if(a.__proto__!==b)throw new TypeError(a+" is not extensible");return a}:null;$jscomp.generator={};$jscomp.generator.ensureIteratorResultIsObject_=function(a){if(!(a instanceof Object))throw new TypeError("Iterator result "+a+" is not an object");}; -$jscomp.generator.Context=function(){this.isRunning_=!1;this.yieldAllIterator_=null;this.yieldResult=void 0;this.nextAddress=1;this.finallyAddress_=this.catchAddress_=0;this.finallyContexts_=this.abruptCompletion_=null};$jscomp.generator.Context.prototype.start_=function(){if(this.isRunning_)throw new TypeError("Generator is already running");this.isRunning_=!0};$jscomp.generator.Context.prototype.stop_=function(){this.isRunning_=!1}; -$jscomp.generator.Context.prototype.jumpToErrorHandler_=function(){this.nextAddress=this.catchAddress_||this.finallyAddress_};$jscomp.generator.Context.prototype.next_=function(a){this.yieldResult=a};$jscomp.generator.Context.prototype.throw_=function(a){this.abruptCompletion_={exception:a,isException:!0};this.jumpToErrorHandler_()};$jscomp.generator.Context.prototype.return=function(a){this.abruptCompletion_={return:a};this.nextAddress=this.finallyAddress_}; -$jscomp.generator.Context.prototype.jumpThroughFinallyBlocks=function(a){this.abruptCompletion_={jumpTo:a};this.nextAddress=this.finallyAddress_};$jscomp.generator.Context.prototype.yield=function(a,b){this.nextAddress=b;return{value:a}};$jscomp.generator.Context.prototype.yieldAll=function(a,b){a=$jscomp.makeIterator(a);var c=a.next();$jscomp.generator.ensureIteratorResultIsObject_(c);if(c.done)this.yieldResult=c.value,this.nextAddress=b;else return this.yieldAllIterator_=a,this.yield(c.value,b)}; -$jscomp.generator.Context.prototype.jumpTo=function(a){this.nextAddress=a};$jscomp.generator.Context.prototype.jumpToEnd=function(){this.nextAddress=0};$jscomp.generator.Context.prototype.setCatchFinallyBlocks=function(a,b){this.catchAddress_=a;void 0!=b&&(this.finallyAddress_=b)};$jscomp.generator.Context.prototype.setFinallyBlock=function(a){this.catchAddress_=0;this.finallyAddress_=a||0};$jscomp.generator.Context.prototype.leaveTryBlock=function(a,b){this.nextAddress=a;this.catchAddress_=b||0}; -$jscomp.generator.Context.prototype.enterCatchBlock=function(a){this.catchAddress_=a||0;a=this.abruptCompletion_.exception;this.abruptCompletion_=null;return a};$jscomp.generator.Context.prototype.enterFinallyBlock=function(a,b,c){c?this.finallyContexts_[c]=this.abruptCompletion_:this.finallyContexts_=[this.abruptCompletion_];this.catchAddress_=a||0;this.finallyAddress_=b||0}; -$jscomp.generator.Context.prototype.leaveFinallyBlock=function(a,b){b=this.finallyContexts_.splice(b||0)[0];if(b=this.abruptCompletion_=this.abruptCompletion_||b){if(b.isException)return this.jumpToErrorHandler_();void 0!=b.jumpTo&&this.finallyAddress_>>0)+"_",d=0,e=function(f){if(this instanceof e)throw new TypeError("Symbol is not a constructor");return new b(c+(f||"")+"_"+d++,f)};return e},"es6","es3"); -$jscomp.polyfill("Symbol.iterator",function(a){if(a)return a;a=Symbol("Symbol.iterator");for(var b="Array Int8Array Uint8Array Uint8ClampedArray Int16Array Uint16Array Int32Array Uint32Array Float32Array Float64Array".split(" "),c=0;c>>0,$jscomp.propertyToPolyfillSymbol[e]=$jscomp.IS_SYMBOL_NATIVE? +$jscomp.global.Symbol(e):$jscomp.POLYFILL_PREFIX+b+"$"+e),$jscomp.defineProperty(d,$jscomp.propertyToPolyfillSymbol[e],{configurable:!0,writable:!0,value:c})))};$jscomp.arrayIteratorImpl=function(a){var c=0;return function(){return c>>0)+"_",d=0,e=function(f){if(this instanceof e)throw new TypeError("Symbol is not a constructor");return new c(b+(f||"")+"_"+d++,f)};return e},"es6","es3"); +$jscomp.polyfill("Symbol.iterator",function(a){if(a)return a;a=Symbol("Symbol.iterator");for(var c="Array Int8Array Uint8Array Uint8ClampedArray Int16Array Uint16Array Int32Array Uint32Array Float32Array Float64Array".split(" "),b=0;b>>0,$jscomp.propertyToPolyfillSymbol[e]=$jscomp.IS_SYMBOL_NATIVE? -$jscomp.global.Symbol(e):$jscomp.POLYFILL_PREFIX+c+"$"+e),$jscomp.defineProperty(d,$jscomp.propertyToPolyfillSymbol[e],{configurable:!0,writable:!0,value:b})))};$jscomp.underscoreProtoCanBeSet=function(){var a={a:!0},b={};try{return b.__proto__=a,b.a}catch(c){}return!1}; -$jscomp.setPrototypeOf=$jscomp.TRUST_ES6_POLYFILLS&&"function"==typeof Object.setPrototypeOf?Object.setPrototypeOf:$jscomp.underscoreProtoCanBeSet()?function(a,b){a.__proto__=b;if(a.__proto__!==b)throw new TypeError(a+" is not extensible");return a}:null;$jscomp.generator={};$jscomp.generator.ensureIteratorResultIsObject_=function(a){if(!(a instanceof Object))throw new TypeError("Iterator result "+a+" is not an object");}; -$jscomp.generator.Context=function(){this.isRunning_=!1;this.yieldAllIterator_=null;this.yieldResult=void 0;this.nextAddress=1;this.finallyAddress_=this.catchAddress_=0;this.finallyContexts_=this.abruptCompletion_=null};$jscomp.generator.Context.prototype.start_=function(){if(this.isRunning_)throw new TypeError("Generator is already running");this.isRunning_=!0};$jscomp.generator.Context.prototype.stop_=function(){this.isRunning_=!1}; -$jscomp.generator.Context.prototype.jumpToErrorHandler_=function(){this.nextAddress=this.catchAddress_||this.finallyAddress_};$jscomp.generator.Context.prototype.next_=function(a){this.yieldResult=a};$jscomp.generator.Context.prototype.throw_=function(a){this.abruptCompletion_={exception:a,isException:!0};this.jumpToErrorHandler_()};$jscomp.generator.Context.prototype.return=function(a){this.abruptCompletion_={return:a};this.nextAddress=this.finallyAddress_}; -$jscomp.generator.Context.prototype.jumpThroughFinallyBlocks=function(a){this.abruptCompletion_={jumpTo:a};this.nextAddress=this.finallyAddress_};$jscomp.generator.Context.prototype.yield=function(a,b){this.nextAddress=b;return{value:a}};$jscomp.generator.Context.prototype.yieldAll=function(a,b){a=$jscomp.makeIterator(a);var c=a.next();$jscomp.generator.ensureIteratorResultIsObject_(c);if(c.done)this.yieldResult=c.value,this.nextAddress=b;else return this.yieldAllIterator_=a,this.yield(c.value,b)}; -$jscomp.generator.Context.prototype.jumpTo=function(a){this.nextAddress=a};$jscomp.generator.Context.prototype.jumpToEnd=function(){this.nextAddress=0};$jscomp.generator.Context.prototype.setCatchFinallyBlocks=function(a,b){this.catchAddress_=a;void 0!=b&&(this.finallyAddress_=b)};$jscomp.generator.Context.prototype.setFinallyBlock=function(a){this.catchAddress_=0;this.finallyAddress_=a||0};$jscomp.generator.Context.prototype.leaveTryBlock=function(a,b){this.nextAddress=a;this.catchAddress_=b||0}; -$jscomp.generator.Context.prototype.enterCatchBlock=function(a){this.catchAddress_=a||0;a=this.abruptCompletion_.exception;this.abruptCompletion_=null;return a};$jscomp.generator.Context.prototype.enterFinallyBlock=function(a,b,c){c?this.finallyContexts_[c]=this.abruptCompletion_:this.finallyContexts_=[this.abruptCompletion_];this.catchAddress_=a||0;this.finallyAddress_=b||0}; -$jscomp.generator.Context.prototype.leaveFinallyBlock=function(a,b){b=this.finallyContexts_.splice(b||0)[0];if(b=this.abruptCompletion_=this.abruptCompletion_||b){if(b.isException)return this.jumpToErrorHandler_();void 0!=b.jumpTo&&this.finallyAddress_>>0)+"_",d=0,e=function(f){if(this instanceof e)throw new TypeError("Symbol is not a constructor");return new b(c+(f||"")+"_"+d++,f)};return e},"es6","es3"); -$jscomp.polyfill("Symbol.iterator",function(a){if(a)return a;a=Symbol("Symbol.iterator");for(var b="Array Int8Array Uint8Array Uint8ClampedArray Int16Array Uint16Array Int32Array Uint32Array Float32Array Float64Array".split(" "),c=0;c>>0,$jscomp.propertyToPolyfillSymbol[e]=$jscomp.IS_SYMBOL_NATIVE? +$jscomp.global.Symbol(e):$jscomp.POLYFILL_PREFIX+b+"$"+e),$jscomp.defineProperty(d,$jscomp.propertyToPolyfillSymbol[e],{configurable:!0,writable:!0,value:c})))};$jscomp.arrayIteratorImpl=function(a){var c=0;return function(){return c>>0)+"_",d=0,e=function(f){if(this instanceof e)throw new TypeError("Symbol is not a constructor");return new c(b+(f||"")+"_"+d++,f)};return e},"es6","es3"); +$jscomp.polyfill("Symbol.iterator",function(a){if(a)return a;a=Symbol("Symbol.iterator");for(var c="Array Int8Array Uint8Array Uint8ClampedArray Int16Array Uint16Array Int32Array Uint32Array Float32Array Float64Array".split(" "),b=0;b