changeset 31:111689a2c177

author Sebastien Jodogne <s.jodogne@gmail.com>
date Wed, 24 Jun 2015 16:20:41 +0200
parents ad7b6757965a
children c6ac4deb303b
files CMakeLists.txt Orthanc/Core/ImageFormats/PngReader.cpp Orthanc/Core/ImageFormats/PngReader.h Orthanc/Resources/CMake/AutoGeneratedCode.cmake Orthanc/Resources/CMake/Compiler.cmake Orthanc/Resources/EmbedResources.py Plugin/DecodedImageAdapter.cpp Plugin/ParsedDicomImage.cpp Plugin/ParsedDicomImage.h Plugin/Plugin.cpp Resources/EmbedResources.py Resources/SyncOrthancFolder.py
diffstat 12 files changed, 1070 insertions(+), 550 deletions(-) [+]
line wrap: on
line diff
--- a/CMakeLists.txt	Thu Jun 04 10:18:43 2015 +0200
+++ b/CMakeLists.txt	Wed Jun 24 16:20:41 2015 +0200
@@ -130,6 +130,7 @@
   ${CMAKE_SOURCE_DIR}/Orthanc/Core/ImageFormats/ImageAccessor.cpp
   ${CMAKE_SOURCE_DIR}/Orthanc/Core/ImageFormats/ImageBuffer.cpp
   ${CMAKE_SOURCE_DIR}/Orthanc/Core/ImageFormats/ImageProcessing.cpp
+  ${CMAKE_SOURCE_DIR}/Orthanc/Core/ImageFormats/PngReader.cpp
   ${CMAKE_SOURCE_DIR}/Orthanc/Core/ImageFormats/PngWriter.cpp
   ${CMAKE_SOURCE_DIR}/Orthanc/Core/MultiThreading/SharedMessageQueue.cpp
   ${CMAKE_SOURCE_DIR}/Orthanc/Core/OrthancException.cpp
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Orthanc/Core/ImageFormats/PngReader.cpp	Wed Jun 24 16:20:41 2015 +0200
@@ -0,0 +1,313 @@
+/**
+ * Orthanc - A Lightweight, RESTful DICOM Store
+ * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ *
+ * This program 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.
+ *
+ * In addition, as a special exception, the copyright holders of this
+ * program give permission to link the code of its release with the
+ * OpenSSL project's "OpenSSL" library (or with modified versions of it
+ * that use the same license as the "OpenSSL" library), and distribute
+ * the linked executables. You must obey the GNU General Public License
+ * in all respects for all of the code used other than "OpenSSL". If you
+ * modify file(s) with this exception, you may extend this exception to
+ * your version of the file(s), but you are not obligated to do so. If
+ * you do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source files
+ * in the program, then also delete it here.
+ * 
+ * This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+
+#include "../PrecompiledHeaders.h"
+#include "PngReader.h"
+
+#include "../OrthancException.h"
+#include "../Toolbox.h"
+
+#include <png.h>
+#include <string.h>  // For memcpy()
+
+namespace Orthanc
+{
+  namespace 
+  {
+    struct FileRabi
+    {
+      FILE* fp_;
+
+      FileRabi(const char* filename)
+      {
+        fp_ = fopen(filename, "rb");
+        if (!fp_)
+        {
+          throw OrthancException(ErrorCode_InexistentFile);
+        }
+      }
+
+      ~FileRabi()
+      {
+        if (fp_)
+          fclose(fp_);
+      }
+    };
+  }
+
+
+  struct PngReader::PngRabi
+  {
+    png_structp png_;
+    png_infop info_;
+    png_infop endInfo_;
+
+    void Destruct()
+    {
+      if (png_)
+      {
+        png_destroy_read_struct(&png_, &info_, &endInfo_);
+
+        png_ = NULL;
+        info_ = NULL;
+        endInfo_ = NULL;
+      }
+    }
+
+    PngRabi()
+    {
+      png_ = NULL;
+      info_ = NULL;
+      endInfo_ = NULL;
+
+      png_ = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
+      if (!png_)
+      {
+        throw OrthancException(ErrorCode_NotEnoughMemory);
+      }
+
+      info_ = png_create_info_struct(png_);
+      if (!info_)
+      {
+        png_destroy_read_struct(&png_, NULL, NULL);
+        throw OrthancException(ErrorCode_NotEnoughMemory);
+      }
+
+      endInfo_ = png_create_info_struct(png_);
+      if (!info_)
+      {
+        png_destroy_read_struct(&png_, &info_, NULL);
+        throw OrthancException(ErrorCode_NotEnoughMemory);
+      }
+    }
+
+    ~PngRabi()
+    {
+      Destruct();
+    }
+
+    static void MemoryCallback(png_structp png_ptr, 
+                               png_bytep data, 
+                               png_size_t size);
+  };
+
+
+  void PngReader::CheckHeader(const void* header)
+  {
+    int is_png = !png_sig_cmp((png_bytep) header, 0, 8);
+    if (!is_png)
+    {
+      throw OrthancException(ErrorCode_BadFileFormat);
+    }
+  }
+
+  PngReader::PngReader()
+  {
+  }
+
+  void PngReader::Read(PngRabi& rabi)
+  {
+    png_set_sig_bytes(rabi.png_, 8);
+
+    png_read_info(rabi.png_, rabi.info_);
+
+    png_uint_32 width, height;
+    int bit_depth, color_type, interlace_type;
+    int compression_type, filter_method;
+    // get size and bit-depth of the PNG-image
+    png_get_IHDR(rabi.png_, rabi.info_,
+                 &width, &height,
+                 &bit_depth, &color_type, &interlace_type,
+                 &compression_type, &filter_method);
+
+    PixelFormat format;
+    unsigned int pitch;
+
+    if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth == 8)
+    {
+      format = PixelFormat_Grayscale8;
+      pitch = width;
+    }
+    else if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth == 16)
+    {
+      format = PixelFormat_Grayscale16;
+      pitch = 2 * width;
+
+      if (Toolbox::DetectEndianness() == Endianness_Little)
+      {
+        png_set_swap(rabi.png_);
+      }
+    }
+    else if (color_type == PNG_COLOR_TYPE_RGB && bit_depth == 8)
+    {
+      format = PixelFormat_RGB24;
+      pitch = 3 * width;
+    }
+    else if (color_type == PNG_COLOR_TYPE_RGBA && bit_depth == 8)
+    {
+      format = PixelFormat_RGBA32;
+      pitch = 4 * width;
+    }
+    else
+    {
+      throw OrthancException(ErrorCode_NotImplemented);
+    }
+
+    data_.resize(height * pitch);
+
+    if (height == 0 || width == 0)
+    {
+      // Empty image, we are done
+      AssignEmpty(format);
+      return;
+    }
+    
+    png_read_update_info(rabi.png_, rabi.info_);
+
+    std::vector<png_bytep> rows(height);
+    for (size_t i = 0; i < height; i++)
+    {
+      rows[i] = &data_[0] + i * pitch;
+    }
+
+    png_read_image(rabi.png_, &rows[0]);
+
+    AssignReadOnly(format, width, height, pitch, &data_[0]);
+  }
+
+  void PngReader::ReadFromFile(const char* filename)
+  {
+    FileRabi f(filename);
+
+    char header[8];
+    if (fread(header, 1, 8, f.fp_) != 8)
+    {
+      throw OrthancException(ErrorCode_BadFileFormat);
+    }
+
+    CheckHeader(header);
+
+    PngRabi rabi;
+
+    if (setjmp(png_jmpbuf(rabi.png_)))
+    {
+      rabi.Destruct();
+      throw OrthancException(ErrorCode_BadFileFormat);
+    }
+
+    png_init_io(rabi.png_, f.fp_);
+
+    Read(rabi);
+  }
+
+
+  namespace
+  {
+    struct MemoryBuffer
+    {
+      const uint8_t* buffer_;
+      size_t size_;
+      size_t pos_;
+      bool ok_;
+    };
+  }
+
+
+  void PngReader::PngRabi::MemoryCallback(png_structp png_ptr, 
+                                          png_bytep outBytes, 
+                                          png_size_t byteCountToRead)
+  {
+    MemoryBuffer* from = reinterpret_cast<MemoryBuffer*>(png_get_io_ptr(png_ptr));
+
+    if (!from->ok_)
+    {
+      return;
+    }
+
+    if (from->pos_ + byteCountToRead > from->size_)
+    {
+      from->ok_ = false;
+      return;
+    }
+
+    memcpy(outBytes, from->buffer_ + from->pos_, byteCountToRead);
+
+    from->pos_ += byteCountToRead;
+  }
+
+
+  void PngReader::ReadFromMemory(const void* buffer,
+                                 size_t size)
+  {
+    if (size < 8)
+    {
+      throw OrthancException(ErrorCode_BadFileFormat);
+    }
+
+    CheckHeader(buffer);
+
+    PngRabi rabi;
+
+    if (setjmp(png_jmpbuf(rabi.png_)))
+    {
+      rabi.Destruct();
+      throw OrthancException(ErrorCode_BadFileFormat);
+    }
+
+    MemoryBuffer tmp;
+    tmp.buffer_ = reinterpret_cast<const uint8_t*>(buffer) + 8;  // We skip the header
+    tmp.size_ = size - 8;
+    tmp.pos_ = 0;
+    tmp.ok_ = true;
+
+    png_set_read_fn(rabi.png_, &tmp, PngRabi::MemoryCallback);
+
+    Read(rabi);
+
+    if (!tmp.ok_)
+    {
+      throw OrthancException(ErrorCode_BadFileFormat);
+    }
+  }
+
+  void PngReader::ReadFromMemory(const std::string& buffer)
+  {
+    if (buffer.size() != 0)
+    {
+      ReadFromMemory(&buffer[0], buffer.size());
+    }
+    else
+    {
+      ReadFromMemory(NULL, 0);
+    }
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Orthanc/Core/ImageFormats/PngReader.h	Wed Jun 24 16:20:41 2015 +0200
@@ -0,0 +1,71 @@
+/**
+ * Orthanc - A Lightweight, RESTful DICOM Store
+ * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+ * Department, University Hospital of Liege, Belgium
+ *
+ * This program 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.
+ *
+ * In addition, as a special exception, the copyright holders of this
+ * program give permission to link the code of its release with the
+ * OpenSSL project's "OpenSSL" library (or with modified versions of it
+ * that use the same license as the "OpenSSL" library), and distribute
+ * the linked executables. You must obey the GNU General Public License
+ * in all respects for all of the code used other than "OpenSSL". If you
+ * modify file(s) with this exception, you may extend this exception to
+ * your version of the file(s), but you are not obligated to do so. If
+ * you do not wish to do so, delete this exception statement from your
+ * version. If you delete this exception statement from all source files
+ * in the program, then also delete it here.
+ * 
+ * This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+
+#pragma once
+
+#include "ImageAccessor.h"
+
+#include "../Enumerations.h"
+
+#include <vector>
+#include <stdint.h>
+#include <boost/shared_ptr.hpp>
+
+namespace Orthanc
+{
+  class PngReader : public ImageAccessor
+  {
+  private:
+    struct PngRabi;
+
+    std::vector<uint8_t> data_;
+
+    void CheckHeader(const void* header);
+
+    void Read(PngRabi& rabi);
+
+  public:
+    PngReader();
+
+    void ReadFromFile(const char* filename);
+
+    void ReadFromFile(const std::string& filename)
+    {
+      ReadFromFile(filename.c_str());
+    }
+
+    void ReadFromMemory(const void* buffer,
+                        size_t size);
+
+    void ReadFromMemory(const std::string& buffer);
+  };
+}
--- a/Orthanc/Resources/CMake/AutoGeneratedCode.cmake	Thu Jun 04 10:18:43 2015 +0200
+++ b/Orthanc/Resources/CMake/AutoGeneratedCode.cmake	Wed Jun 24 16:20:41 2015 +0200
@@ -27,11 +27,11 @@
     "${TARGET_BASE}.cpp"
     COMMAND 
     ${PYTHON_EXECUTABLE}
-    "${CMAKE_CURRENT_SOURCE_DIR}/Resources/EmbedResources.py"
+    "${ORTHANC_ROOT}/Resources/EmbedResources.py"
     "${AUTOGENERATED_DIR}/EmbeddedResources"
     ${SCRIPT_ARGUMENTS}
     DEPENDS
-    "${CMAKE_CURRENT_SOURCE_DIR}/Resources/EmbedResources.py"
+    "${ORTHANC_ROOT}/Resources/EmbedResources.py"
     ${DEPENDENCIES}
     )
 
--- a/Orthanc/Resources/CMake/Compiler.cmake	Thu Jun 04 10:18:43 2015 +0200
+++ b/Orthanc/Resources/CMake/Compiler.cmake	Wed Jun 24 16:20:41 2015 +0200
@@ -78,6 +78,7 @@
   if (CMAKE_COMPILER_IS_GNUCXX)
     # This is a patch for MinGW64
     SET(CMAKE_EXE_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,--allow-multiple-definition -static-libgcc -static-libstdc++")
+    SET(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,--allow-multiple-definition -static-libgcc -static-libstdc++")
 
     CHECK_LIBRARY_EXISTS(winpthread pthread_create "" HAVE_WIN_PTHREAD)
     if (HAVE_WIN_PTHREAD)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Orthanc/Resources/EmbedResources.py	Wed Jun 24 16:20:41 2015 +0200
@@ -0,0 +1,408 @@
+# Orthanc - A Lightweight, RESTful DICOM Store
+# Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
+# Department, University Hospital of Liege, Belgium
+#
+# This program 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.
+#
+# In addition, as a special exception, the copyright holders of this
+# program give permission to link the code of its release with the
+# OpenSSL project's "OpenSSL" library (or with modified versions of it
+# that use the same license as the "OpenSSL" library), and distribute
+# the linked executables. You must obey the GNU General Public License
+# in all respects for all of the code used other than "OpenSSL". If you
+# modify file(s) with this exception, you may extend this exception to
+# your version of the file(s), but you are not obligated to do so. If
+# you do not wish to do so, delete this exception statement from your
+# version. If you delete this exception statement from all source files
+# in the program, then also delete it here.
+# 
+# This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
+
+
+import sys
+import os
+import os.path
+import pprint
+import re
+
+UPCASE_CHECK = True
+ARGS = []
+for i in range(len(sys.argv)):
+    if not sys.argv[i].startswith('--'):
+        ARGS.append(sys.argv[i])
+    elif sys.argv[i].lower() == '--no-upcase-check':
+        UPCASE_CHECK = False
+
+if len(ARGS) < 2 or len(ARGS) % 2 != 0:
+    print ('Usage:')
+    print ('python %s [--no-upcase-check] <TargetBaseFilename> [ <Name> <Source> ]*' % sys.argv[0])
+    exit(-1)
+
+TARGET_BASE_FILENAME = ARGS[1]
+SOURCES = ARGS[2:]
+
+try:
+    # Make sure the destination directory exists
+    os.makedirs(os.path.normpath(os.path.join(TARGET_BASE_FILENAME, '..')))
+except:
+    pass
+
+
+#####################################################################
+## Read each resource file
+#####################################################################
+
+def CheckNoUpcase(s):
+    global UPCASE_CHECK
+    if (UPCASE_CHECK and
+        re.search('[A-Z]', s) != None):
+        raise Exception("Path in a directory with an upcase letter: %s" % s)
+
+resources = {}
+
+counter = 0
+i = 0
+while i < len(SOURCES):
+    resourceName = SOURCES[i].upper()
+    pathName = SOURCES[i + 1]
+
+    if not os.path.exists(pathName):
+        raise Exception("Non existing path: %s" % pathName)
+
+    if resourceName in resources:
+        raise Exception("Twice the same resource: " + resourceName)
+    
+    if os.path.isdir(pathName):
+        # The resource is a directory: Recursively explore its files
+        content = {}
+        for root, dirs, files in os.walk(pathName):
+            base = os.path.relpath(root, pathName)
+
+            # Fix issue #24 (Build fails on OSX when directory has .DS_Store files):
+            # Ignore folders whose name starts with a dot (".")
+            if base.find('/.') != -1:
+                print('Ignoring folder: %s' % root)
+                continue
+
+            for f in files:
+                if f.find('~') == -1:  # Ignore Emacs backup files
+                    if base == '.':
+                        r = f
+                    else:
+                        r = os.path.join(base, f)
+
+                    CheckNoUpcase(r)
+                    r = '/' + r.replace('\\', '/')
+                    if r in content:
+                        raise Exception("Twice the same filename (check case): " + r)
+
+                    content[r] = {
+                        'Filename' : os.path.join(root, f),
+                        'Index' : counter
+                        }
+                    counter += 1
+
+        resources[resourceName] = {
+            'Type' : 'Directory',
+            'Files' : content
+            }
+
+    elif os.path.isfile(pathName):
+        resources[resourceName] = {
+            'Type' : 'File',
+            'Index' : counter,
+            'Filename' : pathName
+            }
+        counter += 1
+
+    else:
+        raise Exception("Not a regular file, nor a directory: " + pathName)
+
+    i += 2
+
+#pprint.pprint(resources)
+
+
+#####################################################################
+## Write .h header
+#####################################################################
+
+header = open(TARGET_BASE_FILENAME + '.h', 'w')
+
+header.write("""
+#pragma once
+
+#include <string>
+#include <list>
+
+namespace Orthanc
+{
+  namespace EmbeddedResources
+  {
+    enum FileResourceId
+    {
+""")
+
+isFirst = True
+for name in resources:
+    if resources[name]['Type'] == 'File':
+        if isFirst:
+            isFirst = False
+        else:    
+            header.write(',\n')
+        header.write('      %s' % name)
+
+header.write("""
+    };
+
+    enum DirectoryResourceId
+    {
+""")
+
+isFirst = True
+for name in resources:
+    if resources[name]['Type'] == 'Directory':
+        if isFirst:
+            isFirst = False
+        else:    
+            header.write(',\n')
+        header.write('      %s' % name)
+
+header.write("""
+    };
+
+    const void* GetFileResourceBuffer(FileResourceId id);
+    size_t GetFileResourceSize(FileResourceId id);
+    void GetFileResource(std::string& result, FileResourceId id);
+
+    const void* GetDirectoryResourceBuffer(DirectoryResourceId id, const char* path);
+    size_t GetDirectoryResourceSize(DirectoryResourceId id, const char* path);
+    void GetDirectoryResource(std::string& result, DirectoryResourceId id, const char* path);
+
+    void ListResources(std::list<std::string>& result, DirectoryResourceId id);
+  }
+}
+""")
+header.close()
+
+
+
+#####################################################################
+## Write the resource content in the .cpp source
+#####################################################################
+
+PYTHON_MAJOR_VERSION = sys.version_info[0]
+
+def WriteResource(cpp, item):
+    cpp.write('    static const uint8_t resource%dBuffer[] = {' % item['Index'])
+
+    f = open(item['Filename'], "rb")
+    content = f.read()
+    f.close()
+
+    # http://stackoverflow.com/a/1035360
+    pos = 0
+    for b in content:
+        if PYTHON_MAJOR_VERSION == 2:
+            c = ord(b[0])
+        else:
+            c = b
+
+        if pos > 0:
+            cpp.write(', ')
+
+        if (pos % 16) == 0:
+            cpp.write('\n    ')
+
+        if c < 0:
+            raise Exception("Internal error")
+
+        cpp.write("0x%02x" % c)
+        pos += 1
+
+    # Zero-size array are disallowed, so we put one single void character in it.
+    if pos == 0:
+        cpp.write('  0')
+
+    cpp.write('  };\n')
+    cpp.write('    static const size_t resource%dSize = %d;\n' % (item['Index'], pos))
+
+
+cpp = open(TARGET_BASE_FILENAME + '.cpp', 'w')
+
+cpp.write("""
+#include "%s.h"
+#include "%s/Core/OrthancException.h"
+
+#include <stdint.h>
+#include <string.h>
+
+namespace Orthanc
+{
+  namespace EmbeddedResources
+  {
+""" % (os.path.basename(TARGET_BASE_FILENAME),
+       os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))))
+
+
+for name in resources:
+    if resources[name]['Type'] == 'File':
+        WriteResource(cpp, resources[name])
+    else:
+        for f in resources[name]['Files']:
+            WriteResource(cpp, resources[name]['Files'][f])
+
+
+
+#####################################################################
+## Write the accessors to the file resources in .cpp
+#####################################################################
+
+cpp.write("""
+    const void* GetFileResourceBuffer(FileResourceId id)
+    {
+      switch (id)
+      {
+""")
+for name in resources:
+    if resources[name]['Type'] == 'File':
+        cpp.write('      case %s:\n' % name)
+        cpp.write('        return resource%dBuffer;\n' % resources[name]['Index'])
+
+cpp.write("""
+      default:
+        throw OrthancException(ErrorCode_ParameterOutOfRange);
+      }
+    }
+
+    size_t GetFileResourceSize(FileResourceId id)
+    {
+      switch (id)
+      {
+""")
+
+for name in resources:
+    if resources[name]['Type'] == 'File':
+        cpp.write('      case %s:\n' % name)
+        cpp.write('        return resource%dSize;\n' % resources[name]['Index'])
+
+cpp.write("""
+      default:
+        throw OrthancException(ErrorCode_ParameterOutOfRange);
+      }
+    }
+""")
+
+
+
+#####################################################################
+## Write the accessors to the directory resources in .cpp
+#####################################################################
+
+cpp.write("""
+    const void* GetDirectoryResourceBuffer(DirectoryResourceId id, const char* path)
+    {
+      switch (id)
+      {
+""")
+
+for name in resources:
+    if resources[name]['Type'] == 'Directory':
+        cpp.write('      case %s:\n' % name)
+        isFirst = True
+        for path in resources[name]['Files']:
+            cpp.write('        if (!strcmp(path, "%s"))\n' % path)
+            cpp.write('          return resource%dBuffer;\n' % resources[name]['Files'][path]['Index'])
+        cpp.write('        throw OrthancException("Unknown path in a directory resource");\n\n')
+
+cpp.write("""      default:
+        throw OrthancException(ErrorCode_ParameterOutOfRange);
+      }
+    }
+
+    size_t GetDirectoryResourceSize(DirectoryResourceId id, const char* path)
+    {
+      switch (id)
+      {
+""")
+
+for name in resources:
+    if resources[name]['Type'] == 'Directory':
+        cpp.write('      case %s:\n' % name)
+        isFirst = True
+        for path in resources[name]['Files']:
+            cpp.write('        if (!strcmp(path, "%s"))\n' % path)
+            cpp.write('          return resource%dSize;\n' % resources[name]['Files'][path]['Index'])
+        cpp.write('        throw OrthancException("Unknown path in a directory resource");\n\n')
+
+cpp.write("""      default:
+        throw OrthancException(ErrorCode_ParameterOutOfRange);
+      }
+    }
+""")
+
+
+
+
+#####################################################################
+## List the resources in a directory
+#####################################################################
+
+cpp.write("""
+    void ListResources(std::list<std::string>& result, DirectoryResourceId id)
+    {
+      result.clear();
+
+      switch (id)
+      {
+""")
+
+for name in resources:
+    if resources[name]['Type'] == 'Directory':
+        cpp.write('      case %s:\n' % name)
+        for path in sorted(resources[name]['Files']):
+            cpp.write('        result.push_back("%s");\n' % path)
+        cpp.write('        break;\n\n')
+
+cpp.write("""      default:
+        throw OrthancException(ErrorCode_ParameterOutOfRange);
+      }
+    }
+""")
+
+
+
+
+#####################################################################
+## Write the convenience wrappers in .cpp
+#####################################################################
+
+cpp.write("""
+    void GetFileResource(std::string& result, FileResourceId id)
+    {
+      size_t size = GetFileResourceSize(id);
+      result.resize(size);
+      if (size > 0)
+        memcpy(&result[0], GetFileResourceBuffer(id), size);
+    }
+
+    void GetDirectoryResource(std::string& result, DirectoryResourceId id, const char* path)
+    {
+      size_t size = GetDirectoryResourceSize(id, path);
+      result.resize(size);
+      if (size > 0)
+        memcpy(&result[0], GetDirectoryResourceBuffer(id, path), size);
+    }
+  }
+}
+""")
+cpp.close()
--- a/Plugin/DecodedImageAdapter.cpp	Thu Jun 04 10:18:43 2015 +0200
+++ b/Plugin/DecodedImageAdapter.cpp	Wed Jun 24 16:20:41 2015 +0200
@@ -84,39 +84,32 @@
       return false;
     }
 
-    std::string file = "/instances/" + instanceId + "/file";
-
-    std::string dicom;
-    if (!GetStringFromOrthanc(dicom, context_, file))
-    {
-      return false;
-    }
-
-    ParsedDicomImage image(dicom);
+    ParsedDicomImage image(context_, instanceId);
 
     Json::Value json;
+    bool ok = false;
 
     if (type == CompressionType_Deflate)
     {
-      if (!image.EncodeUsingDeflate(json, 9))
-      {
-        return false;
-      }
+      ok = image.EncodeUsingDeflate(json, 9);
     }
     else if (type == CompressionType_Jpeg)
     {
-      if (!image.EncodeUsingJpeg(json, level))
-      {
-        return false;
-      }
+      ok = image.EncodeUsingJpeg(json, level);
+    }
+
+    if (ok)
+    {
+      Json::FastWriter writer;
+      content = writer.write(json);
+      return true;
     }
     else
     {
+      char msg[1024];
+      sprintf(msg, "Unable to decode the following instance: %s", uri.c_str());
+      OrthancPluginLogWarning(context_, msg);
       return false;
     }
-
-    Json::FastWriter writer;
-    content = writer.write(json);
-    return true;
   }
 }
--- a/Plugin/ParsedDicomImage.cpp	Thu Jun 04 10:18:43 2015 +0200
+++ b/Plugin/ParsedDicomImage.cpp	Wed Jun 24 16:20:41 2015 +0200
@@ -24,6 +24,7 @@
 #include "../Orthanc/Core/Toolbox.h"
 #include "../Orthanc/Core/ImageFormats/ImageProcessing.h"
 #include "../Orthanc/Core/ImageFormats/ImageBuffer.h"
+#include "../Orthanc/Core/ImageFormats/PngReader.h"
 #include "JpegWriter.h"
 #include "ViewerToolbox.h"
 
@@ -38,12 +39,176 @@
 
 namespace OrthancPlugins
 {
-  struct ParsedDicomImage::PImpl
+  class ParsedDicomImage::PImpl
   {
+  private:
+    OrthancPluginContext* context_;
+    std::string instanceId_;
     gdcm::ImageReader reader_;
     std::auto_ptr<gdcm::ImageChangePhotometricInterpretation> photometric_;
     std::auto_ptr<gdcm::ImageChangePlanarConfiguration> interleaved_;
     std::string decoded_;
+    Orthanc::PngReader png_;
+    bool insidePng_;
+    bool isDecoded_;
+
+    bool DecodeUsingGdcm()
+    {
+      // Change photometric interpretation, if required
+      {
+        const gdcm::Image& image = GetImage();
+        if (image.GetPixelFormat().GetSamplesPerPixel() == 1)
+        {
+          if (image.GetPhotometricInterpretation() != gdcm::PhotometricInterpretation::MONOCHROME1 &&
+              image.GetPhotometricInterpretation() != gdcm::PhotometricInterpretation::MONOCHROME2)
+          {
+            photometric_.reset(new gdcm::ImageChangePhotometricInterpretation());
+            photometric_->SetInput(image);
+            photometric_->SetPhotometricInterpretation(gdcm::PhotometricInterpretation::MONOCHROME2);
+            if (!photometric_->Change() ||
+                GetImage().GetPhotometricInterpretation() != gdcm::PhotometricInterpretation::MONOCHROME2)
+            {
+              OrthancPluginLogWarning(context_, "GDCM cannot change the photometric interpretation");
+              return false;
+            }
+          }      
+        }
+        else 
+        {
+          if (image.GetPixelFormat().GetSamplesPerPixel() == 3 &&
+              image.GetPhotometricInterpretation() != gdcm::PhotometricInterpretation::RGB)
+          {
+            photometric_.reset(new gdcm::ImageChangePhotometricInterpretation());
+            photometric_->SetInput(image);
+            photometric_->SetPhotometricInterpretation(gdcm::PhotometricInterpretation::RGB);
+            if (!photometric_->Change() ||
+                GetImage().GetPhotometricInterpretation() != gdcm::PhotometricInterpretation::RGB)
+            {
+              OrthancPluginLogWarning(context_, "GDCM cannot change the photometric interpretation");
+              return false;
+            }
+          }
+        }
+      }
+
+      // Possibly convert planar configuration to interleaved
+      {
+        const gdcm::Image& image = GetImage();
+        if (image.GetPlanarConfiguration() != 0 && 
+            image.GetPixelFormat().GetSamplesPerPixel() != 1)
+        {
+          interleaved_.reset(new gdcm::ImageChangePlanarConfiguration());
+          interleaved_->SetInput(image);
+          if (!interleaved_->Change() ||
+              GetImage().GetPlanarConfiguration() != 0)
+          {
+            OrthancPluginLogWarning(context_, "GDCM cannot change the planar configuration to interleaved");
+            return false;
+          }
+        }
+      }
+
+      // Decode the image to the memory buffer
+      {
+        const gdcm::Image& image = GetImage();
+        decoded_.resize(image.GetBufferLength());
+      
+        if (decoded_.size() > 0)
+        {
+          image.GetBuffer(&decoded_[0]);
+        }
+      }
+
+      return true;
+    }
+
+
+    bool DecodeUsingOrthanc()
+    {
+      /**
+       * This is a DICOM image that cannot be properly decoded by
+       * GDCM. Let's give a try with the Orthanc built-in decoder.
+       **/
+      std::string file = "/instances/" + instanceId_;
+
+      const gdcm::Image& image = GetImage();
+      if (image.GetPixelFormat().GetSamplesPerPixel() == 3 ||
+          image.GetPixelFormat().GetSamplesPerPixel() == 4)
+      {
+        file += "/preview";
+      }
+      else
+      {
+        file += "/image-uint16";
+      }
+
+      std::string png;
+      if (!GetStringFromOrthanc(png, context_, file))
+      {
+        return false;
+      }
+      else
+      {
+        try
+        {
+          png_.ReadFromMemory(png);
+          insidePng_ = true;
+          return true;
+        }
+        catch (Orthanc::OrthancException&)
+        {
+          return false;
+        }
+      }
+    }
+
+
+    bool Decode()
+    {
+      if (isDecoded_)
+      {
+        return true;
+      }
+
+      if (DecodeUsingGdcm())
+      {
+        isDecoded_ = true;
+        return true;
+      }
+
+      // GDCM cannot decode this image, try and use Orthanc built-in functions
+      photometric_.reset();
+      interleaved_.reset();
+      decoded_.clear();
+
+      if (DecodeUsingOrthanc())
+      {
+        isDecoded_ = true;
+        return true;
+      }
+      else
+      {
+        return false;
+      }
+    }
+
+
+  public:
+    PImpl(OrthancPluginContext* context,
+          const std::string& instanceId) : 
+      context_(context),
+      instanceId_(instanceId),
+      insidePng_(false),
+      isDecoded_(false)
+    {
+    }
+
+
+    const gdcm::DataSet& GetDataSet() const
+    {
+      return reader_.GetFile().GetDataSet();
+    }
+
 
     const gdcm::Image& GetImage() const
     {
@@ -61,9 +226,78 @@
     }
 
 
-    const gdcm::DataSet& GetDataSet() const
+    void Parse(const std::string& dicom)
+    {
+      // Prepare a memory stream over the DICOM instance
+      std::stringstream stream(dicom);
+
+      // Parse the DICOM instance using GDCM
+      reader_.SetStream(stream);
+      if (!reader_.Read())
+      {
+        throw Orthanc::OrthancException("GDCM cannot extract an image from this DICOM instance");
+      }
+    }
+
+
+    bool GetAccessor(Orthanc::ImageAccessor& accessor)
     {
-      return reader_.GetFile().GetDataSet();
+      if (!Decode())
+      {
+        return false;
+      }
+
+      if (insidePng_)
+      {
+        // The image was decoded using Orthanc's built-in REST API
+        accessor = png_;
+        return true;
+      }
+
+      const gdcm::Image& image = GetImage();
+
+      size_t size = decoded_.size();
+      void* buffer = (size ? &decoded_[0] : NULL);    
+      unsigned int height = image.GetRows();
+      unsigned int width = image.GetColumns();
+
+      if (image.GetPixelFormat().GetSamplesPerPixel() == 1 &&
+          (image.GetPhotometricInterpretation() == gdcm::PhotometricInterpretation::MONOCHROME1 ||
+           image.GetPhotometricInterpretation() == gdcm::PhotometricInterpretation::MONOCHROME2))
+      {
+        switch (image.GetPixelFormat())
+        {
+          case gdcm::PixelFormat::UINT16:
+            accessor.AssignWritable(Orthanc::PixelFormat_Grayscale16, width, height, 2 * width, buffer);
+            return true;
+
+          case gdcm::PixelFormat::INT16:
+            accessor.AssignWritable(Orthanc::PixelFormat_SignedGrayscale16, width, height, 2 * width, buffer);
+            return true;
+
+          case gdcm::PixelFormat::UINT8:
+            accessor.AssignWritable(Orthanc::PixelFormat_Grayscale8, width, height, width, buffer);
+            return true;
+
+          default:
+            return false;
+        }
+      }
+      else if (image.GetPixelFormat().GetSamplesPerPixel() == 3 &&
+               image.GetPhotometricInterpretation() == gdcm::PhotometricInterpretation::RGB)
+      {
+        switch (image.GetPixelFormat())
+        {
+          case gdcm::PixelFormat::UINT8:
+            accessor.AssignWritable(Orthanc::PixelFormat_RGB24, width, height, 3 * width, buffer);
+            return true;
+
+          default:
+            return false;
+        }      
+      }
+
+      return false;
     }
   };
 
@@ -112,82 +346,19 @@
   }
 
 
-  void ParsedDicomImage::Setup(const std::string& dicom)
+  ParsedDicomImage::ParsedDicomImage(OrthancPluginContext* context,
+                                     const std::string& instanceId) : 
+    pimpl_(new PImpl(context, instanceId))
   {
-    // Prepare a memory stream over the DICOM instance
-    std::stringstream stream(dicom);
+    std::string file = "/instances/" + instanceId + "/file";
 
-    // Parse the DICOM instance using GDCM
-    pimpl_->reader_.SetStream(stream);
-    if (!pimpl_->reader_.Read())
+    std::string dicom;
+    if (!GetStringFromOrthanc(dicom, context, file))
     {
-      throw Orthanc::OrthancException("GDCM cannot extract an image from this DICOM instance");
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_UnknownResource);
     }
 
-    // Change photometric interpretation, if required
-    {
-      const gdcm::Image& image = pimpl_->GetImage();
-      if (image.GetPixelFormat().GetSamplesPerPixel() == 1)
-      {
-        if (image.GetPhotometricInterpretation() != gdcm::PhotometricInterpretation::MONOCHROME1 &&
-            image.GetPhotometricInterpretation() != gdcm::PhotometricInterpretation::MONOCHROME2)
-        {
-          pimpl_->photometric_.reset(new gdcm::ImageChangePhotometricInterpretation());
-          pimpl_->photometric_->SetInput(image);
-          pimpl_->photometric_->SetPhotometricInterpretation(gdcm::PhotometricInterpretation::MONOCHROME2);
-          if (!pimpl_->photometric_->Change())
-          {
-            throw Orthanc::OrthancException("GDCM cannot change the photometric interpretation");
-          }
-        }      
-      }
-      else 
-      {
-        if (image.GetPixelFormat().GetSamplesPerPixel() == 3 &&
-            image.GetPhotometricInterpretation() != gdcm::PhotometricInterpretation::RGB)
-        {
-          pimpl_->photometric_.reset(new gdcm::ImageChangePhotometricInterpretation());
-          pimpl_->photometric_->SetInput(image);
-          pimpl_->photometric_->SetPhotometricInterpretation(gdcm::PhotometricInterpretation::RGB);
-          if (!pimpl_->photometric_->Change())
-          {
-            throw Orthanc::OrthancException("GDCM cannot change the photometric interpretation");
-          }
-        }
-      }
-    }
-
-    // Possibly convert planar configuration to interleaved
-    {
-      const gdcm::Image& image = pimpl_->GetImage();
-      if (image.GetPlanarConfiguration() != 0 && 
-          image.GetPixelFormat().GetSamplesPerPixel() != 1)
-      {
-        pimpl_->interleaved_.reset(new gdcm::ImageChangePlanarConfiguration());
-        pimpl_->interleaved_->SetInput(image);
-        if (!pimpl_->interleaved_->Change())
-        {
-          throw Orthanc::OrthancException("GDCM cannot change the planar configuration to interleaved");
-        }
-      }
-    }
-
-    // Decode the image to the memory buffer
-    {
-      const gdcm::Image& image = pimpl_->GetImage();
-      pimpl_->decoded_.resize(image.GetBufferLength());
-      
-      if (pimpl_->decoded_.size() > 0)
-      {
-        image.GetBuffer(&pimpl_->decoded_[0]);
-      }
-    }
-  }
-
-
-  ParsedDicomImage::ParsedDicomImage(const std::string& dicom) : pimpl_(new PImpl)
-  {
-    Setup(dicom);
+    pimpl_->Parse(dicom);
   }
 
 
@@ -218,60 +389,12 @@
   }
 
 
-  bool ParsedDicomImage::GetAccessor(Orthanc::ImageAccessor& accessor)
-  {
-    const gdcm::Image& image = pimpl_->GetImage();
-
-    size_t size = pimpl_->decoded_.size();
-    void* buffer = (size ? &pimpl_->decoded_[0] : NULL);    
-    unsigned int height = image.GetRows();
-    unsigned int width = image.GetColumns();
-
-    if (image.GetPixelFormat().GetSamplesPerPixel() == 1 &&
-        (image.GetPhotometricInterpretation() == gdcm::PhotometricInterpretation::MONOCHROME1 ||
-         image.GetPhotometricInterpretation() == gdcm::PhotometricInterpretation::MONOCHROME2))
-    {
-      switch (image.GetPixelFormat())
-      {
-        case gdcm::PixelFormat::UINT16:
-          accessor.AssignWritable(Orthanc::PixelFormat_Grayscale16, width, height, 2 * width, buffer);
-          return true;
-
-        case gdcm::PixelFormat::INT16:
-          accessor.AssignWritable(Orthanc::PixelFormat_SignedGrayscale16, width, height, 2 * width, buffer);
-          return true;
-
-        case gdcm::PixelFormat::UINT8:
-          accessor.AssignWritable(Orthanc::PixelFormat_Grayscale8, width, height, width, buffer);
-          return true;
-
-      default:
-	return false;
-      }
-    }
-    else if (image.GetPixelFormat().GetSamplesPerPixel() == 3 &&
-             image.GetPhotometricInterpretation() == gdcm::PhotometricInterpretation::RGB)
-    {
-      switch (image.GetPixelFormat())
-      {
-        case gdcm::PixelFormat::UINT8:
-          accessor.AssignWritable(Orthanc::PixelFormat_RGB24, width, height, 3 * width, buffer);
-          return true;
-
-      default:
-	return false;
-      }      
-    }
-
-    return false;
-  }
-
   bool ParsedDicomImage::GetCornerstoneMetadata(Json::Value& json)
   {
     using namespace Orthanc;
 
     ImageAccessor accessor;
-    if (!GetAccessor(accessor))
+    if (!pimpl_->GetAccessor(accessor))
     {
       return false;
     }
@@ -355,7 +478,7 @@
     using namespace Orthanc;
 
     ImageAccessor accessor;
-    if (!GetAccessor(accessor))
+    if (!pimpl_->GetAccessor(accessor))
     {
       return false;
     }
@@ -422,7 +545,7 @@
     using namespace Orthanc;
 
     ImageAccessor accessor;
-    if (!GetAccessor(accessor))
+    if (!pimpl_->GetAccessor(accessor))
     {
       return false;
     }
--- a/Plugin/ParsedDicomImage.h	Thu Jun 04 10:18:43 2015 +0200
+++ b/Plugin/ParsedDicomImage.h	Wed Jun 24 16:20:41 2015 +0200
@@ -22,6 +22,7 @@
 
 #include "../Orthanc/Core/ImageFormats/ImageAccessor.h"
 
+#include <orthanc/OrthancCPlugin.h>
 #include <stdint.h>
 #include <boost/noncopyable.hpp>
 #include <boost/shared_ptr.hpp>
@@ -32,21 +33,18 @@
   class ParsedDicomImage : public boost::noncopyable
   {
   private:
-    struct PImpl;
+    class PImpl;
     boost::shared_ptr<PImpl> pimpl_;
   
-    void Setup(const std::string& dicom);
-
   public:
-    ParsedDicomImage(const std::string& dicom);
+    ParsedDicomImage(OrthancPluginContext* context,
+                     const std::string& instanceId);
 
     bool GetTag(std::string& result,
                 uint16_t group,
                 uint16_t element,
                 bool stripSpaces = true);
 
-    bool GetAccessor(Orthanc::ImageAccessor& accessor);
-
     bool GetCornerstoneMetadata(Json::Value& json);
 
     bool EncodeUsingDeflate(Json::Value& result,
--- a/Plugin/Plugin.cpp	Thu Jun 04 10:18:43 2015 +0200
+++ b/Plugin/Plugin.cpp	Wed Jun 24 16:20:41 2015 +0200
@@ -180,7 +180,7 @@
 
 
 
-template <enum OrthancPlugins::EmbeddedResources::DirectoryResourceId folder>
+template <enum Orthanc::EmbeddedResources::DirectoryResourceId folder>
 static int32_t ServeEmbeddedFolder(OrthancPluginRestOutput* output,
                                    const char* url,
                                    const OrthancPluginHttpRequest* request)
@@ -197,7 +197,7 @@
   try
   {
     std::string s;
-    OrthancPlugins::EmbeddedResources::GetDirectoryResource(s, folder, path.c_str());
+    Orthanc::EmbeddedResources::GetDirectoryResource(s, folder, path.c_str());
 
     const char* resource = s.size() ? s.c_str() : NULL;
     OrthancPluginAnswerBuffer(context_, output, resource, s.size(), mime);
@@ -377,10 +377,10 @@
     OrthancPluginRegisterRestCallback(context_, "/web-viewer/series/(.*)", ServeCache<CacheBundle_SeriesInformation>);
     OrthancPluginRegisterRestCallback(context_, "/web-viewer/is-stable-series/(.*)", IsStableSeries);
     OrthancPluginRegisterRestCallback(context_, "/web-viewer/instances/(.*)", ServeCache<CacheBundle_DecodedImage>);
-    OrthancPluginRegisterRestCallback(context, "/web-viewer/libs/(.*)", ServeEmbeddedFolder<EmbeddedResources::JAVASCRIPT_LIBS>);
+    OrthancPluginRegisterRestCallback(context, "/web-viewer/libs/(.*)", ServeEmbeddedFolder<Orthanc::EmbeddedResources::JAVASCRIPT_LIBS>);
 
 #if ORTHANC_STANDALONE == 1
-    OrthancPluginRegisterRestCallback(context, "/web-viewer/app/(.*)", ServeEmbeddedFolder<EmbeddedResources::WEB_VIEWER>);
+    OrthancPluginRegisterRestCallback(context, "/web-viewer/app/(.*)", ServeEmbeddedFolder<Orthanc::EmbeddedResources::WEB_VIEWER>);
 #else
     OrthancPluginRegisterRestCallback(context, "/web-viewer/app/(.*)", ServeWebViewer);
 #endif
@@ -390,7 +390,7 @@
 
     /* Extend the default Orthanc Explorer with custom JavaScript */
     std::string explorer;
-    EmbeddedResources::GetFileResource(explorer, EmbeddedResources::ORTHANC_EXPLORER);
+    Orthanc::EmbeddedResources::GetFileResource(explorer, Orthanc::EmbeddedResources::ORTHANC_EXPLORER);
     OrthancPluginExtendOrthancExplorer(context_, explorer.c_str());
 
     return 0;
--- a/Resources/EmbedResources.py	Thu Jun 04 10:18:43 2015 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,391 +0,0 @@
-# Orthanc - A Lightweight, RESTful DICOM Store
-# Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics
-# Department, University Hospital of Liege, Belgium
-#
-# This program is free software: you can redistribute it and/or
-# modify it under the terms of the GNU Affero General Public License
-# as published by the Free Software Foundation, either version 3 of
-# the License, or (at your option) any later version.
-#
-# This program 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
-# Affero General Public License for more details.
-# 
-# You should have received a copy of the GNU Affero General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
-
-
-import sys
-import os
-import os.path
-import pprint
-import re
-
-UPCASE_CHECK = True
-ARGS = []
-for i in range(len(sys.argv)):
-    if not sys.argv[i].startswith('--'):
-        ARGS.append(sys.argv[i])
-    elif sys.argv[i].lower() == '--no-upcase-check':
-        UPCASE_CHECK = False
-
-if len(ARGS) < 2 or len(ARGS) % 2 != 0:
-    print ('Usage:')
-    print ('python %s [--no-upcase-check] <TargetBaseFilename> [ <Name> <Source> ]*' % sys.argv[0])
-    exit(-1)
-
-TARGET_BASE_FILENAME = ARGS[1]
-SOURCES = ARGS[2:]
-
-try:
-    # Make sure the destination directory exists
-    os.makedirs(os.path.normpath(os.path.join(TARGET_BASE_FILENAME, '..')))
-except:
-    pass
-
-
-#####################################################################
-## Read each resource file
-#####################################################################
-
-def CheckNoUpcase(s):
-    global UPCASE_CHECK
-    if (UPCASE_CHECK and
-        re.search('[A-Z]', s) != None):
-        raise Exception("Path in a directory with an upcase letter: %s" % s)
-
-resources = {}
-
-counter = 0
-i = 0
-while i < len(SOURCES):
-    resourceName = SOURCES[i].upper()
-    pathName = SOURCES[i + 1]
-
-    if not os.path.exists(pathName):
-        raise Exception("Non existing path: %s" % pathName)
-
-    if resourceName in resources:
-        raise Exception("Twice the same resource: " + resourceName)
-    
-    if os.path.isdir(pathName):
-        # The resource is a directory: Recursively explore its files
-        content = {}
-        for root, dirs, files in os.walk(pathName):
-            base = os.path.relpath(root, pathName)
-
-            # Fix issue #24 (Build fails on OSX when directory has .DS_Store files):
-            # Ignore folders whose name starts with a dot (".")
-            if base.find('/.') != -1:
-                print('Ignoring folder: %s' % root)
-                continue
-
-            for f in files:
-                if f.find('~') == -1:  # Ignore Emacs backup files
-                    if base == '.':
-                        r = f
-                    else:
-                        r = os.path.join(base, f)
-
-                    CheckNoUpcase(r)
-                    r = '/' + r.replace('\\', '/')
-                    if r in content:
-                        raise Exception("Twice the same filename (check case): " + r)
-
-                    content[r] = {
-                        'Filename' : os.path.join(root, f),
-                        'Index' : counter
-                        }
-                    counter += 1
-
-        resources[resourceName] = {
-            'Type' : 'Directory',
-            'Files' : content
-            }
-
-    elif os.path.isfile(pathName):
-        resources[resourceName] = {
-            'Type' : 'File',
-            'Index' : counter,
-            'Filename' : pathName
-            }
-        counter += 1
-
-    else:
-        raise Exception("Not a regular file, nor a directory: " + pathName)
-
-    i += 2
-
-#pprint.pprint(resources)
-
-
-#####################################################################
-## Write .h header
-#####################################################################
-
-header = open(TARGET_BASE_FILENAME + '.h', 'w')
-
-header.write("""
-#pragma once
-
-#include <string>
-#include <list>
-
-namespace OrthancPlugins
-{
-  namespace EmbeddedResources
-  {
-    enum FileResourceId
-    {
-""")
-
-isFirst = True
-for name in resources:
-    if resources[name]['Type'] == 'File':
-        if isFirst:
-            isFirst = False
-        else:    
-            header.write(',\n')
-        header.write('      %s' % name)
-
-header.write("""
-    };
-
-    enum DirectoryResourceId
-    {
-""")
-
-isFirst = True
-for name in resources:
-    if resources[name]['Type'] == 'Directory':
-        if isFirst:
-            isFirst = False
-        else:    
-            header.write(',\n')
-        header.write('      %s' % name)
-
-header.write("""
-    };
-
-    const void* GetFileResourceBuffer(FileResourceId id);
-    size_t GetFileResourceSize(FileResourceId id);
-    void GetFileResource(std::string& result, FileResourceId id);
-
-    const void* GetDirectoryResourceBuffer(DirectoryResourceId id, const char* path);
-    size_t GetDirectoryResourceSize(DirectoryResourceId id, const char* path);
-    void GetDirectoryResource(std::string& result, DirectoryResourceId id, const char* path);
-
-    void ListResources(std::list<std::string>& result, DirectoryResourceId id);
-  }
-}
-""")
-header.close()
-
-
-
-#####################################################################
-## Write the resource content in the .cpp source
-#####################################################################
-
-PYTHON_MAJOR_VERSION = sys.version_info[0]
-
-def WriteResource(cpp, item):
-    cpp.write('    static const uint8_t resource%dBuffer[] = {' % item['Index'])
-
-    f = open(item['Filename'], "rb")
-    content = f.read()
-    f.close()
-
-    # http://stackoverflow.com/a/1035360
-    pos = 0
-    for b in content:
-        if PYTHON_MAJOR_VERSION == 2:
-            c = ord(b[0])
-        else:
-            c = b
-
-        if pos > 0:
-            cpp.write(', ')
-
-        if (pos % 16) == 0:
-            cpp.write('\n    ')
-
-        if c < 0:
-            raise Exception("Internal error")
-
-        cpp.write("0x%02x" % c)
-        pos += 1
-
-    cpp.write('  };\n')
-    cpp.write('    static const size_t resource%dSize = %d;\n' % (item['Index'], pos))
-
-
-cpp = open(TARGET_BASE_FILENAME + '.cpp', 'w')
-
-cpp.write("""
-#include "%s.h"
-
-#include <stdexcept>
-#include <stdint.h>
-#include <string.h>
-
-namespace OrthancPlugins
-{
-  namespace EmbeddedResources
-  {
-""" % (os.path.basename(TARGET_BASE_FILENAME)))
-
-
-for name in resources:
-    if resources[name]['Type'] == 'File':
-        WriteResource(cpp, resources[name])
-    else:
-        for f in resources[name]['Files']:
-            WriteResource(cpp, resources[name]['Files'][f])
-
-
-
-#####################################################################
-## Write the accessors to the file resources in .cpp
-#####################################################################
-
-cpp.write("""
-    const void* GetFileResourceBuffer(FileResourceId id)
-    {
-      switch (id)
-      {
-""")
-for name in resources:
-    if resources[name]['Type'] == 'File':
-        cpp.write('      case %s:\n' % name)
-        cpp.write('        return resource%dBuffer;\n' % resources[name]['Index'])
-
-cpp.write("""
-      default:
-        throw std::runtime_error("Parameter out of range");
-      }
-    }
-
-    size_t GetFileResourceSize(FileResourceId id)
-    {
-      switch (id)
-      {
-""")
-
-for name in resources:
-    if resources[name]['Type'] == 'File':
-        cpp.write('      case %s:\n' % name)
-        cpp.write('        return resource%dSize;\n' % resources[name]['Index'])
-
-cpp.write("""
-      default:
-        throw std::runtime_error("Parameter out of range");
-      }
-    }
-""")
-
-
-
-#####################################################################
-## Write the accessors to the directory resources in .cpp
-#####################################################################
-
-cpp.write("""
-    const void* GetDirectoryResourceBuffer(DirectoryResourceId id, const char* path)
-    {
-      switch (id)
-      {
-""")
-
-for name in resources:
-    if resources[name]['Type'] == 'Directory':
-        cpp.write('      case %s:\n' % name)
-        isFirst = True
-        for path in resources[name]['Files']:
-            cpp.write('        if (!strcmp(path, "%s"))\n' % path)
-            cpp.write('          return resource%dBuffer;\n' % resources[name]['Files'][path]['Index'])
-        cpp.write('        throw std::runtime_error("Unknown path in a directory resource");\n\n')
-
-cpp.write("""      default:
-        throw std::runtime_error("Parameter out of range");
-      }
-    }
-
-    size_t GetDirectoryResourceSize(DirectoryResourceId id, const char* path)
-    {
-      switch (id)
-      {
-""")
-
-for name in resources:
-    if resources[name]['Type'] == 'Directory':
-        cpp.write('      case %s:\n' % name)
-        isFirst = True
-        for path in resources[name]['Files']:
-            cpp.write('        if (!strcmp(path, "%s"))\n' % path)
-            cpp.write('          return resource%dSize;\n' % resources[name]['Files'][path]['Index'])
-        cpp.write('        throw std::runtime_error("Unknown path in a directory resource");\n\n')
-
-cpp.write("""      default:
-        throw std::runtime_error("Parameter out of range");
-      }
-    }
-""")
-
-
-
-
-#####################################################################
-## List the resources in a directory
-#####################################################################
-
-cpp.write("""
-    void ListResources(std::list<std::string>& result, DirectoryResourceId id)
-    {
-      result.clear();
-
-      switch (id)
-      {
-""")
-
-for name in resources:
-    if resources[name]['Type'] == 'Directory':
-        cpp.write('      case %s:\n' % name)
-        for path in sorted(resources[name]['Files']):
-            cpp.write('        result.push_back("%s");\n' % path)
-        cpp.write('        break;\n\n')
-
-cpp.write("""      default:
-        throw std::runtime_error("Parameter out of range");
-      }
-    }
-""")
-
-
-
-
-#####################################################################
-## Write the convenience wrappers in .cpp
-#####################################################################
-
-cpp.write("""
-    void GetFileResource(std::string& result, FileResourceId id)
-    {
-      size_t size = GetFileResourceSize(id);
-      result.resize(size);
-      if (size > 0)
-        memcpy(&result[0], GetFileResourceBuffer(id), size);
-    }
-
-    void GetDirectoryResource(std::string& result, DirectoryResourceId id, const char* path)
-    {
-      size_t size = GetDirectoryResourceSize(id, path);
-      result.resize(size);
-      if (size > 0)
-        memcpy(&result[0], GetDirectoryResourceBuffer(id, path), size);
-    }
-  }
-}
-""")
-cpp.close()
--- a/Resources/SyncOrthancFolder.py	Thu Jun 04 10:18:43 2015 +0200
+++ b/Resources/SyncOrthancFolder.py	Wed Jun 24 16:20:41 2015 +0200
@@ -26,6 +26,8 @@
     'Core/ImageFormats/ImageBuffer.h',
     'Core/ImageFormats/ImageProcessing.cpp',
     'Core/ImageFormats/ImageProcessing.h',
+    'Core/ImageFormats/PngReader.cpp',
+    'Core/ImageFormats/PngReader.h',
     'Core/ImageFormats/PngWriter.cpp',
     'Core/ImageFormats/PngWriter.h',
     'Core/MultiThreading/SharedMessageQueue.cpp',
@@ -54,6 +56,7 @@
     'Core/Toolbox.h',
     'Core/Uuid.cpp',
     'Core/Uuid.h',
+    'Resources/EmbedResources.py',
     'Resources/CMake/AutoGeneratedCode.cmake',
     'Resources/CMake/BoostConfiguration.cmake',
     'Resources/CMake/Compiler.cmake',