1515 */
1616#include "jswrap_graphics.h"
1717#include "jswrap_math.h" // for jswrap_math_cos/sin
18+ #include "jswrap_string.h" // for jswrap_string_split
19+ #include "jswrap_array.h" // for jswrap_array_join
1820#include "jsutils.h"
1921#include "jsinteractive.h"
2022
5052#ifdef ESPR_LINE_FONTS
5153#include "line_font.h"
5254#endif
53-
55+ #ifdef BANGLEJS2
56+ #include "jswrap_font_15.h"
57+ #include "jswrap_font_19.h"
58+ #include "jswrap_font_22.h"
59+ #endif
5460
5561#ifdef GRAPHICS_PALETTED_IMAGES
5662#if defined(ESPR_GRAPHICS_12BIT )
@@ -2297,7 +2303,7 @@ int jswrap_graphics_getFontHeight(JsVar *parent) {
22972303
22982304typedef struct {
22992305 int stringWidth ; // width in pixels
2300- int stringHeight ; // height in pixels
2306+ int stringHeight , lineHeight ; // height in pixels
23012307 bool unrenderableChars ; // are any chars in this not renderable in the current font?
23022308#ifndef SAVE_ON_FLASH
23032309 int imageCount ; // how many inline images are in this string?
@@ -2308,11 +2314,13 @@ typedef struct {
23082314/** Work out the width and height of a bit of text. If 'lineStartIndex' is -1 the whole string is used
23092315 * otherwise *just* the line of text starting at that char index is used */
23102316void _jswrap_graphics_stringMetrics (JsGraphics * gfx , JsVar * var , int lineStartIndex , StringMetricsResult * result ) {
2317+ assert (result );
23112318 JsGraphicsFontInfo info ;
23122319 _jswrap_graphics_getFontInfo (gfx , & info );
23132320 memset (result , 0 , sizeof (StringMetricsResult ));
23142321
23152322 int fontHeight = _jswrap_graphics_getFontHeightInternal (gfx , & info );
2323+ result -> lineHeight = fontHeight ;
23162324 JsVar * str = jsvAsString (var );
23172325 JsvStringIterator it ;
23182326 jsvStringIteratorNewUTF8 (& it , str , (size_t )((lineStartIndex < 0 )?0 :lineStartIndex ));
@@ -2585,6 +2593,148 @@ JsVar *jswrap_graphics_wrapString(JsVar *parent, JsVar *str, int maxWidth) {
25852593 return lines ;
25862594}
25872595
2596+
2597+ /*JSON{
2598+ "type" : "method",
2599+ "class" : "Graphics",
2600+ "name" : "findFont",
2601+ "ifdef" : "BANGLEJS2",
2602+ "generate" : "jswrap_graphics_findFont",
2603+ "params" : [
2604+ ["text","JsVar","The text to render"],
2605+ ["options","JsVar","Options for finding the required font"]
2606+ ],
2607+ "return" : ["JsVar","An object containing info about the font"]
2608+ }
2609+ Works out which font to use, and sets the current font to it.
2610+
2611+ Usage:
2612+
2613+ ```
2614+ g.findFont("Hello World", {
2615+ w : 100, // optional: width available (default = screen width)
2616+ h : 100, // optional: height available (default = screen height)
2617+ min : 10, // optional: min font height
2618+ max : 30, // optional: max font height
2619+ wrap : true // optional: allow word wrap?
2620+ trim : true // optional: trim to the specified height, add '...'
2621+ });
2622+ ```
2623+
2624+ Returns:
2625+
2626+ ```
2627+ {
2628+ text : "Hello\nWorld"
2629+ font : "..."
2630+ }
2631+ ```
2632+ */
2633+
2634+
2635+ #ifdef BANGLEJS2
2636+ typedef struct {
2637+ const char * name ;
2638+ uint8_t height ;
2639+ JsVar * (* setFont )(JsVar * parent , int scale );
2640+ } JswFindFontFont ;
2641+
2642+ JsVar * jswrap_graphics_setFont6x8 (JsVar * parent , int scale ) {
2643+ return jswrap_graphics_setFontSizeX (parent , 1 + JSGRAPHICS_FONTSIZE_6X8 , false);
2644+ }
2645+ JsVar * jswrap_graphics_setFont4x6 (JsVar * parent , int scale ) {
2646+ return jswrap_graphics_setFontSizeX (parent , 1 + JSGRAPHICS_FONTSIZE_4X6 , false);
2647+ }
2648+
2649+
2650+ JsVar * jswrap_graphics_findFont (JsVar * parent , JsVar * text , JsVar * options ) {
2651+ if (!jsvIsString (text )) return 0 ;
2652+ JsGraphics gfx ; if (!graphicsGetFromVar (& gfx , parent )) return 0 ;
2653+ int width = gfx .data .width , height = gfx .data .height ;
2654+ int minHeight = 4 , maxHeight = 100 ;
2655+ bool wrap = false, trim = false;
2656+ JsVar * result = jsvNewObject ();
2657+ if (!result ) return 0 ;
2658+
2659+ jsvConfigObject configs [] = {
2660+ {"w" , JSV_INTEGER , & width },
2661+ {"h" , JSV_INTEGER , & height },
2662+ {"min" , JSV_INTEGER , & minHeight },
2663+ {"max" , JSV_INTEGER , & maxHeight },
2664+ {"wrap" , JSV_BOOLEAN , & wrap },
2665+ {"trim" , JSV_BOOLEAN , & trim },
2666+ };
2667+ if (!jsvReadConfigObject (options , configs , sizeof (configs ) / sizeof (jsvConfigObject ))) {
2668+ return 0 ;
2669+ }
2670+
2671+ const int FONTS = 5 ;
2672+ JswFindFontFont FONT [5 ] = {
2673+ {"22" , 22 , jswrap_graphics_setFont22 },
2674+ {"19" , 19 , jswrap_graphics_setFont19 },
2675+ {"15" , 15 , jswrap_graphics_setFont15 },
2676+ {"6x8" , 8 , jswrap_graphics_setFont6x8 },
2677+ {"4x6" , 6 , jswrap_graphics_setFont4x6 }
2678+ };
2679+ int fontIdx = 0 ;
2680+ // check max font size
2681+ while (fontIdx < FONTS - 1 && FONT [fontIdx ].height > maxHeight )
2682+ fontIdx ++ ;
2683+ // Run through fonts, big->small, to find one that fits
2684+ StringMetricsResult stringMetrics ;
2685+ JsVar * finalText = jsvLockAgain (text );
2686+ JsVar * finalLines = NULL ;
2687+ JsVar * newline = jsvNewFromString ("\n" );
2688+ while (fontIdx < FONTS - 1 ) {
2689+ jsvUnLock (FONT [fontIdx ].setFont (parent ,1 ));
2690+ graphicsGetFromVar (& gfx , parent );
2691+ if (wrap ) {
2692+ jsvUnLock2 (finalText , finalLines );
2693+ finalLines = jswrap_graphics_wrapString (parent , text , width );
2694+ finalText = jsvArrayJoin (finalLines ,newline ,true);
2695+ _jswrap_graphics_stringMetrics (& gfx , finalText , -1 , & stringMetrics );
2696+ } else
2697+ _jswrap_graphics_stringMetrics (& gfx , text , -1 , & stringMetrics );
2698+ if (((stringMetrics .stringWidth <= width ) && (stringMetrics .stringHeight <= height )) || // all good!
2699+ fontIdx == FONTS - 1 || // no more fonts
2700+ FONT [fontIdx + 1 ].height < minHeight // next font is too small
2701+ ) break ;
2702+ fontIdx ++ ;
2703+ }
2704+ const char * fontName = FONT [fontIdx ].name ;
2705+ // if there were unrenderable characters, use the international font instead if we have one
2706+ if (stringMetrics .unrenderableChars ) {
2707+ JsVar * intlFont = jspGetNamedField (parent , "setFontIntl" , false);
2708+ if (intlFont ) {
2709+ fontName = "Intl" ;
2710+ jsvUnLock (jspExecuteFunction (intlFont , parent , 0 , NULL ));
2711+ graphicsGetFromVar (& gfx , parent );
2712+ _jswrap_graphics_stringMetrics (& gfx , text , -1 , & stringMetrics );
2713+ }
2714+ }
2715+ if (trim && stringMetrics .stringHeight > height ) { // do we have to trim these lines to length?
2716+ JsVar * lines = jsvNewFromInteger (height / stringMetrics .lineHeight );
2717+ if (!finalLines )
2718+ finalLines = jswrap_string_split (finalText , newline );
2719+ JsVar * croppedArr = jswrap_array_slice (finalLines , 0 , lines );
2720+ jsvUnLock2 (finalText , lines );
2721+ finalText = jsvArrayJoin (croppedArr ,newline ,true);
2722+ jsvUnLock (croppedArr );
2723+ jsvAppendString (finalText , "..." ); // Add ... to the end (TODO: check if room?)
2724+ _jswrap_graphics_stringMetrics (& gfx , finalText , -1 , & stringMetrics ); // work out string size again
2725+ }
2726+ // TODO: trim width if not wrapping?
2727+ jsvUnLock2 (newline , finalLines );
2728+ jsvObjectSetChildAndUnLock (result , "text" , finalText );
2729+ jsvObjectSetChildAndUnLock (result , "font" , jsvNewFromString (fontName ));
2730+ jsvObjectSetChildAndUnLock (result , "w" , jsvNewFromInteger (stringMetrics .stringWidth ));
2731+ jsvObjectSetChildAndUnLock (result , "h" , jsvNewFromInteger (stringMetrics .stringHeight ));
2732+
2733+ return result ;
2734+ }
2735+ #endif
2736+
2737+
25882738/*JSON{
25892739 "type" : "method",
25902740 "class" : "Graphics",
0 commit comments