From db50b97c9ef69331c4a3cabc7be3552d28cb1ac0 Mon Sep 17 00:00:00 2001
From: robertoffmoura <robertoffmoura@gmail.com>
Date: Sun, 4 Aug 2024 17:51:32 +0100
Subject: [PATCH] Use built-in 'matlab.net.base64decode' (introduced in R2016b)
 instead of vendoring 'base64decode'

---
 plotly/export_fig2/write_image.m | 198 ++++++++++++++++---------------
 plotly/plotly_aux/base64decode.m | 117 ------------------
 2 files changed, 102 insertions(+), 213 deletions(-)
 delete mode 100644 plotly/plotly_aux/base64decode.m

diff --git a/plotly/export_fig2/write_image.m b/plotly/export_fig2/write_image.m
index bf84f72e..921ef066 100644
--- a/plotly/export_fig2/write_image.m
+++ b/plotly/export_fig2/write_image.m
@@ -1,111 +1,117 @@
 function output = write_image(pfObj, imageFormat, filename, height, width, scale)
+    % Function to write plotly figures to a supported image format, which
+    % are the following: "png", "jpg", "jpeg", "webp", "svg", "pdf", "eps",
+    % "json".
+
+    debug=0;
+    if nargin < 2
+        imageFormat = "png";
+        filename = "figure.png";
+        height = pfObj.layout.height;
+        width = pfObj.layout.width;
+        scale = 1;
+    elseif nargin < 3
+        filename = "figure." + imageFormat;
+        height = pfObj.layout.height;
+        width = pfObj.layout.width;
+        scale = 1;
+    elseif nargin < 4
+        height = pfObj.layout.height;
+        width = pfObj.layout.width;
+        scale = 1;
+    elseif nargin < 5
+        width = pfObj.layout.width;
+        scale = 1;
+    elseif nargin < 6
+        scale = 1;
+    end
 
-% Function to write plotly figures to a supported image format, which are the following: "png", "jpg", "jpeg", "webp", "svg", "pdf", "eps", "json"
-
-debug=0;
-if nargin < 2
-    imageFormat="png";
-    filename='figure.png';
-    height=pfObj.layout.height;
-    width=pfObj.layout.width;
-    scale=1;
-elseif nargin < 3
-    filename=['figure.',char(imageFormat)];
-    height=pfObj.layout.height;
-    width=pfObj.layout.width;
-    scale=1;
-elseif nargin < 4
-    height=pfObj.layout.height;
-    width=pfObj.layout.width;
-    scale=1;
-elseif nargin < 5
-    width=pfObj.layout.width;
-    scale=1;
-elseif nargin < 6
-    scale=1;
-end
-
-if strcmpi(imageFormat,'jpg')
-    imageFormat = "jpeg";
-end
-    
-wd=fileparts(fileparts(mfilename('fullpath')));
-output=[];
-
-if ~isa(pfObj,'plotlyfig')
-    fprintf('\nError: Input is not a plotlyfig object.\n\n');
-    return
-end
-
-if isunix()
-    kExec = string(fullfile(wd,'kaleido','kaleido'));
-    cc="cat";
-else
-    kExec = string(fullfile(wd,'kaleido','kaleido.cmd'));
-    cc="type";
-end
-plyJsLoc = string(fullfile(wd,'kaleido','plotly-latest.min.js'));
+    if strcmpi(imageFormat, "jpg")
+        imageFormat = "jpeg";
+    end
 
-if ~isfile(kExec) || ~isfile(plyJsLoc)
-    status=getKaleido();
-else
-    status=1;
-end
+    wd = fileparts(fileparts(mfilename("fullpath")));
+    output = [];
 
-if status == 0 
-    return 
-end
+    if ~isa(pfObj, "plotlyfig")
+        fprintf("\nError: Input is not a plotlyfig object.\n\n");
+        return
+    end
 
-mjLoc = replace(string(fullfile(wd,'kaleido','etc','mathjax','MathJax.js')),'\','/');
-scope="plotly";
+    if isunix()
+        kExec = string(fullfile(wd,"kaleido", "kaleido"));
+        cc = "cat";
+    else
+        kExec = string(fullfile(wd,"kaleido", "kaleido.cmd"));
+        cc = "type";
+    end
+    plyJsLoc = string(fullfile(wd,"kaleido", "plotly-latest.min.js"));
 
+    if ~isfile(kExec) || ~isfile(plyJsLoc)
+        status = getKaleido();
+    else
+        status = 1;
+    end
 
-% Prepare input plotly object for Kaleido
-q=struct();
-q.data.data = pfObj.data;
-q.data.layout = pfObj.layout;
-q.data.layout = rmfield(q.data.layout,'height');
-q.data.layout = rmfield(q.data.layout,'width');
-q.format = string(imageFormat);
-q.height = height;
-q.scale = scale;
-q.width = width;
+    if status == 0
+        return
+    end
 
-pfJson = native2unicode(jsonencode(q),'UTF-8');
-tFile = string(fullfile(wd,'kaleido','temp.txt'));
-f=fopen(tFile,'w');
-fprintf(f,"%s",pfJson);
-fclose(f);
+    mjLoc = replace(string(fullfile( ...
+            wd, "kaleido", "etc", "mathjax", "MathJax.js")), "\", "/");
+    scope="plotly";
+
+    % Prepare input plotly object for Kaleido
+    q = struct();
+    q.data.data = pfObj.data;
+    q.data.layout = pfObj.layout;
+    q.data.layout = rmfield(q.data.layout, "height");
+    q.data.layout = rmfield(q.data.layout, "width");
+    q.format = string(imageFormat);
+    q.height = height;
+    q.scale = scale;
+    q.width = width;
+
+    pfJson = native2unicode(jsonencode(q), "UTF-8");
+    tFile = string(fullfile(wd, "kaleido", "temp.txt"));
+    f = fopen(tFile, "w");
+    fprintf(f, "%s", pfJson);
+    fclose(f);
 
-cmd=[cc," ",tFile," | ",kExec," ",scope," --plotlyjs='",plyJsLoc,"' ","--mathjax='file:///",mjLoc,"' --no-sandbox --disable-gpu --allow-file-access-from-files --disable-breakpad --disable-dev-shm-usage"];
+    cmd = [cc, " ", tFile, " | ", kExec, " ", scope, " --plotlyjs='", ...
+            plyJsLoc, "' ", "--mathjax='file:///",mjLoc,"' " ...
+            + "--no-sandbox --disable-gpu " ...
+            + "--allow-file-access-from-files --disable-breakpad " ...
+            + "--disable-dev-shm-usage"];
 
-if debug
-    inputCmd=char(join(cmd,''));
-    fprintf('\nDebug info:\n%s\n\n',inputCmd);
-end
+    if debug
+        inputCmd = char(join(cmd, ""));
+        fprintf("\nDebug info:\n%s\n\n", inputCmd);
+    end
 
-[code,out]=system(char(join(cmd,'')));
-if debug
-    disp(out);
-end
+    [code,out] = system(char(join(cmd, "")));
+    if debug
+        disp(out);
+    end
 
-if code ~= 0
-    fprintf('\nFatal: Failed to run Kaleido.\n\n');
-    return;
-else
-    a=string(split(out,newline));
-    if a(end)==""
-        a(end)=[];
+    if code ~= 0
+        fprintf("\nFatal: Failed to run Kaleido.\n\n");
+        return;
+    else
+        a = string(split(out,newline));
+        if a(end) == ""
+            a(end) = [];
+        end
+        output = jsondecode(a(end));
     end
-    output = jsondecode(a(end));
-end
 
-if output.code ~= 0
-    fprintf('\nError: %s\n',output.message);
-else
-    out=unicode2native(output.result,'UTF-8');
-    out=base64decode(out);
-    f=fopen(char(filename),'wb');
-    fwrite(f,out);
-    fclose(f);
+    if output.code ~= 0
+        fprintf("\nError: %s\n", output.message);
+    else
+        out = unicode2native(output.result, "UTF-8");
+        out = matlab.net.base64decode(out);
+        f = fopen(char(filename), "wb");
+        fwrite(f, out);
+        fclose(f);
+    end
 end
diff --git a/plotly/plotly_aux/base64decode.m b/plotly/plotly_aux/base64decode.m
deleted file mode 100644
index 949a075a..00000000
--- a/plotly/plotly_aux/base64decode.m
+++ /dev/null
@@ -1,117 +0,0 @@
-function y = base64decode(x, outfname, alg)
-%BASE64DECODE Perform base64 decoding on a string.
-%
-% INPUT:
-%   x    - block of data to be decoded.  Can be a string or a numeric  
-%          vector containing integers in the range 0-255. Any character
-%          not part of the 65-character base64 subset set is silently
-%          ignored.  Characters occuring after a '=' padding character are 
-%          never decoded. If the length of the string to decode (after 
-%          ignoring non-base64 chars) is not a multiple of 4, then a 
-%          warning is generated.
-%
-%   outfname - if provided the binary date from decoded string will be
-%          saved into a file. Since Base64 coding is often used to embbed
-%          binary data in xml files, this option can be used to extract and
-%          save them.
-%
-%   alg  - Algorithm to use: can take values 'java' or 'matlab'. Optional
-%          variable defaulting to 'java' which is a little faster. If 
-%          'java' is chosen than core of the code is performed by a call to
-%          a java library. Optionally all operations can be performed using
-%          matleb code. 
-%
-% OUTPUT:
-%   y    - array of binary data returned as uint8 
-%
-%   This function is used to decode strings from the Base64 encoding specified
-%   in RFC 2045 - MIME (Multipurpose Internet Mail Extensions).  The Base64
-%   encoding is designed to represent arbitrary sequences of octets in a form
-%   that need not be humanly readable.  A 65-character subset ([A-Za-z0-9+/=])
-%   of US-ASCII is used, enabling 6 bits to be represented per printable
-%   character.
-%
-%   See also BASE64ENCODE.
-%
-%   Written by Jarek Tuszynski, SAIC, jaroslaw.w.tuszynski_at_saic.com
-%
-%   Matlab version based on 2004 code by Peter J. Acklam
-%   E-mail:      pjacklam@online.no
-%   URL:         http://home.online.no/~pjacklam
-%   http://home.online.no/~pjacklam/matlab/software/util/datautil/base64encode.m
-
-if nargin<3, alg='java';  end
-if nargin<2, outfname=''; end
-
-%% if x happen to be a filename than read the file
-if (numel(x)<256)
-  if (exist(x, 'file')==2)
-    fid = fopen(x,'rb');
-    x = fread(fid, 'uint8');   
-    fclose(fid);
-  end
-end
-x = uint8(x(:)); % unify format
-
-%% Perform conversion
-switch (alg)
-  case 'java' 
-    base64 = org.apache.commons.codec.binary.Base64;
-    y = base64.decode(x);
-    y = mod(int16(y),256); % convert from int8 to uint8
-  case 'matlab'
-    %%  Perform the mapping
-    %   A-Z  ->  0  - 25
-    %   a-z  ->  26 - 51
-    %   0-9  ->  52 - 61
-    %   + -  ->  62       '-' is URL_SAFE alternative
-    %   / _  ->  63       '_' is URL_SAFE alternative
-    map = uint8(zeros(1,256)+65);
-    map(uint8(['A':'Z', 'a':'z', '0':'9', '+/=']))= 0:64;
-    map(uint8('-_'))= 62:63;  % URL_SAFE alternatives
-    x = map(x);  % mapping
-    
-    x(x>64)=[]; % remove non-base64 chars
-    if rem(numel(x), 4)
-      warning('Length of base64 data not a multiple of 4; padding input.');
-    end
-    x(x==64)=[]; % remove padding characters
-    
-    %% add padding and reshape
-    nebytes = length(x);         % number of encoded bytes
-    nchunks = ceil(nebytes/4);   % number of chunks/groups
-    if rem(nebytes, 4)>0
-      x(end+1 : 4*nchunks) = 0;  % add padding
-    end
-    x = reshape(uint8(x), 4, nchunks);
-    y = repmat(uint8(0), 3, nchunks);            % for the decoded data
-    
-    %% Rearrange every 4 bytes into 3 bytes
-    %    00aaaaaa 00bbbbbb 00cccccc 00dddddd
-    % to form
-    %    aaaaaabb bbbbcccc ccdddddd
-    y(1,:) = bitshift(x(1,:), 2);                 % 6 highest bits of y(1,:)
-    y(1,:) = bitor(y(1,:), bitshift(x(2,:), -4)); % 2 lowest  bits of y(1,:)
-    y(2,:) = bitshift(x(2,:), 4);                 % 4 highest bits of y(2,:)
-    y(2,:) = bitor(y(2,:), bitshift(x(3,:), -2)); % 4 lowest  bits of y(2,:)
-    y(3,:) = bitshift(x(3,:), 6);                 % 2 highest bits of y(3,:)
-    y(3,:) = bitor(y(3,:), x(4,:));               % 6 lowest  bits of y(3,:)
-    
-    %% remove extra padding
-    switch rem(nebytes, 4)
-      case 2
-        y = y(1:end-2);
-      case 3
-        y = y(1:end-1);
-    end
-end
-
-%% reshape to a row vector and make it a character array
-y = uint8(reshape(y, 1, numel(y)));
-
-%% save to file if needed
-if ~isempty(outfname)
-  fid = fopen(outfname,'wb');
-  fwrite(fid, y, 'uint8');  
-  fclose(fid);
-end
\ No newline at end of file
