view libinterp/corefcn/txt-latex.cc @ 17436:d415dc6ac1e2

Added horizontal/vertical alignment and rotation to latex_render class
author Andrej Lojdl <andrej.lojdl@gmail.com>
date Tue, 17 Sep 2013 16:20:47 +0200
parents 71b6f8a81e80
children fd3e999305ea
line wrap: on
line source

/*

   Copyright (C) 2013 Andrej Lojdl

   This file is part of Octave.

   Octave is free software; you can redistribute it and/or modify it
   under the terms of the GNU General Public License as published by the
   Free Software Foundation; either version 3 of the License, or (at your
   option) any later version.

   Octave is distributed in the hope that it will be useful, but WITHOUT
   ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
   FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
   for more details.

   You should have received a copy of the GNU General Public License
   along with Octave; see the file COPYING.  If not, see
   <http://www.gnu.org/licenses/>.

 */

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include <iostream>
#include <fstream>
#include <string>
#include <cstdlib>
#include <cstring>
#include <sstream>

#ifdef ENABLE_LATEX
#include <png.h>

#include "error.h"
#include "txt-latex.h"
#include "oct.h"
#include "dim-vector.h"
#include "file-ops.h"


latex_render::latex_render (void)
: bbox (1, 4, 0.0), color (dim_vector (1, 3), 0) 
{
}

latex_render::~latex_render (void)
{
}

int
latex_render::check_programs (void)
{
   int cmd, ready = 1; 
   std::string check_latex, check_dvips, check_ghostscript;
   
   check_latex = "latex  --version >/dev/null 2>/dev/null";
   check_dvips = "dvips  --version >/dev/null 2>/dev/null";
   check_ghostscript = "gs  --version >/dev/null 2>/dev/null";
   
   cmd = system (check_latex.c_str ());
   
   if (cmd != 0)
   {
      ::error("There's no LaTeX document preparation system installed on this system. Please install it and then try again.");
      ready = 0;
   }
   
   cmd = system (check_dvips.c_str ());
   
   if (cmd != 0)
   {
      ::error("There's no dvips converter installed on this system. Please install it and then try again.");
      ready = 0;
   }
   
   cmd = system (check_ghostscript.c_str ());
   
   if (cmd != 0)
   {
      ::error("There's no GhostScript program for converting installed on this system. Please install it and then try again.");
      ready = 0;
   }
   
   return ready;
}

void 
latex_render::adapter (const std::string& txt)
{
  std::ofstream file;
  std::string directory_name = "latex", tex;
  int status;

  /* Creating unique name of directory - this function should use temporary files on every system*/ 
  directory_path = octave_tempnam ("", directory_name);

  /* Creating the directory */
  status = octave_mkdir (directory_path, 0700);

  /*Check if directory is created successfully. */  
  if (status == 0)
    {
      /* Pointing to temporary directory, for creating .tex file */
      tex = file_ops::concat (directory_path, "default.tex");

      /* Creating and writing to temporary .tex file */
      file.open (tex.c_str ());
      file << "\\documentclass[fleqn]{article} \n\\usepackage{amsmath} \n\\usepackage{color} \n\\pagestyle{empty}	\n\\begin{document} \n\t\\fontsize{12pt}{12pt}\\selectfont \n\t";
      file << "\\usefont{T1}{" << font_name << "}{m}{n} \n\t\t";
      file << txt;
      file << "\n\\end{document}";
      file.close ();  
    }
  else
    {
      /* Printing error if directory is not created */
      ::error ("Failed to create temp directory.");
      std::cout << error_state << std::endl;
    }
}

uint8NDArray 
latex_render::render (void)
{
  int status;
  std::stringstream ss;
  std::string comment, string_font_size;
  uint8NDArray data;
  std::ifstream file;
  std::string sfsize, tex, aux, log, dvi, eps, png, command;

  /* Check if command processor is available */
  if(system (NULL) && error_state == 0)
    {
      log = file_ops::concat (directory_path, "default.log");
      tex = file_ops::concat (directory_path, "default.tex");
      command = "latex -interaction=nonstopmode -output-directory=" + directory_path + " " + tex + " > " + log;

      /* .tex -> .dvi */
      status = system (command.c_str ());

      if(status != 0)
        {
          data = uint8NDArray (dim_vector (4, 1, 1), static_cast<uint8_t> (0));

          ::error ("LaTeX converting .tex to .dvi file, failed.");

          file.open (log.c_str ());

          for(int i=0; i<33; i++)
            {
              std::getline (file, comment);
            }

          /* Print additional info from default.log */
          comment = "Here is LaTeX description of error:";
          std::cout << comment << std::endl;
          std::getline (file, comment);
          std::cout << comment << std::endl;
          std::getline (file, comment); 
          std::cout << comment << std::endl; 
          file.close ();       

          return data;         
        }

      /* .dvi -> .eps */  
      dvi = file_ops::concat (directory_path, "default.dvi"); 
      eps = file_ops::concat (directory_path, "default.eps");
      command = "dvips " + dvi + " -E -o " + eps + " -q";

      status = system (command.c_str ());

      if(status != 0)
        {
          data = uint8NDArray (dim_vector (4, 1, 1), static_cast<uint8_t> (0));

          ::error ("dvips converting .dvi to .eps file file, failed");

          return data;  
        }

      /* .eps -> .png */
      font_size = font_size * (72 / 12);

      ss << font_size;
      ss >> string_font_size;

      png = file_ops::concat (directory_path, "default.png");
      command = "gs -q -dEPSCrop -dSAFER -dBATCH -dNOPAUSE -r" + string_font_size + " -dTextAlphaBits=4 -sDEVICE=pngalpha -sOutputFile=" + png + " \"" + eps + "\" "; 

      status = system (command.c_str ());

      if(status != 0)
        {
          data = uint8NDArray (dim_vector (4, 1, 1), static_cast<uint8_t> (0));

          ::error ("GhostScript converting .eps to .png file, failed");

          return data; 
        }

      /* Read image from PNG file */
      int x;
      int y;

      int width, height;
      png_byte color_type;
      png_byte bit_depth;

      png_structp png_ptr;
      png_infop info_ptr;
      int number_of_passes;
      png_bytep * row_pointers;

      char header [8];

      /* Open file and test for it being a png */
      FILE *fp = fopen (png.c_str (), "rb");
      if (!fp)
        {
          ::error ("File default.png could not be opened for reading");
        }
      fread (header, 1, 8, fp);


      /* Initialize stuff */
      png_ptr = png_create_read_struct (PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);

      if (!png_ptr)
        {
          ::error ("png_create_read_struct failed");
        }

      info_ptr = png_create_info_struct (png_ptr);
      if (!info_ptr)
        {
          ::error ("png_create_info_struct failed");
        }

      if (setjmp (png_jmpbuf (png_ptr)))
        {
          ::error ("Error during init_io");
        }

      png_init_io (png_ptr, fp);
      png_set_sig_bytes (png_ptr, 8);

      png_read_info (png_ptr, info_ptr);

      width = png_get_image_width (png_ptr, info_ptr);
      height = png_get_image_height (png_ptr, info_ptr);
      color_type = png_get_color_type(png_ptr, info_ptr);
      bit_depth = png_get_bit_depth (png_ptr, info_ptr);

      number_of_passes = png_set_interlace_handling (png_ptr);
      png_read_update_info (png_ptr, info_ptr);

      /* Read file */
      if (setjmp (png_jmpbuf (png_ptr)))
        {
          ::error ("Error during read_image");
        }

      row_pointers = (png_bytep*) malloc (sizeof (png_bytep) * height);
      for (y=0; y<height; y++)
        row_pointers[y] = (png_byte*) malloc (png_get_rowbytes (png_ptr, info_ptr));

      png_read_image (png_ptr, row_pointers);

      fclose (fp);

      /* Instantise data array */
      data = uint8NDArray (dim_vector (4, width, height), static_cast<uint8_t> (0));

      for (int i=0; i<height; i++)
        { 
          png_byte* row = row_pointers[height - 1 - i];
          for (int j=0; j<width; j++)
            {
              png_byte* ptr = &(row[j * 4]);

              /* If the color of text is black we can change it, if not we render the original color. */
              if(ptr[0] < 50 && ptr[1] < 50 && ptr[2] < 50 )
                {
                  data(0, j, i) = color(0);
                  data(1, j, i) = color(1);
                  data(2, j, i) = color(2);
                  data(3, j, i) = ptr[3];
                }
              else
                {
                  data(0, j, i) = ptr[0];
                  data(1, j, i) = ptr[1];
                  data(2, j, i) = ptr[2];
                  data(3, j, i) = ptr[3];                  	
                }
            }
        }

      /* Remove the temporary directory. */  
      if( !directory_path.empty ())         
        status = octave_recursive_rmdir (directory_path);
    }
  else
    {
      /* It's possible that command procesor is not
         available and we already made a temporary dir*/  
      if( !directory_path.empty ())         
        status = octave_recursive_rmdir (directory_path);

      data = uint8NDArray (dim_vector (4, 1, 1), static_cast<uint8_t> (0));

      if(error_state == 1)
        ::warning ("LaTeX interpreter can't proceed, please try again.");
      else
        ::warning ("Command processor not ready yet, please try again."); 
    }  

  return data;
}

void
latex_render::set_font (const std::string& name, const std::string& weight,
                        const std::string& angle, double size)
{
  font_size = size;
  if(name[0] == '*')
    font_name = "cmr";
  else
    font_name = name;
}

void
latex_render::set_color (Matrix c)
{
  if (c.numel () == 3)
    {
      color(0) = static_cast<uint8_t> (c (0) * 255);
      color(1) = static_cast<uint8_t> (c (1) * 255);
      color(2) = static_cast<uint8_t> (c (2) * 255);
    }
  else
    ::warning ("latex_render::set_color: invalid color");
}

int
latex_render::rotation_to_mode (double rotation) const
{
  if (rotation == 0.0)
    return ROTATION_0;
  else if (rotation == 90.0)
    return ROTATION_90;
  else if (rotation == 180.0)
    return ROTATION_180;
  else if (rotation == 270.0)
    return ROTATION_270;
  else
    return ROTATION_0;
}


void
latex_render::text_to_pixels (const std::string& txt,
                              uint8NDArray& pixels, Matrix& bbox,
                              int halign, int valign, double rotation)
{
  if (check_programs () == 1)
  {
     adapter (txt);
     pixels = render();
  }
  else
  {
     pixels = uint8NDArray (dim_vector (4, 10, 10), static_cast<uint8_t> (0));
  }

  if(pixels.ndims () < 3)
    ::warning ("Pixels variable not properly set.");
  else
    {
      bbox = Matrix (1, 4, 0.0);
      bbox (2) = pixels.dim2 ();
      bbox (3) = pixels.dim3 ();
      
      switch (halign)
      {
        default: bbox(0) = 0; break;
        case 1: bbox(0) = -bbox(2)/2; break;
        case 2: bbox(0) = -bbox(2); break;
      }
      switch (valign)
      {
        default: bbox(1) = 0; break;
        case 1: bbox(1) = -bbox(3)/2; break;
        case 2: bbox(1) = -bbox(3); break;
        case 3: break;
        case 4: bbox(1) = -bbox(3)-bbox(1); break;
      }
      
      int rot_mode = rotation_to_mode (rotation);
      
      switch (rot_mode)
      {
      case ROTATION_90:
        std::swap (bbox(0), bbox(1));
        std::swap (bbox(2), bbox(3));
        bbox(0) = -bbox(0)-bbox(2);
        break;
      case ROTATION_180:
        bbox(0) = -bbox(0)-bbox(2);
        bbox(1) = -bbox(1)-bbox(3);
        break;
      case ROTATION_270:
        std::swap (bbox(0), bbox(1));
        std::swap (bbox(2), bbox(3));
        bbox(1) = -bbox(1)-bbox(3);
        break;
      }
    }
    
    /* Rotating pixels */
    int rot_mode = rotation_to_mode (rotation);
    
    if (pixels.numel () > 0)
    {
      switch (rot_mode)
        {
        case ROTATION_0:
          break;
        case ROTATION_90:
            {
              Array<octave_idx_type> perm (dim_vector (3, 1));
              perm(0) = 0;
              perm(1) = 2;
              perm(2) = 1;
              pixels = pixels.permute (perm);

              Array<idx_vector> idx (dim_vector (3, 1));
              idx(0) = idx_vector (':');
              idx(1) = idx_vector (pixels.dim2 ()-1, -1, -1);
              idx(2) = idx_vector (':');
              pixels = uint8NDArray (pixels.index (idx));
            }
          break;
        case ROTATION_180:
            {
              Array<idx_vector> idx (dim_vector (3, 1));
              idx(0) = idx_vector (':');
              idx(1) = idx_vector (pixels.dim2 ()-1, -1, -1);
              idx(2)=  idx_vector (pixels.dim3 ()-1, -1, -1);
              pixels = uint8NDArray (pixels.index (idx));
            }
          break;
        case ROTATION_270:
            {
              Array<octave_idx_type> perm (dim_vector (3, 1));
              perm(0) = 0;
              perm(1) = 2;
              perm(2) = 1;
              pixels = pixels.permute (perm);

              Array<idx_vector> idx (dim_vector (3, 1));
              idx(0) = idx_vector (':');
              idx(1) = idx_vector (':');
              idx(2) = idx_vector (pixels.dim3 ()-1, -1, -1);
              pixels = uint8NDArray (pixels.index (idx));
            }
          break;
        }
      }
}

Matrix
latex_render::get_extent (text_element *elt, double rotation)
{
  Matrix extent (1, 2, 0.0);

  return extent;
}


Matrix 
latex_render::get_extent (const std::string& txt, double rotation)
{
  text_element *elt = text_parser_none ().parse (txt);
  Matrix extent = get_extent (elt, rotation);
  delete elt;

  return extent;
}
#endif //ENABLE_LATEX