]> Creatis software - clitk.git/commitdiff
Merge branch 'clitkImage2Dicom'
authortbaudier <thomas.baudier@creatis.insa-lyon.fr>
Fri, 27 Jul 2018 12:01:08 +0000 (14:01 +0200)
committertbaudier <thomas.baudier@creatis.insa-lyon.fr>
Fri, 27 Jul 2018 12:01:08 +0000 (14:01 +0200)
161 files changed:
.travis.yml
CMakeLists.txt
Dockerfile [new file with mode: 0644]
README.md
bw-output/build-wrapper-dump.json [new file with mode: 0644]
bw-output/build-wrapper.log [new file with mode: 0644]
cluster_tools/CMakeLists.txt
cluster_tools/gate_common.sh [changed mode: 0644->0755]
cluster_tools/gate_job_cluster.job
cluster_tools/gate_power_merge.sh
cluster_tools/gate_run_submit_cluster.sh
cluster_tools/gate_run_submit_cluster_nomove.sh [changed mode: 0644->0755]
cmake/build_opt.cmake
cmake/dependencies.cmake
common/clitkChangeDicomTagGenericFilter.h [new file with mode: 0644]
common/clitkChangeDicomTagGenericFilter.txx [new file with mode: 0644]
common/clitkCommon.cxx
common/clitkCommon.h
common/clitkDicomRTStruct2ImageFilter.cxx
common/clitkDicomRTStruct2ImageFilter.h
common/clitkDicomRT_Contour.cxx
common/clitkDicomRT_Contour.h
common/clitkDicomRT_ROI.cxx
common/clitkDicomRT_ROI.h
common/clitkDicomRT_StructureSet.cxx
common/clitkDicomRT_StructureSet.h
common/clitkImage2DicomRTStructFilter.txx
common/clitkImageCommon.cxx
common/clitkImageToImageGenericFilterBase.cxx
common/clitkXdrImageIOReader.cxx
common/clitkXml2DicomRTStructFilter.h [new file with mode: 0644]
common/clitkXml2DicomRTStructFilter.txx [new file with mode: 0644]
common/vvImage.cxx
common/vvImage.h
common/vvImageReader.cxx
common/vvImageReader.h
common/vvImageReader.txx
docWiki.sh [new file with mode: 0755]
registration/CMakeLists.txt
registration/clitkAffineRegistrationGenericFilter.cxx
registration/clitkBLUTDIRGenericFilter.cxx
registration/clitkBLUTDIRGenericFilter.h
registration/clitkConvertBLUTCoeffsToVFFilter.h
registration/clitkConvertBLUTCoeffsToVFFilter.txx
registration/clitkGenericMetric.txx
registration/clitkMatrixTransformToVFGenericFilter.h
registration/clitkMatrixTransformToVFGenericFilter.txx
registration/clitkMultiResolutionPDEDeformableRegistration.txx
segmentation/CMakeLists.txt
segmentation/clitkExtractBonesGenericFilter.txx
segmentation/clitkExtractPatient.ggo
segmentation/clitkExtractPatientFilter.txx
sonar-project.properties [new file with mode: 0644]
tools/CMakeLists.txt
tools/clitkAffineTransform.ggo
tools/clitkAffineTransformGenericFilter.h
tools/clitkAffineTransformGenericFilter.txx
tools/clitkAnisotropicDiffusionGenericFilter.cxx
tools/clitkBinarizeImage.ggo
tools/clitkBinarizeImageGenericFilter.cxx
tools/clitkBinarizeImageGenericFilter.h
tools/clitkBlurImage.ggo
tools/clitkBlurImageGenericFilter.txx
tools/clitkChangeDicomTag.cxx [new file with mode: 0644]
tools/clitkChangeDicomTag.ggo [new file with mode: 0644]
tools/clitkCropImageGenericFilter.cxx
tools/clitkDicom2Image.cxx
tools/clitkDicom2Image.ggo
tools/clitkDicomRTStruct2Image.cxx
tools/clitkDicomRTStruct2Image.ggo
tools/clitkGateSimulation2Dicom.cxx [new file with mode: 0644]
tools/clitkGateSimulation2Dicom.ggo [new file with mode: 0644]
tools/clitkGateSimulation2DicomGenericFilter.cxx [new file with mode: 0644]
tools/clitkGateSimulation2DicomGenericFilter.h [new file with mode: 0644]
tools/clitkGateSimulation2DicomGenericFilter.txx [new file with mode: 0644]
tools/clitkHistogramImage.cxx
tools/clitkHistogramImageGenericFilter.cxx
tools/clitkHistogramImageGenericFilter.h
tools/clitkImage2DicomDose.cxx [new file with mode: 0644]
tools/clitkImage2DicomDose.ggo [new file with mode: 0644]
tools/clitkImage2DicomDoseGenericFilter.cxx [new file with mode: 0644]
tools/clitkImage2DicomDoseGenericFilter.h [new file with mode: 0644]
tools/clitkImage2DicomDoseGenericFilter.txx [new file with mode: 0644]
tools/clitkImage2DicomRTStruct.ggo
tools/clitkImageArithm.ggo
tools/clitkImageArithmGenericFilter.txx
tools/clitkImageConvertGenericFilter.cxx
tools/clitkImageGradientMagnitudeGenericFilter.txx
tools/clitkImageIntensityWindowingGenericFilter.txx
tools/clitkImageLaplacianGenericFilter.txx
tools/clitkImageStatistics.ggo
tools/clitkImageStatisticsGenericFilter.h
tools/clitkImageStatisticsGenericFilter.txx
tools/clitkMIP.ggo
tools/clitkMaskOfIntegratedIntensity.cxx [moved from tools/clitkResampleImage.cxx with 80% similarity]
tools/clitkMaskOfIntegratedIntensity.ggo [new file with mode: 0644]
tools/clitkMaskOfIntegratedIntensityGenericFilter.h [new file with mode: 0644]
tools/clitkMaskOfIntegratedIntensityGenericFilter.txx [new file with mode: 0644]
tools/clitkMergeRootFiles.cxx
tools/clitkNormalizeImageFilterGenericFilter.txx
tools/clitkPartitionEnergyWindowDicom.cxx [new file with mode: 0644]
tools/clitkPartitionEnergyWindowDicom.ggo [new file with mode: 0644]
tools/clitkPartitionEnergyWindowDicomGenericFilter.cxx [new file with mode: 0644]
tools/clitkPartitionEnergyWindowDicomGenericFilter.h [new file with mode: 0644]
tools/clitkPartitionEnergyWindowDicomGenericFilter.txx [new file with mode: 0644]
tools/clitkResampleImage.ggo [deleted file]
tools/clitkResampleImageGenericFilter.cxx [deleted file]
tools/clitkResampleImageGenericFilter.h [deleted file]
tools/clitkResampleImageGenericFilter.txx [deleted file]
tools/clitkSplitImageGenericFilter.cxx
tools/clitkUnsharpMask.ggo
tools/clitkUpdateVRTagDicom.cxx [new file with mode: 0644]
tools/clitkUpdateVRTagDicom.ggo [new file with mode: 0644]
tools/clitkUpdateVRTagDicomGenericFilter.cxx [new file with mode: 0644]
tools/clitkUpdateVRTagDicomGenericFilter.h [new file with mode: 0644]
tools/clitkUpdateVRTagDicomGenericFilter.txx [new file with mode: 0644]
tools/clitkVFResampleGenericFilter.cxx
tools/clitkVectorArithm.ggo
tools/clitkVectorArithmGenericFilter.txx
tools/clitkXml2DicomRTStruct.cxx [new file with mode: 0644]
tools/clitkXml2DicomRTStruct.ggo [new file with mode: 0644]
travis/travis_build_itk.sh
travis/travis_build_qt.sh
travis/travis_build_vv.sh
utilities/CxImage/ximadef.h
utilities/CxImage/ximaenc.cpp
utilities/CxImage/ximage.cpp
utilities/CxImage/ximagif.cpp
utilities/CxImage/ximaint.cpp
utilities/CxImage/ximaiter.h
utilities/CxImage/ximapal.cpp
utilities/CxImage/ximasel.cpp
utilities/CxImage/ximath.cpp
utilities/CxImage/ximatran.cpp
utilities/pugixml/pugiconfig.hpp [new file with mode: 0644]
utilities/pugixml/pugixml.cpp [new file with mode: 0644]
utilities/pugixml/pugixml.hpp [new file with mode: 0644]
vv/CMakeLists.txt
vv/qt_ui/vvDicomSeriesSelector.ui
vv/vtkVOXImageWriter.cxx
vv/vvAnimatedGIFWriter.cxx
vv/vvBinaryImageOverlayActor.cxx
vv/vvBinaryImageOverlayActor.h
vv/vvBlendImageActor.cxx
vv/vvDeformableRegistration.cxx
vv/vvGlyph2D.cxx
vv/vvInteractorStyleNavigator.cxx
vv/vvInteractorStyleNavigator.h
vv/vvMainWindow.cxx
vv/vvMeshActor.cxx
vv/vvMeshActor.h
vv/vvQDicomSeriesSelector.cxx
vv/vvQDicomSeriesSelector.h
vv/vvROIActor.cxx
vv/vvROIActor.h
vv/vvSlicer.cxx
vv/vvSlicerManager.cxx
vv/vvSlicerManager.h
vv/vvToolHistogram.cxx
vv/vvToolProfile.cxx
vv/vvToolROIManager.cxx

index e7bb44e67fa1fb6d5052257b1577077a273996f2..a1d3ba91e565cff4c73e222b5006d0a8225a95b6 100644 (file)
@@ -16,19 +16,22 @@ env:
     - WITH_CMAKE=true VTK_VERSION=7.0.0 ITK_VERSION=4.9.1 QT_VERSION=4.8.7 C11=false
     - WITH_CMAKE=true VTK_VERSION=6.3 ITK_VERSION=4.9.1 QT_VERSION=5.5.1 C11=false
     - WITH_CMAKE=true VTK_VERSION=6.3 ITK_VERSION=4.9.1 QT_VERSION=4.8.7 C11=false
-    - WITH_CMAKE=true VTK_VERSION=5.10 ITK_VERSION=4.5 QT_VERSION=4.8.7 C11=false
+    - WITH_CMAKE=true VTK_VERSION=5.10 ITK_VERSION=4.6 QT_VERSION=4.8.7 C11=false
     - WITH_CMAKE=true VTK_VERSION=7.0.0 ITK_VERSION=4.9.1 QT_VERSION=5.5.1 C11=true
     - WITH_CMAKE=true VTK_VERSION=7.0.0 ITK_VERSION=4.9.1 QT_VERSION=4.8.7 C11=true
     - WITH_CMAKE=true VTK_VERSION=6.3 ITK_VERSION=4.9.1 QT_VERSION=5.5.1 C11=true
     - WITH_CMAKE=true VTK_VERSION=6.3 ITK_VERSION=4.9.1 QT_VERSION=4.8.7 C11=true
-    - WITH_CMAKE=true VTK_VERSION=5.10 ITK_VERSION=4.5 QT_VERSION=4.8.7 C11=true
+    - WITH_CMAKE=true VTK_VERSION=7.0.0 ITK_VERSION=4.13.0 QT_VERSION=5.5.1 C11=false
+    - WITH_CMAKE=true VTK_VERSION=6.3 ITK_VERSION=4.13.0 QT_VERSION=5.5.1 C11=false
+    - WITH_CMAKE=true VTK_VERSION=7.0.0 ITK_VERSION=4.13.0 QT_VERSION=5.5.1 C11=true
+    - WITH_CMAKE=true VTK_VERSION=6.3 ITK_VERSION=4.13.0 QT_VERSION=5.5.1 C11=true
 
 matrix:
   exclude:
   - os: osx
-    env: WITH_CMAKE=true VTK_VERSION=5.10 ITK_VERSION=4.5 QT_VERSION=4.8.7 C11=false #VTK5.10 doesn't work with osX
+    env: WITH_CMAKE=true VTK_VERSION=5.10 ITK_VERSION=4.6 QT_VERSION=4.8.7 C11=false #VTK5.10 doesn't work with osX
   - os: osx
-    env: WITH_CMAKE=true VTK_VERSION=5.10 ITK_VERSION=4.5 QT_VERSION=4.8.7 C11=true #VTK5.10 doesn't work with osX
+    env: WITH_CMAKE=true VTK_VERSION=5.10 ITK_VERSION=4.6 QT_VERSION=4.8.7 C11=true #VTK5.10 doesn't work with osX
   - os: osx
     env: WITH_CMAKE=true VTK_VERSION=6.3 ITK_VERSION=4.9.1 QT_VERSION=4.8.7 C11=false #Qt4 doesn't work with brew anymore
   - os: osx
@@ -38,7 +41,23 @@ matrix:
   - os: osx
     env: WITH_CMAKE=true VTK_VERSION=7.0.0 ITK_VERSION=4.9.1 QT_VERSION=4.8.7 C11=true
   - os: linux
-    env: WITH_CMAKE=true VTK_VERSION=5.10 ITK_VERSION=4.5 QT_VERSION=4.8.7 C11=true #Problem with VTK 5.10 and c++11
+    env: WITH_CMAKE=true VTK_VERSION=5.10 ITK_VERSION=4.6 QT_VERSION=4.8.7 C11=true #Problem with VTK 5.10 and c++11
+  - os: linux
+    compiler: clang #Remove ITK with clang on linux
+  include:
+  - os: linux
+    compiler: clang
+    env: WITH_CMAKE=true VTK_VERSION=7.0.0 ITK_VERSION=4.13.0 QT_VERSION=5.5.1 C11=false
+  - os: linux
+    compiler: clang
+    env: WITH_CMAKE=true VTK_VERSION=6.3 ITK_VERSION=4.13.0 QT_VERSION=5.5.1 C11=false
+  - os: linux
+    compiler: clang
+    env: WITH_CMAKE=true VTK_VERSION=7.0.0 ITK_VERSION=4.13.0 QT_VERSION=5.5.1 C11=true
+  - os: linux
+    compiler: clang
+    env: WITH_CMAKE=true VTK_VERSION=6.3 ITK_VERSION=4.13.0 QT_VERSION=5.5.1 C11=true
+
 
 language: cpp
 
@@ -47,6 +66,11 @@ notifications:
   on_success: never
   on_failure: never
 
+addons:
+  sonarcloud:
+    organization: "open-vv-github"
+    token: $SONAR_TOKEN
+
 branches:
 only:
 - master
@@ -65,22 +89,17 @@ before_install:
 - export ITK_SOURCE_DIR=${EXTERNALS_DIR}/itk-${ITK_VERSION}
 - export ITK_DIR=${EXTERNALS_DIR}/itk-${ITK_VERSION}-build
 - export BUILD_DIR=$cwd/build
-
-install:
 - if test $TRAVIS_OS_NAME == linux ; then sudo apt-get install -y -qq build-essential cmake; fi
 #Install qt5
 - $SCRIPTS/travis_build_qt.sh
 - if test $TRAVIS_OS_NAME == linux; then sudo apt-get -y install libgdcm2-dev libpq-dev; fi
 - if test $TRAVIS_OS_NAME == linux; then if [[ "$QT_VERSION" == "5.5.1" ]] ; then QTDIR="/opt/qt55" && PATH="$PATH:$QTDIR/bin" && qt55-env.sh ; fi; fi
 - if test $TRAVIS_OS_NAME == osx ; then export HOMEBREW_QT_VERSION=$(brew list --versions qt | rev | cut -d' ' -f1 | rev); fi
-- if test $TRAVIS_OS_NAME == osx ; then if [[ "$QT_VERSION" == "5.5.1" ]] ; then ln -s /usr/local/Cellar/qt@5.5/5.5.1_1/mkspecs /usr/local/mkspecs; fi; fi
-- if test $TRAVIS_OS_NAME == osx ; then if [[ "$QT_VERSION" == "5.5.1" ]] ; then ln -s /usr/local/Cellar/qt@5.5/5.5.1_1/plugins /usr/local/plugins; fi; fi
-- if test $TRAVIS_OS_NAME == osx ; then if [[ "$QT_VERSION" == "4.8.7" ]] ; then ln -s /usr/local/Cellar/qt@4/4.8.7_1/mkspecs /usr/local/mkspecs; fi; fi
-- if test $TRAVIS_OS_NAME == osx ; then if [[ "$QT_VERSION" == "4.8.7" ]] ; then ln -s /usr/local/Cellar/qt@4/4.8.7_1/plugins /usr/local/plugins; fi; fi
+- if test $TRAVIS_OS_NAME == osx ; then brew install ccache; fi
+- if test $TRAVIS_OS_NAME == osx ; then export PATH="/usr/local/opt/qt/bin:/usr/local/opt/ccache/libexec:$PATH"; fi
 - if test $TRAVIS_OS_NAME == osx ; then qmake -v; fi
 
 cache:
- - timeout: 1000
  - directories:
    - $HOME/vtk-5.10
    - $HOME/vtk-5.10-build
@@ -88,18 +107,25 @@ cache:
    - $HOME/vtk-6.3-build
    - $HOME/vtk-7.0.0
    - $HOME/vtk-7.0.0-build
-   - $HOME/itk-4.5
-   - $HOME/itk-4.5-build
+   - $HOME/itk-4.6
+   - $HOME/itk-4.6-build
    - $HOME/itk-4.9.1
    - $HOME/itk-4.9.1-build
+   - $HOME/itk-4.13.0
+   - $HOME/itk-4.13.0-build
+   - '$HOME/.sonar/cache'
+ - timeout: 10000
 
 before_script:
+- export DO_BUILD_VTK=false
+- export DO_BUILD_ITK=false
 - if test $TRAVIS_OS_NAME == linux ; then sudo ln -s /usr/bin/x86_64-linux-gnu/libpq.so /usr/bin/libpq.so; fi
-- echo $PATH
-- $SCRIPTS/travis_build_vtk.sh
-- $SCRIPTS/travis_build_itk.sh
+- if ! test -e ${VTK_SOURCE_DIR}/CMakeLists.txt; then rm -fr $VTK_SOURCE_DIR; DO_BUILD_VTK=true; fi
+- if ! test -e ${ITK_SOURCE_DIR}/CMakeLists.txt; then rm -fr $ITK_SOURCE_DIR; DO_BUILD_ITK=true; fi
 #Install vtk and itk (different version)
 
 script:
-- echo "coucou";
-- $SCRIPTS/travis_build_vv.sh
+- 'if [ $DO_BUILD_VTK == true ]; then $SCRIPTS/travis_build_vtk.sh || travis_terminate 1;
+   elif [ $DO_BUILD_ITK == true ]; then $SCRIPTS/travis_build_itk.sh || travis_terminate 1;
+   else $SCRIPTS/travis_build_vv.sh || travis_terminate 1; fi'
+#- if [ $TRAVIS_OS_NAME == linux ] && [ $DO_BUILD_VTK == false ] && [ $DO_BUILD_ITK == false ]; then sonar-scanner ; fi
index c4fbc59a14fe072247bd46ae6fa5a61637c054ff..6554e49bda4910e3ed6b395fc5a41d6013e06c03 100644 (file)
@@ -6,6 +6,7 @@ cmake_policy(VERSION 2.8)
 if(COMMAND cmake_policy)
     cmake_policy(SET CMP0003 NEW)
     cmake_policy(SET CMP0007 NEW)
+    cmake_policy(SET CMP0053 NEW)
 endif(COMMAND cmake_policy)
 if(NOT DEFINED CLITK_SOURCE_DIR)
   set(CLITK_SOURCE_DIR ${PROJECT_SOURCE_DIR})
diff --git a/Dockerfile b/Dockerfile
new file mode 100644 (file)
index 0000000..9a1a82b
--- /dev/null
@@ -0,0 +1,72 @@
+
+# systemctl start docker
+# docker login
+# docker build -t tbaudier/vv .
+# docker push tbaudier/vv
+# docker pull tbaudier/vv
+# docker run -ti --rm -e DISPLAY=$DISPLAY -v /tmp/.X11-unix:/tmp/.X11-unix -v /home/tbaudier:/home tbaudier/vv
+# docker images
+# docker ps -a
+# docker rm -f `docker ps -aq `
+# docker rmi -f `docker images -q `
+
+FROM opensuse:42.3
+RUN zypper install -y cmake \
+                      git \
+                      gcc \
+                      gcc-c++ \
+                      libQt5Core-devel \
+                      libqt5-qtbase-devel \
+                      libqt5-qttools-devel \
+                      libqt5-qtx11extras-devel \
+                      libXt-devel
+
+#Build VTK
+RUN mkdir VTK \
+ && cd VTK \
+ && mkdir src \
+ && mkdir bin \
+ && mkdir build \
+ && git clone --branch v7.1.0 https://github.com/Kitware/VTK.git src \
+ && cd bin \
+ && cmake ../src/ -DCMAKE_INSTALL_PREFIX="/VTK/build/" -DBUILD_SHARED_LIBS=ON -DBUILD_TESTING=OFF -DCMAKE_BUILD_TYPE=Release -DVTK_RENDERING_BACKEND=OpenGL -DVTK_USE_CXX11_FEATURES=ON -DModule_vtkGUISupportQt=ON -DModule_vtkGUISupportQtOpenGL=ON -DModule_vtkRenderingQt=ON -DModule_vtkViewsQt=ON -DVTK_QT_VERSION=5 \
+ && make -j4 \
+ && make install \
+ && cd .. \
+ && rm -rf bin src
+
+#Build ITK
+RUN mkdir ITK \
+ && cd ITK \
+ && mkdir src \
+ && mkdir bin \
+ && mkdir build \
+ && git clone --branch v4.13.0 https://github.com/InsightSoftwareConsortium/ITK.git src \
+ && cd bin \
+ && cmake ../src/ -DCMAKE_INSTALL_PREFIX="/ITK/build/" -DVTK_DIR="/VTK/build/lib/cmake/vtk-7.1" -DBUILD_TESTING=OFF -DBUILD_SHARED_LIBS=ON -DModule_ITKVtkGlue=ON \
+ && make -j4 \
+ && make install \
+ && cd .. \
+ && rm -rf bin src
+
+#Build vv
+RUN mkdir vv \
+ && cd vv \
+ && mkdir src \
+ && mkdir bin \
+ && mkdir build \
+ && git clone https://github.com/open-vv/vv.git src \
+ && cd bin \
+ && cmake ../src/ -DCMAKE_INSTALL_PREFIX="/vv/build/" -DITK_DIR="/ITK/build/lib/cmake/ITK-4.13" -DCLITK_BUILD_TOOLS=ON -DCLITK_BUILD_REGISTRATION=ON -DCLITK_BUILD_SEGMENTATION=ON \
+ && make -j4 \
+ && make install \
+ && cd .. \
+ && rm -rf bin src
+
+RUN echo 'PATH=/VTK/build/bin/:/ITK/build/bin/:/vv/build/bin/:${PATH}'>>~/.bashrc \
+ && echo 'LD_LIBRARY_PATH=/VTK/build/lib/:/ITK/build/lib/:${LD_LIBRARY_PATH}'>>~/.bashrc \
+ && echo 'export LD_LIBRARY_PATH'>>~/.bashrc
+RUN source ~/.bashrc
+RUN ldconfig -v
+
+ENTRYPOINT [ "bash" ]
index e904d36e4641bfd1572030fbd0dbf6fdb1405194..8646a37c095c0e416bfcf420e7f453219493a385 100644 (file)
--- a/README.md
+++ b/README.md
@@ -1,12 +1,12 @@
 
 
-VV, a 4D image viewer, see : http://vv.creatis.insa-lyon.fr
+VV, a 4D image viewer, see : [creatis](http://vv.creatis.insa-lyon.fr)
 
 ### Status
-[![Build Status](https://travis-ci.org/open-vv/vv.svg?branch=master)](https://travis-ci.org/open-vv/vv)
+[![Build Status](https://travis-ci.org/open-vv/vv.svg?branch=master)](https://travis-ci.org/open-vv/vv) [![Quality Gate](https://sonarcloud.io/api/project_badges/measure?project=vv&metric=alert_status)](https://sonarcloud.io/dashboard/index/vv)
 
 ### Download
 You can download the binaries here:
  - [Linux 64bits](https://www.creatis.insa-lyon.fr/rio/vv?action=AttachFile&do=get&target=vv-1.4Qt4-linux64)
  - [Windows 32bits](https://www.creatis.insa-lyon.fr/rio/vv?action=AttachFile&do=get&target=vv-1.4Qt4-win32.zip)
- - [Windows 64bits](https://www.creatis.insa-lyon.fr/rio/vv?action=AttachFile&do=get&target=vv-1.4Qt4-win64.zip)
\ No newline at end of file
+ - [Windows 64bits](https://www.creatis.insa-lyon.fr/rio/vv?action=AttachFile&do=get&target=vv-1.4Qt4-win64.zip)
diff --git a/bw-output/build-wrapper-dump.json b/bw-output/build-wrapper-dump.json
new file mode 100644 (file)
index 0000000..4fe62cf
--- /dev/null
@@ -0,0 +1,11 @@
+# (C) SonarSource SA, 2014-2017, info@sonarsource.com
+# All SONARSOURCE programs and content are copyright protected.
+# SONARSOURCE and SONARQUBE are trademarks of SonarSource SA. All rights are expressly reserved.
+#
+# This file is designed exclusively for use with the SONARSOURCE C / C++ / Objective-C Plugin.
+# It may not be used in connection with any other software.
+# Any other use is prohibited by law and may be grounds for immediate termination of your License.
+{
+"version":0,
+"captures":[
+]}
diff --git a/bw-output/build-wrapper.log b/bw-output/build-wrapper.log
new file mode 100644 (file)
index 0000000..452d8e0
--- /dev/null
@@ -0,0 +1,149 @@
+Wed Feb 14 12:50:05 2018: build-wrapper, version 5.0 (linux-x86)
+Wed Feb 14 12:50:05 2018: System name: Linux Nodename: penicillium Release: 4.4.104-18.44-default Version: #1 SMP Thu Jan 4 08:07:55 UTC 2018 (05a9de6) Machine: x86_64
+Wed Feb 14 12:50:05 2018: socket path: /tmp/build-wrapper-socket.iw074A
+Wed Feb 14 12:50:05 2018: dynamic library found: /home/tbaudier/Software/sonar-scanner-3.0.3.778-linux/build-wrapper-linux-x86/libinterceptor-i686.so
+Wed Feb 14 12:50:05 2018: dynamic library found: /home/tbaudier/Software/sonar-scanner-3.0.3.778-linux/build-wrapper-linux-x86/libinterceptor-x86_64.so
+Wed Feb 14 12:50:05 2018: command executed as: <build-wrapper-linux-x86-64>
+Wed Feb 14 12:50:05 2018: command line received: <make clean all>
+Wed Feb 14 12:50:05 2018: env 0: <XDG_VTNR=7>
+Wed Feb 14 12:50:05 2018: env 1: <G4LEVELGAMMADATA=/home/tbaudier/Software/Geant4/bin/data/PhotonEvaporation4.3>
+Wed Feb 14 12:50:05 2018: env 2: <LESSKEY=/etc/lesskey.bin>
+Wed Feb 14 12:50:05 2018: env 3: <NNTPSERVER=news>
+Wed Feb 14 12:50:05 2018: env 4: <MANPATH=/home/tbaudier/Software/root/bin/man:/home/tbaudier/Software/root/bin/man:/usr/local/man:/usr/local/share/man:/usr/share/man>
+Wed Feb 14 12:50:05 2018: env 5: <SSH_AGENT_PID=3155>
+Wed Feb 14 12:50:05 2018: env 6: <XDG_SESSION_ID=1>
+Wed Feb 14 12:50:05 2018: env 7: <HOSTNAME=penicillium>
+Wed Feb 14 12:50:05 2018: env 8: <XKEYSYMDB=/usr/X11R6/lib/X11/XKeysymDB>
+Wed Feb 14 12:50:05 2018: env 9: <GPG_AGENT_INFO=/tmp/gpg-iRdPNp/S.gpg-agent:3156:1>
+Wed Feb 14 12:50:05 2018: env 10: <G4INSTALL=/home/tbaudier/Software/Geant4/src>
+Wed Feb 14 12:50:05 2018: env 11: <TERM=xterm-256color>
+Wed Feb 14 12:50:05 2018: env 12: <VTE_VERSION=4402>
+Wed Feb 14 12:50:05 2018: env 13: <XDG_MENU_PREFIX=gnome->
+Wed Feb 14 12:50:05 2018: env 14: <HOST=penicillium>
+Wed Feb 14 12:50:05 2018: env 15: <SHELL=/bin/bash>
+Wed Feb 14 12:50:05 2018: env 16: <HISTSIZE=1000>
+Wed Feb 14 12:50:05 2018: env 17: <PROFILEREAD=true>
+Wed Feb 14 12:50:05 2018: env 18: <GJS_DEBUG_OUTPUT=stderr>
+Wed Feb 14 12:50:05 2018: env 19: <G4INCLUDE=/home/tbaudier/Software/Geant4/src/this_is_a_deliberate_dummy_path>
+Wed Feb 14 12:50:05 2018: env 20: <WINDOWID=38044408>
+Wed Feb 14 12:50:05 2018: env 21: <MORE=-sl>
+Wed Feb 14 12:50:05 2018: env 22: <G4NEUTRONXSDATA=/home/tbaudier/Software/Geant4/bin/data/G4NEUTRONXS1.4>
+Wed Feb 14 12:50:05 2018: env 23: <GJS_DEBUG_TOPICS=JS ERROR;JS LOG>
+Wed Feb 14 12:50:05 2018: env 24: <XSESSION_IS_UP=yes>
+Wed Feb 14 12:50:05 2018: env 25: <GTK_MODULES=canberra-gtk-module>
+Wed Feb 14 12:50:05 2018: env 26: <NO_PROXY=localhost,127.0.0.0/8,::1>
+Wed Feb 14 12:50:05 2018: env 27: <G4LEDATA=/home/tbaudier/Software/Geant4/bin/data/G4EMLOW6.50>
+Wed Feb 14 12:50:05 2018: env 28: <USER=tbaudier>
+Wed Feb 14 12:50:05 2018: env 29: <JRE_HOME=/usr/lib64/jvm/jre>
+Wed Feb 14 12:50:05 2018: env 30: <LS_COLORS=no=00:fi=00:di=01;34:ln=00;36:pi=40;33:so=01;35:do=01;35:bd=40;33;01:cd=40;33;01:or=41;33;01:ex=00;32:*.cmd=00;32:*.exe=01;32:*.com=01;32:*.bat=01;32:*.btm=01;32:*.dll=01;32:*.tar=00;31:*.tbz=00;31:*.tgz=00;31:*.rpm=00;31:*.deb=00;31:*.arj=00;31:*.taz=00;31:*.lzh=00;31:*.lzma=00;31:*.zip=00;31:*.zoo=00;31:*.z=00;31:*.Z=00;31:*.gz=00;31:*.bz2=00;31:*.tb2=00;31:*.tz2=00;31:*.tbz2=00;31:*.xz=00;31:*.avi=01;35:*.bmp=01;35:*.fli=01;35:*.gif=01;35:*.jpg=01;35:*.jpeg=01;35:*.mng=01;35:*.mov=01;35:*.mpg=01;35:*.pcx=01;35:*.pbm=01;35:*.pgm=01;35:*.png=01;35:*.ppm=01;35:*.tga=01;35:*.tif=01;35:*.xbm=01;35:*.xpm=01;35:*.dl=01;35:*.gl=01;35:*.wmv=01;35:*.aiff=00;32:*.au=00;32:*.mid=00;32:*.mp3=00;32:*.ogg=00;32:*.voc=00;32:*.wav=00;32:>
+Wed Feb 14 12:50:05 2018: env 31: <LD_LIBRARY_PATH=/home/tbaudier/Software/Geant4/bin/BuildProducts/lib64:/home/tbaudier/Software/root/bin/lib:/usr/local/Qt-5.5.1/lib:/home/tbaudier/Software/elastix/lib/:/home/tbaudier/Software/Geant4/bin/BuildProducts/lib64:/home/tbaudier/Software/root/bin/lib:/usr/local/Qt-5.5.1/lib:/home/tbaudier/Software/elastix/lib/:>
+Wed Feb 14 12:50:05 2018: env 32: <XNLSPATH=/usr/share/X11/nls>
+Wed Feb 14 12:50:05 2018: env 33: <SSH_AUTH_SOCK=/run/user/1036/keyring/ssh>
+Wed Feb 14 12:50:05 2018: env 34: <HOSTTYPE=x86_64>
+Wed Feb 14 12:50:05 2018: env 35: <G4NEUTRONHPDATA=/home/tbaudier/Software/Geant4/bin/data/G4NDL4.5>
+Wed Feb 14 12:50:05 2018: env 36: <QEMU_AUDIO_DRV=pa>
+Wed Feb 14 12:50:05 2018: env 37: <FROM_HEADER=>
+Wed Feb 14 12:50:05 2018: env 38: <SESSION_MANAGER=local/penicillium:@/tmp/.ICE-unix/3075,unix/penicillium:/tmp/.ICE-unix/3075>
+Wed Feb 14 12:50:05 2018: env 39: <LIBPATH=/home/tbaudier/Software/root/bin/lib:/home/tbaudier/Software/root/bin/lib>
+Wed Feb 14 12:50:05 2018: env 40: <G4ENSDFSTATEDATA=/home/tbaudier/Software/Geant4/bin/data/G4ENSDFSTATE2.1>
+Wed Feb 14 12:50:05 2018: env 41: <G4RADIOACTIVEDATA=/home/tbaudier/Software/Geant4/bin/data/RadioactiveDecay5.1>
+Wed Feb 14 12:50:05 2018: env 42: <USERNAME=tbaudier>
+Wed Feb 14 12:50:05 2018: env 43: <CONFIG_SITE=/usr/share/site/x86_64-unknown-linux-gnu>
+Wed Feb 14 12:50:05 2018: env 44: <PAGER=less>
+Wed Feb 14 12:50:05 2018: env 45: <G4ABLADATA=/home/tbaudier/Software/Geant4/bin/data/G4ABLA3.0>
+Wed Feb 14 12:50:05 2018: env 46: <CSHEDIT=emacs>
+Wed Feb 14 12:50:05 2018: env 47: <XDG_CONFIG_DIRS=/etc/xdg>
+Wed Feb 14 12:50:05 2018: env 48: <JUPYTER_PATH=/home/tbaudier/Software/root/bin/etc/notebook:/home/tbaudier/Software/root/bin/etc/notebook>
+Wed Feb 14 12:50:05 2018: env 49: <MINICOM=-c on>
+Wed Feb 14 12:50:05 2018: env 50: <MAIL=/var/spool/mail/tbaudier>
+Wed Feb 14 12:50:05 2018: env 51: <DESKTOP_SESSION=gnome>
+Wed Feb 14 12:50:05 2018: env 52: <PATH=/home/tbaudier/Software/syd/syd/bin/bin:/home/tbaudier/Software/syd/ODB/odb-2.4.0-x86_64-linux-gnu/bin/:/home/tbaudier/geant4_workdir/bin/Linux-g++:/home/tbaudier/Software/Geant4/bin:/home/tbaudier/Software/root/bin/bin:/home/tbaudier/Software/root/bin/bin/root-config:/home/tbaudier/Software/sonar-scanner-3.0.3.778-linux/build-wrapper-linux-x86/:/home/tbaudier/Software/sonar-scanner-3.0.3.778-linux/bin:/home/tbaudier/Software/rtk/bin/bin/:/home/tbaudier/Software/node-v6.11.2-linux-x64/bin/:/home/tbaudier/vv/vv_dynamique/vv/vv_bin/bin:/home/tbaudier/Software/elastix/bin/:/home/tbaudier/Software/3DSlicer/:/home/tbaudier/Software/DocFetcher-1.1.16/:/home/tbaudier/Software/FIJI/:/home/tbaudier/Software/itkSnap/bin/:/home/tbaudier/Software/mendeley/bin/:/home/tbaudier/Software/pycharm-community-5.0.4/bin/:/home/tbaudier/Software/syd/syd/bin/bin:/home/tbaudier/Software/syd/ODB/odb-2.4.0-x86_64-linux-gnu/bin/:/home/tbaudier/geant4_workdir/bin/Linux-g++:/home/tbaudier/Software/Geant4/bin:/home/tbaudier/Software/root/bin/bin:/home/tbaudier/Software/root/bin/bin/root-config:/home/tbaudier/Software/sonar-scanner-3.0.3.778-linux/build-wrapper-linux-x86/:/home/tbaudier/Software/sonar-scanner-3.0.3.778-linux/bin:/home/tbaudier/Software/rtk/bin/bin/:/home/tbaudier/Software/node-v6.11.2-linux-x64/bin/:/home/tbaudier/vv/vv_dynamique/vv/vv_bin/bin:/home/tbaudier/Software/elastix/bin/:/home/tbaudier/Software/3DSlicer/:/home/tbaudier/Software/DocFetcher-1.1.16/:/home/tbaudier/Software/FIJI/:/home/tbaudier/Software/itkSnap/bin/:/home/tbaudier/Software/mendeley/bin/:/home/tbaudier/Software/pycharm-community-5.0.4/bin/:/home/tbaudier/bin:/usr/local/bin:/usr/bin:/bin:/usr/bin/X11:/usr/games>
+Wed Feb 14 12:50:05 2018: env 53: <G4LIB=/home/tbaudier/Software/Geant4/bin/BuildProducts/lib64>
+Wed Feb 14 12:50:05 2018: env 54: <CPU=x86_64>
+Wed Feb 14 12:50:05 2018: env 55: <SYD_CURRENT_DB=/home/tbaudier/david/wsy/src/studies/synfrizz/synfrizz.db>
+Wed Feb 14 12:50:05 2018: env 56: <QT_IM_MODULE=xim>
+Wed Feb 14 12:50:05 2018: env 57: <JAVA_BINDIR=/usr/lib64/jvm/jre/bin>
+Wed Feb 14 12:50:05 2018: env 58: <QT_QPA_PLATFORMTHEME=qgnomeplatform>
+Wed Feb 14 12:50:05 2018: env 59: <G4LIB_BUILD_SHARED=1>
+Wed Feb 14 12:50:05 2018: env 60: <INPUTRC=/etc/inputrc>
+Wed Feb 14 12:50:05 2018: env 61: <PWD=/home/tbaudier/vv/vv_dynamique/vv/vv_src>
+Wed Feb 14 12:50:05 2018: env 62: <G4PIIDATA=/home/tbaudier/Software/Geant4/bin/data/G4PII1.3>
+Wed Feb 14 12:50:05 2018: env 63: <XMODIFIERS=@im=local>
+Wed Feb 14 12:50:05 2018: env 64: <JAVA_HOME=/usr/lib64/jvm/jre>
+Wed Feb 14 12:50:05 2018: env 65: <LANG=en_US.UTF-8>
+Wed Feb 14 12:50:05 2018: env 66: <PYTHONSTARTUP=/etc/pythonstart>
+Wed Feb 14 12:50:05 2018: env 67: <G4SAIDXSDATA=/home/tbaudier/Software/Geant4/bin/data/G4SAIDDATA1.1>
+Wed Feb 14 12:50:05 2018: env 68: <GDMSESSION=gnome>
+Wed Feb 14 12:50:05 2018: env 69: <ROOTSYS=/home/tbaudier/Software/root/bin>
+Wed Feb 14 12:50:05 2018: env 70: <SSH_ASKPASS=/usr/lib/ssh/ssh-askpass>
+Wed Feb 14 12:50:05 2018: env 71: <G4UI_USE_TCSH=1>
+Wed Feb 14 12:50:05 2018: env 72: <AUDIODRIVER=pulseaudio>
+Wed Feb 14 12:50:05 2018: env 73: <GPG_TTY=/dev/pts/5>
+Wed Feb 14 12:50:05 2018: env 74: <SHLVL=1>
+Wed Feb 14 12:50:05 2018: env 75: <HOME=/home/tbaudier>
+Wed Feb 14 12:50:05 2018: env 76: <QT_SYSTEM_DIR=/usr/share/desktop-data>
+Wed Feb 14 12:50:05 2018: env 77: <XDG_SEAT=seat0>
+Wed Feb 14 12:50:05 2018: env 78: <G4SYSTEM=Linux-g++>
+Wed Feb 14 12:50:05 2018: env 79: <LESS_ADVANCED_PREPROCESSOR=no>
+Wed Feb 14 12:50:05 2018: env 80: <ALSA_CONFIG_PATH=/etc/alsa-pulse.conf>
+Wed Feb 14 12:50:05 2018: env 81: <SDL_AUDIODRIVER=pulse>
+Wed Feb 14 12:50:05 2018: env 82: <G4REALSURFACEDATA=/home/tbaudier/Software/Geant4/bin/data/RealSurface1.0>
+Wed Feb 14 12:50:05 2018: env 83: <OSTYPE=linux>
+Wed Feb 14 12:50:05 2018: env 84: <LS_OPTIONS=-N --color=tty -T 0>
+Wed Feb 14 12:50:05 2018: env 85: <XCURSOR_THEME=DMZ>
+Wed Feb 14 12:50:05 2018: env 86: <no_proxy=localhost,127.0.0.0/8,::1>
+Wed Feb 14 12:50:05 2018: env 87: <GNOME_DESKTOP_SESSION_ID=this-is-deprecated>
+Wed Feb 14 12:50:05 2018: env 88: <DYLD_LIBRARY_PATH=/home/tbaudier/Software/root/bin/lib:/home/tbaudier/Software/root/bin/lib>
+Wed Feb 14 12:50:05 2018: env 89: <WINDOWMANAGER=/usr/bin/gnome>
+Wed Feb 14 12:50:05 2018: env 90: <LESS=-M -I -R>
+Wed Feb 14 12:50:05 2018: env 91: <G_FILENAME_ENCODING=@locale,UTF-8,ISO-8859-15,CP1252>
+Wed Feb 14 12:50:05 2018: env 92: <PYTHONPATH=/home/tbaudier/Software/root/bin/lib:/home/tbaudier/Software/root/bin/lib>
+Wed Feb 14 12:50:05 2018: env 93: <XDG_SESSION_DESKTOP=gnome>
+Wed Feb 14 12:50:05 2018: env 94: <LOGNAME=tbaudier>
+Wed Feb 14 12:50:05 2018: env 95: <MACHTYPE=x86_64-suse-linux>
+Wed Feb 14 12:50:05 2018: env 96: <CVS_RSH=ssh>
+Wed Feb 14 12:50:05 2018: env 97: <XDG_DATA_DIRS=/usr/share>
+Wed Feb 14 12:50:05 2018: env 98: <DBUS_SESSION_BUS_ADDRESS=unix:abstract=/tmp/dbus-Np4L04qj8g,guid=eba77b3f63b69f9ea9fde6c95a795e47>
+Wed Feb 14 12:50:05 2018: env 99: <G4WORKDIR=/home/tbaudier/geant4_workdir>
+Wed Feb 14 12:50:05 2018: env 100: <LESSOPEN=lessopen.sh %s>
+Wed Feb 14 12:50:05 2018: env 101: <SHLIB_PATH=/home/tbaudier/Software/root/bin/lib:/home/tbaudier/Software/root/bin/lib>
+Wed Feb 14 12:50:05 2018: env 102: <WINDOWPATH=7>
+Wed Feb 14 12:50:05 2018: env 103: <SYD_PLUGIN=/home/tbaudier/Software/syd/syd/bin/lib:/home/tbaudier/Software/syd/syd/bin/lib:>
+Wed Feb 14 12:50:05 2018: env 104: <XDG_RUNTIME_DIR=/run/user/1036>
+Wed Feb 14 12:50:05 2018: env 105: <CMAKE_PREFIX_PATH=/home/tbaudier/Software/root/bin:/home/tbaudier/Software/root/bin>
+Wed Feb 14 12:50:05 2018: env 106: <DISPLAY=:0>
+Wed Feb 14 12:50:05 2018: env 107: <XDG_CURRENT_DESKTOP=GNOME>
+Wed Feb 14 12:50:05 2018: env 108: <XAUTHLOCALHOSTNAME=penicillium>
+Wed Feb 14 12:50:05 2018: env 109: <GTK_IM_MODULE=cedilla>
+Wed Feb 14 12:50:05 2018: env 110: <LESSCLOSE=lessclose.sh %s %s>
+Wed Feb 14 12:50:05 2018: env 111: <G4LIB_USE_ZLIB=1>
+Wed Feb 14 12:50:05 2018: env 112: <G_BROKEN_FILENAMES=1>
+Wed Feb 14 12:50:05 2018: env 113: <QT_IM_SWITCHER=imsw-multi>
+Wed Feb 14 12:50:05 2018: env 114: <JAVA_ROOT=/usr/lib64/jvm/jre>
+Wed Feb 14 12:50:05 2018: env 115: <XAUTHORITY=/run/gdm/auth-for-tbaudier-QjlGl6/database>
+Wed Feb 14 12:50:05 2018: env 116: <COLORTERM=truecolor>
+Wed Feb 14 12:50:05 2018: env 117: <OLDPWD=/home/tbaudier/vv/vv_dev/vv_travis/src>
+Wed Feb 14 12:50:05 2018: env 118: <_=/home/tbaudier/Software/sonar-scanner-3.0.3.778-linux/build-wrapper-linux-x86/build-wrapper-linux-x86-64>
+Wed Feb 14 12:50:05 2018: executing: <make clean all>
+Wed Feb 14 12:50:05 2018: initializing json file
+Wed Feb 14 12:50:05 2018: process created with pid: 445
+Wed Feb 14 12:50:05 2018: parent pid: 444
+Wed Feb 14 12:50:05 2018: working directory: </home/tbaudier/vv/vv_dynamique/vv/vv_src>
+Wed Feb 14 12:50:05 2018: executable: </home/tbaudier/Software/sonar-scanner-3.0.3.778-linux/build-wrapper-linux-x86/build-wrapper-linux-x86-64>
+Wed Feb 14 12:50:05 2018: argv[0]: <build-wrapper-linux-x86-64>
+Wed Feb 14 12:50:05 2018: argv[1]: <-c>
+Wed Feb 14 12:50:05 2018: argv[2]: <>
+Wed Feb 14 12:50:05 2018: argv[3]: <make>
+Wed Feb 14 12:50:05 2018: argv[4]: <clean>
+Wed Feb 14 12:50:05 2018: argv[5]: <all>
+Wed Feb 14 12:50:05 2018: skipping process with pid: 445
+Wed Feb 14 12:50:05 2018: process created with pid: 446
+Wed Feb 14 12:50:05 2018: parent pid: 445
+Wed Feb 14 12:50:05 2018: working directory: </home/tbaudier/vv/vv_dynamique/vv/vv_src>
+Wed Feb 14 12:50:05 2018: executable: </usr/bin/make>
+Wed Feb 14 12:50:05 2018: argv[0]: <make>
+Wed Feb 14 12:50:05 2018: argv[1]: <clean>
+Wed Feb 14 12:50:05 2018: argv[2]: <all>
+Wed Feb 14 12:50:05 2018: skipping process with pid: 446
+Wed Feb 14 12:50:05 2018: finalizing json file
+Wed Feb 14 12:50:05 2018: returned with code: 2
index 0d2af3b89fe8f0a66e7eeff091ab6d4c11111ba2..f0167f6ee934cec9d524515cbdd6d87a4379a36d 100644 (file)
@@ -17,6 +17,6 @@ if(CLITK_BUILD_TOOLS)
     check_tmp_cluster.sh
     )
 
-  install(FILES ${SCRIPTS} DESTINATION bin PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_EXECUTE WORLD_EXECUTE)
+  install(FILES ${SCRIPTS} DESTINATION bin PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE)
 
 endif(CLITK_BUILD_TOOLS)
old mode 100644 (file)
new mode 100755 (executable)
index 9401268acb080c8e348d88d288feabf78ab30930..be749e50f4f46e1627f219dfd3eb4de34425b2e7 100644 (file)
@@ -40,7 +40,13 @@ echo "PARAM=${PARAM}"
 if test "$RELEASEDIR" = "NONE"
 then
        echo Using $(which Gate)
-       ldd $(which Gate)
+  unamestr=`uname`
+  if [[ "$unamestr" != 'Darwin' ]]; then
+      ldd $(which Gate)
+  else
+      ## ldd "equivalent" on osx is otool
+      otool -L $(which Gate)
+  fi
 else
        test -d "${RELEASEDIR}" || error "can't find release"
        md5sum ${RELEASEDIR}/Gate
@@ -64,7 +70,13 @@ echo "Copying inputs"
 LOCALMACRODIR=$(mktemp -d)
 trap "mv output ${OUTPUTDIR}/output.${PBS_JOBID%%.*} ; rm -r ${LOCALMACRODIR} ; exit 1" 1 2 3 15
 cd ${LOCALMACRODIR}
-cp -r -L "${MACRODIR}"/{data,mac} .
+unamestr=`uname`
+if [[ "$unamestr" == 'Darwin' ]]; then
+    ## cp is slightly different on OSX
+    cp -R -L "${MACRODIR}"/{data,mac} .
+else
+    cp -r -L "${MACRODIR}"/{data,mac} .
+fi
 mkdir output
 
 # Enforce one thread
@@ -74,8 +86,8 @@ echo "Lauching macro"
 date
 if test "$RELEASEDIR" = "NONE"
 then
-        echo Gate ${PARAM} ${MACROFILE}
-       eval Gate ${PARAM} ${MACROFILE} || error "gate failed"
+    echo Gate ${PARAM} ${MACROFILE}
+         eval Gate ${PARAM} ${MACROFILE} || error "gate failed"
 else
        LD_PRELOAD="${ROOTLIBS}${G4LIBS}${CLHEPLIBS}${GATELIBS}" \
        G4LEVELGAMMADATA="${RELEASEDIR}/PhotonEvaporation2.1" \
@@ -92,6 +104,7 @@ else
 fi
 
 echo "Copying data back"
+pwd
 mv output "${OUTPUTDIR}/output.${PBS_JOBID%%.*}"
 
 echo "Cleanup"
index aaf063b18d4f8cc85be856ef0f19df1087c1570d..a0c3e183318d0fbdd17c338dc6e2ecdaa315c303 100755 (executable)
@@ -329,9 +329,9 @@ function merge_dispatcher {
     local firstpartialoutputextension="${firstpartialoutputfile##*.}"
     echo "${indent}testing file type on ${firstpartialoutputfile}"
 
-    if test "${firstpartialoutputextension}" == "hdr" && grep -qs 'INTERFILE' "${firstpartialoutputfile}"
+    if test "${firstpartialoutputextension}" == "hdr" && test grep -qs 'INTERFILE' "${firstpartialoutputfile}"
     then
-        echo "${indent}this is a interfile image"
+        echo "${indent}this is an interfile image"
         echo "${indent}creating mhd headers"
         for partialoutputfile in $partialoutputfiles; do write_mhd_header "${partialoutputfile}"; done
         local mhd_partialoutputfiles="$(for partialoutputfile in $partialoutputfiles; do echo "${partialoutputfile%.*}.mhd"; done)"
@@ -347,7 +347,7 @@ function merge_dispatcher {
 
     if test "${firstpartialoutputextension}" == "hdr"
     then
-        echo "${indent}this is a analyse image"
+        echo "${indent}this is an analyse image"
         local mergedfile="${outputdir}/$(basename "${firstpartialoutputfile}")"
         merge_hdr_image "${mergedfile}" ${partialoutputfiles} || error "error while merging"
         return
@@ -450,7 +450,7 @@ function merge_dispatcher_uncertainty {
 
     if [[ "${firstpartialoutputfile}" == *Uncertainty* ]]
     then
-       if test "${firstpartialoutputextension}" == "mhd" || test "${firstpartialoutputextension}" == "mha"
+         if test "${firstpartialoutputextension}" == "mhd" || test "${firstpartialoutputextension}" == "mha"
         then
             echo "${indent}Uncertainty file found: ${firstpartialoutputfile}"
             ## search for sum
@@ -472,7 +472,14 @@ function merge_dispatcher_uncertainty {
             echo "${indent}${squared_merged_file} found"
             ## search for NumberOfEvent
             totalEvents=0;
-            for outputfile in $(find -L "${rundir}" -regextype 'posix-extended' -type f -regex "${rundir}/output.*\.(hdr|mhd|mha|root|txt)" | awk -F '/' '{ print $NF; }' | sort | uniq)
+            unamestr=`uname`
+            if [[ "$unamestr" == 'Darwin' ]]; then
+                files=$(find -L "${rundir}" -type f -regex ".*output.*[hdr|mhd|mha|root|txt]" | awk -F '/' '{ print $NF; }' | sort | uniq)
+            else
+                files=$(find -L "${rundir}" -regextype 'posix-extended' -type f -regex "${rundir}/output.*\.(hdr|mhd|mha|root|txt)" | awk -F '/' '{ print $NF; }' | sort | uniq)
+            fi
+            echo $files
+            for outputfile in ${files}
             do
                 #echo $outputfile
                 if grep -q 'NumberOfEvent' "${outputdir}/${outputfile}"
@@ -491,7 +498,7 @@ function merge_dispatcher_uncertainty {
                 warning "${totalEvents} not positive. A at least one stat file (SimulationStatisticActor) must be provided. Error, no uncertainty computed"
                 return;
             fi
-       else
+             else
             error "merge_dispatcher_uncertainty does not handle ${firstpartialoutputfile} files"
         fi
     fi
@@ -520,14 +527,30 @@ echo "output dir is ${outputdir}"
 
 test -d "${outputdir}" && rm -r "${outputdir}"
 mkdir "${outputdir}"
-for outputfile in $(find -L "${rundir}" -regextype 'posix-extended' -type f -regex "${rundir}/output.*\.(hdr|mhd|mha|root|txt)" | awk -F '/' '{ print $NF; }' | sort | uniq)
+
+unamestr=`uname`
+if [[ "$unamestr" == 'Darwin' ]]; then
+    files=$(find -L "${rundir}" -type f -regex ".*output.*[hdr|mhd|mha|root|txt]" | awk -F '/' '{ print $NF; }' | sort | uniq)
+else
+    files=$(find -L "${rundir}" -regextype 'posix-extended' -type f -regex "${rundir}/output.*\.(hdr|mhd|mha|root|txt)" | awk -F '/' '{ print $NF; }' | sort | uniq)
+fi
+echo files $files
+for outputfile in ${files}
 do
     merge_dispatcher "${outputfile}" "${force}"
 done
 
 echo ""
 echo "Merging done. Special case for statistical uncertainty"
-for outputfile in $(find -L "${outputdir}" -regextype 'posix-extended' -type f -regex "${outputdir}/.*\.(hdr|mhd|mha|root|txt)" | awk -F '/' '{ print $NF; }' | sort | uniq)
+
+unamestr=`uname`
+if [[ "$unamestr" == 'Darwin' ]]; then
+    files=$(find -L "${outputdir}" -type f -regex ".*[hdr|mhd|mha|root|txt]" | awk -F '/' '{ print $NF; }' | sort | uniq)
+else
+    files=$(find -L "${outputdir}" -regextype 'posix-extended' -type f -regex "${outputdir}/.*\.(hdr|mhd|mha|root|txt)" | awk -F '/' '{ print $NF; }' | sort | uniq)
+fi
+echo files = $files
+for outputfile in ${files}
 do
     merge_dispatcher_uncertainty "${outputfile}" "${force}"
 done
index edd798bf62839fdbaccd39d28e828be2086d808e..a74429a10f7525ac200e0d883bf23f39eec1807d 100755 (executable)
@@ -52,7 +52,16 @@ then
     OUTPUTDIR=$(mktemp -d -p "${MACRODIR}" run.XXXX || error "can't create temp dir")
     ssh -i ${HOME}/.ssh/ccin2p3 linux1.dg.creatis.insa-lyon.fr mkdir -p "cc/$(basename ${OUTPUTDIR})"
 else
-    OUTPUTDIR=$(mktemp --tmpdir=${MACRODIR} -d run.XXXX || error "can't create temp dir")
+    unamestr=`uname`
+    if [[ "$unamestr" == 'Darwin' ]]; then
+        ## On OSX (Darwin), mktemp version is different from Linux version . We
+        ## need the absolute folder here
+        OUTPUTDIR=$(mktemp -d run.XXXX || error "can't create temp dir")
+        OUTPUTDIR=${MACRODIR}/${OUTPUTDIR}
+    else
+        OUTPUTDIR=$(mktemp --tmpdir=${MACRODIR} -d run.XXXX || error "can't create temp dir")
+    fi
+    echo $OUTPUTDIR
 fi
 test -d ${OUTPUTDIR} || error "can't locate output dir"
 RUNID=${OUTPUTDIR##*.}
@@ -104,14 +113,15 @@ while test $NJOBS -gt 0; do
     then
         PROJECTGROUP=creatis
         qsub -o "${OUTPUTDIR}" \
-            -l sps=1 \
+             -e "${OUTPUTDIR}" \
+             -l sps=1 \
              -N "gate.${RUNID}" \
              -v "PARAM=\"${PARAM}\",INDEX=${NJOBS},INDEXMAX=${NJOBSMAX},OUTPUTDIR=${OUTPUTDIR},RELEASEDIR=${RELEASEDIR},MACROFILE=${MACROFILE},MACRODIR=${MACRODIR}" \
              "${JOBFILE}" || error "submission error"
     else
         qsub -N "gatejob.${RUNID}" -o "${OUTPUTDIR}" \
-           -v "PARAM=${PARAM},INDEX=${NJOBS},INDEXMAX=${NJOBSMAX},OUTPUTDIR=${OUTPUTDIR},RELEASEDIR=${RELEASEDIR},MACROFILE=${MACROFILE},MACRODIR=${MACRODIR}" \
-           "${JOBFILE}" || error "submission error"
+      -v "PARAM=${PARAM},INDEX=${NJOBS},INDEXMAX=${NJOBSMAX},OUTPUTDIR=${OUTPUTDIR},RELEASEDIR=${RELEASEDIR},MACROFILE=${MACROFILE},MACRODIR=${MACRODIR}" \
+      "${JOBFILE}" || error "submission error"
     fi
 
     let NJOBS--
old mode 100644 (file)
new mode 100755 (executable)
index 47ad699..c3eb2f0
@@ -90,9 +90,9 @@ while test $NJOBS -gt 0; do
         echo "Launching Gate log in ${OUTPUTDIR}/gate_${NJOBS}.log"
         PARAM=\"${PARAM}\" INDEX=${NJOBS} INDEXMAX=${NJOBSMAX} OUTPUTDIR=${OUTPUTDIR}  RELEASEDIR=${RELEASEDIR} MACROFILE=${MACROFILE} MACRODIR=${MACRODIR} PBS_JOBID="local_${NJOBS}" bash "${JOBFILE}" > ${OUTPUTDIR}/gate_${NJOBS}.log &
     else
-       qsub -N "gatejob.${RUNID}" -o "${OUTPUTDIR}" \
-           -v "PARAM=${PARAM},INDEX=${NJOBS},INDEXMAX=${NJOBSMAX},OUTPUTDIR=${OUTPUTDIR},RELEASEDIR=${RELEASEDIR},MACROFILE=${MACROFILE},MACRODIR=${MACRODIR}" \
-           "${JOBFILE}" || error "submission error"
+  qsub -N "gatejob.${RUNID}" -o "${OUTPUTDIR}" -e "${OUTPUTDIR}" \
+      -v "PARAM=${PARAM},INDEX=${NJOBS},INDEXMAX=${NJOBSMAX},OUTPUTDIR=${OUTPUTDIR},RELEASEDIR=${RELEASEDIR},MACROFILE=${MACROFILE},MACRODIR=${MACRODIR}" \
+      "${JOBFILE}" || error "submission error"
     fi
 
     let NJOBS--
index 54a35317f71a5373d213f1f3d52b152ae0869f59..ff66917f4d20d96c41247239f9b978420aa78c74 100644 (file)
@@ -22,6 +22,8 @@ option(CLITK_EXPERIMENTAL "Enable experimental software and features" OFF)
 mark_as_advanced(CLITK_EXPERIMENTAL)
 option(CLITK_MEMORY_INFO "Enable memory info (need libstatgrab >= v.0.90)" OFF)
 mark_as_advanced(CLITK_MEMORY_INFO)
+option(CLITK_EXTERNAL_GDCM "Enable features using external GDCM" OFF)
+mark_as_advanced(CLITK_EXTERNAL_GDCM)
 option(CLITK_BUILD_TOOLS "Build command-line tools" OFF)
 option(CLITK_BUILD_SEGMENTATION "Build command-line segmentation tools" OFF)
 option(CLITK_BUILD_REGISTRATION "Build command-line registration tools" OFF)
index a871fecd0d19523b2fa28823856adcf5637c32ed..25b613bf4a069a8a9c6e015184486a366700cf27 100644 (file)
@@ -3,6 +3,9 @@
 find_package(ITK)
 if(ITK_FOUND)
   include("${ITK_USE_FILE}")
+  if(ITK_VERSION VERSION_GREATER 5.0.0 OR ITK_VERSION VERSION_EQUAL 5.0.0)
+    set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
+  endif(ITK_VERSION VERSION_GREATER 5.0.0 OR ITK_VERSION VERSION_EQUAL 5.0.0)
 else(ITK_FOUND)
   message(FATAL_ERROR "Cannot build without ITK.  Please set ITK_DIR.")
 endif(ITK_FOUND)
@@ -57,11 +60,11 @@ endif()
 
 
 #=========================================================
-### Check if ITK was compiled with SYSTEM_GDCM = ON
+### Check if ITK was compiled with SYSTEM_GDCM = ON (Not possible anymore with ITK 4.13)
+### Add option to activate external GDCM in clitk
 set(CLITK_USE_SYSTEM_GDCM FALSE)
-# ITK4 creates a target for each gdcm library when it compiles GDCM
-get_target_property(GDCMDICTTARG gdcmDICT TYPE )
-if(NOT GDCMDICTTARG)
+if(CLITK_EXTERNAL_GDCM)
+  message("Be sure to use external GDCM with ITK")
   set(CLITK_USE_SYSTEM_GDCM TRUE)
 endif()
 
diff --git a/common/clitkChangeDicomTagGenericFilter.h b/common/clitkChangeDicomTagGenericFilter.h
new file mode 100644 (file)
index 0000000..9780446
--- /dev/null
@@ -0,0 +1,112 @@
+/*=========================================================================
+  Program:   vv                     http://www.creatis.insa-lyon.fr/rio/vv
+
+  Authors belong to: 
+  - University of LYON              http://www.universite-lyon.fr/
+  - Léon Bérard cancer center       http://www.centreleonberard.fr
+  - CREATIS CNRS laboratory         http://www.creatis.insa-lyon.fr
+
+  This software is distributed WITHOUT ANY WARRANTY; without even
+  the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+  PURPOSE.  See the copyright notices for more information.
+
+  It is distributed under dual licence
+
+  - BSD        See included LICENSE.txt file
+  - CeCILL-B   http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.html
+===========================================================================**/
+#ifndef clitkChangeDicomTagGenericFilter_h
+#define clitkChangeDicomTagGenericFilter_h
+
+/* =================================================
+ * @file   clitkChangeDicomTagGenericFilter.h
+ * @author 
+ * @date   
+ * 
+ * @brief 
+ * 
+ ===================================================*/
+
+
+// clitk include
+#include "clitkChangeDicomTag_ggo.h"
+
+//itk include
+#include "itkGDCMImageIO.h"
+#include "itkMetaDataDictionary.h"
+#include "itkGDCMSeriesFileNames.h"
+#include <vector>
+#include <itksys/SystemTools.hxx>
+
+namespace clitk 
+{
+  template<class args_info_type>
+  class ITK_EXPORT ChangeDicomTagGenericFilter : public itk::LightObject
+  {
+  public:
+    //----------------------------------------
+    // ITK
+    //----------------------------------------
+    typedef ChangeDicomTagGenericFilter                   Self;
+    typedef itk::LightObject                   Superclass;
+    typedef itk::SmartPointer<Self>            Pointer;
+    typedef itk::SmartPointer<const Self>      ConstPointer;
+   
+    // Method for creation through the object factory
+    itkNewMacro(Self);  
+
+    // Run-time type information (and related methods)
+    itkTypeMacro( ChangeDicomTagGenericFilter, LightObject );
+
+
+    //----------------------------------------
+    // Typedefs
+    //----------------------------------------
+
+
+    //----------------------------------------
+    // Set & Get
+    //----------------------------------------    
+    void SetArgsInfo(const args_info_type & a)
+    {
+      m_ArgsInfo=a;
+      m_Verbose=m_ArgsInfo.verbose_flag;
+    }
+    
+    
+    //----------------------------------------  
+    // Update
+    //----------------------------------------  
+    void Update();
+
+  protected:
+
+    //----------------------------------------  
+    // Constructor & Destructor
+    //----------------------------------------  
+    ChangeDicomTagGenericFilter();
+    ~ChangeDicomTagGenericFilter() {};
+
+    
+    //----------------------------------------  
+    // Templated members
+    //----------------------------------------  
+    void UpdateWithDimAndPixelType();
+
+
+    //----------------------------------------  
+    // Data members
+    //----------------------------------------
+    args_info_type m_ArgsInfo;
+    bool m_Verbose;
+
+  };
+
+
+} // end namespace clitk
+
+#ifndef ITK_MANUAL_INSTANTIATION
+#include "clitkChangeDicomTagGenericFilter.txx"
+#endif
+
+#endif // #define clitkChangeDicomTagGenericFilter_h
diff --git a/common/clitkChangeDicomTagGenericFilter.txx b/common/clitkChangeDicomTagGenericFilter.txx
new file mode 100644 (file)
index 0000000..0bb9882
--- /dev/null
@@ -0,0 +1,122 @@
+/*=========================================================================
+  Program:   vv                     http://www.creatis.insa-lyon.fr/rio/vv
+
+  Authors belong to:
+  - University of LYON              http://www.universite-lyon.fr/
+  - Léon Bérard cancer center       http://www.centreleonberard.fr
+  - CREATIS CNRS laboratory         http://www.creatis.insa-lyon.fr
+
+  This software is distributed WITHOUT ANY WARRANTY; without even
+  the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+  PURPOSE.  See the copyright notices for more information.
+
+  It is distributed under dual licence
+
+  - BSD        See included LICENSE.txt file
+  - CeCILL-B   http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.html
+===========================================================================**/
+#ifndef clitkChangeDicomTagGenericFilter_txx
+#define clitkChangeDicomTagGenericFilter_txx
+
+/* =================================================
+ * @file   clitkChangeDicomTagGenericFilter.txx
+ * @author
+ * @date
+ *
+ * @brief
+ *
+ ===================================================*/
+
+// clitk
+#include "clitkResampleImageWithOptionsFilter.h"
+#if GDCM_MAJOR_VERSION >= 2
+#include "gdcmUIDGenerator.h"
+#include <gdcmImageHelper.h>
+#include <gdcmAttribute.h>
+#include <gdcmReader.h>
+#include <gdcmWriter.h>
+#include <gdcmDataElement.h>
+#include <gdcmTag.h>
+#else
+#include "gdcmFile.h"
+#include "gdcmUtil.h"
+#endif
+
+
+namespace clitk
+{
+
+
+//-----------------------------------------------------------
+// Constructor
+//-----------------------------------------------------------
+template<class args_info_type>
+ChangeDicomTagGenericFilter<args_info_type>::ChangeDicomTagGenericFilter()
+{
+  m_Verbose=false;
+}
+
+
+//-----------------------------------------------------------
+// Update
+//-----------------------------------------------------------
+template<class args_info_type>
+void ChangeDicomTagGenericFilter<args_info_type>::Update()
+{
+  UpdateWithDimAndPixelType();
+}
+
+//-------------------------------------------------------------------
+// Update with the number of dimensions and the pixeltype read from
+// the dicom files. The MHD files may be resampled to match the
+// dicom spacing (and number of slices). Rounding errors in resampling
+// are handled by removing files when generating the output dicom
+// series.
+//-------------------------------------------------------------------
+template<class args_info_type>
+void
+ChangeDicomTagGenericFilter<args_info_type>::UpdateWithDimAndPixelType()
+{
+  //Read the dicom file
+  gdcm::Reader reader;
+  reader.SetFileName( m_ArgsInfo.input_arg );
+  reader.Read();
+  gdcm::File &fileOutput = reader.GetFile();
+  gdcm::DataSet &dsOutput = fileOutput.GetDataSet();
+  const unsigned int ptr_len = 42;
+  char *ptr = new char[ptr_len];
+  memset(ptr,0,ptr_len);
+
+  unsigned int numberOfKeysGiven=m_ArgsInfo.key_given;
+  // For all input tag
+  for (unsigned int i = 0; i < numberOfKeysGiven; i++) {
+    // Split the tag element separated with '|' in 2 parts
+    char* copy = (char*)m_ArgsInfo.key_arg[i];
+    std::vector<char*> v;
+    char* chars_array = strtok(copy, "|");
+    while(chars_array) {
+      v.push_back(chars_array);
+      chars_array = strtok(NULL, "|");
+    }
+    std::stringstream str0(v[0]), str1(v[1]);
+    int first, second;
+    str0 >> first;
+    str1 >> second;
+
+    //Change the tag element by the corresponding value
+    gdcm::DataElement tagDE( gdcm::Tag(first, second) );
+    std::string value( m_ArgsInfo.tag_arg[i] );
+    tagDE.SetByteValue(value.c_str(), (uint32_t)strlen(value.c_str()));
+    dsOutput.Insert(tagDE);
+  }
+
+  //Write the output dicom file
+  gdcm::Writer w;
+  w.SetFile( fileOutput );
+  w.SetFileName( m_ArgsInfo.output_arg );
+  w.Write();
+}
+
+}//end clitk
+
+#endif //#define clitkChangeDicomTagGenericFilter_txx
index 57be29e5297f833d53769818b0aa097cb3d93a6c..c71bd5078fbf5598e7982eb81287204c1c26c694 100644 (file)
 #ifndef CLITKCOMMON_CXX
 #define CLITKCOMMON_CXX
 
+#include <itksys/SystemTools.hxx>
+
+#ifdef _WIN32
+  #define _USE_MATH_DEFINES
+  #include <cmath>
+  const double M_PI = std::acos(-1.0);
+#endif
+
 // clitk include 
 #include "clitkCommon.h"
 
@@ -69,6 +77,24 @@ std::string clitk::GetExtension(const std::string& filename)
 } ////
 //------------------------------------------------------------------
 
+
+//------------------------------------------------------------------
+// Return filename splitting in 1 or 2 parts : directory name (if exists) & filename
+std::vector<std::string> clitk::SplitFilename(const std::string& filename)
+{
+  std::vector<std::string> dirname;
+  std::string path = itksys::SystemTools::GetFilenamePath(filename);
+  std::vector<std::string> pathComponents;
+  itksys::SystemTools::SplitPath(filename.c_str(), pathComponents);
+  std::string fileName = pathComponents.back();
+  if (path != "")
+    dirname.push_back(path);
+  dirname.push_back(fileName);
+  return( dirname );
+} ////
+//------------------------------------------------------------------
+
+
 //------------------------------------------------------------------
 // Display progression
 void clitk::VerboseInProgress(const int nb, const int current, const int percentage)
index d7cd4606d4183fe69f36ad4d6d76b69723faa651..3729584686bdd1d2510d4603000336fe307a0443 100644 (file)
@@ -99,6 +99,9 @@ namespace clitk {
   // Return filename extension
   std::string GetExtension(const std::string& filename);
 
+  // Return filename splitting in 1 or 2 parts : directory name (if exists) & filename
+  std::vector<std::string> SplitFilename(const std::string& filename);
+
   //--------------------------------------------------------------------
   // Convert float, double ... to string
   template<class T> std::string toString(const T & t);
index fcc9e2be26da9c02cfdcb1a94f10e5ff67874745..29ca84fab88f9c62fe13822f70f80e34562210df 100644 (file)
@@ -31,6 +31,7 @@
 #include <vtkImageStencil.h>
 #include <vtkLinearExtrusionFilter.h>
 #include <vtkMetaImageWriter.h>
+#include <vtkXMLPolyDataWriter.h>
 
 
 //--------------------------------------------------------------------
@@ -38,6 +39,7 @@ clitk::DicomRTStruct2ImageFilter::DicomRTStruct2ImageFilter()
 {
   mROI = NULL;
   mWriteOutput = false;
+  mWriteMesh = true;
   mCropMask = true;
 }
 //--------------------------------------------------------------------
@@ -83,6 +85,14 @@ void clitk::DicomRTStruct2ImageFilter::SetCropMaskEnabled(bool b)
 //--------------------------------------------------------------------
 
 
+//--------------------------------------------------------------------
+void clitk::DicomRTStruct2ImageFilter::SetWriteMesh(bool b)
+{
+  mWriteMesh = b;
+}
+//--------------------------------------------------------------------
+
+
 //--------------------------------------------------------------------
 void clitk::DicomRTStruct2ImageFilter::SetOutputImageFilename(std::string s)
 {
@@ -102,10 +112,15 @@ void clitk::DicomRTStruct2ImageFilter::SetImage(vvImage::Pointer image)
   mSpacing.resize(3);
   mOrigin.resize(3);
   mSize.resize(3);
+  mDirection.resize(3);
+  mTransformMatrix = image->GetTransform()[0]->GetMatrix();
   for(unsigned int i=0; i<3; i++) {
     mSpacing[i] = image->GetSpacing()[i];
     mOrigin[i] = image->GetOrigin()[i];
     mSize[i] = image->GetSize()[i];
+    mDirection[i].resize(3);
+    for(unsigned int j=0; j<3; j++)
+      mDirection[i][j] = image->GetDirection()[i][j];
   }
 }
 //--------------------------------------------------------------------
@@ -125,10 +140,14 @@ void clitk::DicomRTStruct2ImageFilter::SetImageFilename(std::string f)
   mSpacing.resize(3);
   mOrigin.resize(3);
   mSize.resize(3);
+  mDirection.resize(3);
   for(unsigned int i=0; i<3; i++) {
     mSpacing[i] = header->GetSpacing(i);
     mOrigin[i] = header->GetOrigin(i);
     mSize[i] = header->GetDimensions(i);
+    mDirection[i].resize(3);
+    for(unsigned int j=0; j<3; j++)
+      mDirection[i][j] = header->GetDirection(i)[j];
   }
 }
 //--------------------------------------------------------------------
@@ -171,11 +190,55 @@ void clitk::DicomRTStruct2ImageFilter::Update()
   }
 
   // Get Mesh
-  vtkPolyData * mesh = mROI->GetMesh();  
+  vtkPolyData * mesh = mROI->GetMesh();
+  if (mWriteMesh) {
+    vtkSmartPointer<vtkXMLPolyDataWriter> meshWriter = vtkSmartPointer<vtkXMLPolyDataWriter>::New();
+    std::string vtkName = mOutputFilename;
+    vtkName += ".vtk";
+    meshWriter->SetFileName(vtkName.c_str());
+#if VTK_MAJOR_VERSION <= 5
+    meshWriter->SetInput(mesh);
+#else
+    meshWriter->SetInputData(mesh);
+#endif
+    meshWriter->Write();
+  }
 
   // Get bounds
   double *bounds=mesh->GetBounds();
 
+  //Change mOrigin, mSize and mSpacing with respect to the directions
+  // Spacing is influenced by input direction
+  std::vector<double> tempSpacing;
+  tempSpacing.resize(3);
+  for(int i=0; i<3; i++) {
+    tempSpacing[i] = 0.0;
+    for(int j=0; j<3; j++) {
+      tempSpacing[i] += mDirection[i][j] * mSpacing[j];
+    }
+  }
+  for(int i=0; i<3; i++)
+    mSpacing[i] = tempSpacing[i];
+
+  // Size is influenced by affine transform matrix and input direction
+  // Size is converted to double, transformed and converted back to size type.
+  std::vector<double> tempSize;
+  tempSize.resize(3);
+  for(int i=0; i<3; i++) {
+    tempSize[i] = 0.0;
+    for(int j=0; j<3; j++) {
+      tempSize[i] += mDirection[i][j] * mSize[j];
+    }
+  }
+  for(int i=0; i<3; i++) {
+    if (tempSize[i] < 0.0) {
+      tempSize[i] *= -1;
+      mOrigin[i] += mSpacing[i]*(tempSize[i] -1);
+      mSpacing[i] *= -1;
+    }
+    mSize[i] = lrint(tempSize[i]);
+  }
+
   // Compute origin
   std::vector<double> origin;
   origin.resize(3);
@@ -189,7 +252,6 @@ void clitk::DicomRTStruct2ImageFilter::Update()
   extend[0] = ceil((bounds[1]-origin[0])/mSpacing[0]+4);
   extend[1] = ceil((bounds[3]-origin[1])/mSpacing[1]+4);
   extend[2] = ceil((bounds[5]-origin[2])/mSpacing[2]+4);
-
   // If no crop, set initial image size/origin
   if (!mCropMask) {
     for(int i=0; i<3; i++) {
index 0df00dd46fcfca8a0bf86647487b26156b462eaf..f93cbb2969ca833f70682d1307b7f4993eed4cb0 100644 (file)
@@ -23,6 +23,7 @@
 #include "clitkDicomRT_ROI.h"
 #include "clitkImageCommon.h"
 #include <vtkImageData.h>
+#include <vtkMatrix4x4.h>
 #include <itkImage.h>
 #include <itkVTKImageToImageFilter.h>
 
@@ -43,6 +44,7 @@ namespace clitk {
     void SetOutputSpacing(const double* spacing);
     void SetOutputSize(const unsigned long* size);
     void SetOutputImageFilename(std::string s);
+    void SetWriteMesh(bool b);
     void Update();    
     vtkImageData * GetOutput();
     template <int Dimension> typename itk::Image<unsigned char,Dimension>::ConstPointer GetITKOutput();
@@ -52,11 +54,14 @@ namespace clitk {
   protected:
     bool ImageInfoIsSet() const;
     bool mWriteOutput;
+    bool mWriteMesh;
     bool mCropMask;
     std::string mOutputFilename;
     std::vector<double> mSpacing;
     std::vector<double> mOrigin;
     std::vector<unsigned long> mSize;
+    std::vector< std::vector< double> > mDirection;
+    vtkSmartPointer<vtkMatrix4x4> mTransformMatrix;
     clitk::DicomRT_ROI * mROI;
     vtkSmartPointer<vtkImageData> mBinaryImage;
   };
index ee55e44bc704986ce736c82e19fa7a9c423b145b..28fd054102ab0d62ed6ecdd32653bed7bf03d3fb 100644 (file)
@@ -82,7 +82,11 @@ void clitk::DicomRT_Contour::UpdateDicomItem()
     double * p = mData->GetPoint(i);
     points[i*3] = p[0];
     points[i*3+1] = p[1];
-    points[i*3+2] = p[2];
+#if VTK_MAJOR_VERSION <= 5
+    points[i*3+1] = p[2];
+#else
+    points[i*3+1] = p[2]-0.5;
+#endif
   }
 
   // Get attribute
@@ -157,7 +161,11 @@ bool clitk::DicomRT_Contour::Read(gdcm::Item * item)
     double p[3];
     p[0] = points[i*3];
     p[1] = points[i*3+1];
+#if VTK_MAJOR_VERSION <= 5
     p[2] = points[i*3+2];
+#else
+    p[2] = points[i*3+2]+0.5;
+#endif
     mData->SetPoint(i, p);
     if (mZ == -1) mZ = p[2];
     if (p[2] != mZ) {
@@ -204,7 +212,11 @@ bool clitk::DicomRT_Contour::Read(gdcm::SQItem * item)
     double p[3];
     p[0] = points[i*3];
     p[1] = points[i*3+1];
+#if VTK_MAJOR_VERSION <= 5
     p[2] = points[i*3+2];
+#else
+    p[2] = points[i*3+2]+0.5;
+#endif
     mData->SetPoint(i, p);
     if (mZ == -1) mZ = p[2];
     if (p[2] != mZ) {
@@ -241,6 +253,14 @@ void clitk::DicomRT_Contour::SetMesh(vtkPolyData * mesh)
 //--------------------------------------------------------------------
 
 
+//--------------------------------------------------------------------
+void clitk::DicomRT_Contour::SetTransformMatrix(vtkMatrix4x4* matrix)
+{
+  mTransformMatrix = matrix;
+}
+//--------------------------------------------------------------------
+
+
 //--------------------------------------------------------------------
 void clitk::DicomRT_Contour::ComputeMeshFromDataPoints()
 {
@@ -251,13 +271,25 @@ void clitk::DicomRT_Contour::ComputeMeshFromDataPoints()
   mMesh->SetPoints(mPoints);
   vtkIdType ids[2];
   for (unsigned int idx=0 ; idx<mNbOfPoints ; idx++) {
+    double pointIn[4];
+    for (unsigned int j=0 ; j<3; ++j)
+      pointIn[j] = mData->GetPoint(idx)[j];
+    pointIn[3] = 1.0;
+    /*double pointOut[4];
+    mTransformMatrix->MultiplyPoint(pointIn, pointOut);
+    std::cout << pointOut[0] << " " << pointOut[1] << " " << pointOut[2] << " " << pointOut[3] << std::endl;
+    mMesh->GetPoints()->InsertNextPoint(pointOut[0],
+                                        pointOut[1],
+                                        pointOut[2]);*/
     mMesh->GetPoints()->InsertNextPoint(mData->GetPoint(idx)[0],
                                         mData->GetPoint(idx)[1],
                                         mData->GetPoint(idx)[2]);
+    //std::cout << mData->GetPoint(idx)[0] << " " << mData->GetPoint(idx)[1] << " " << mData->GetPoint(idx)[2] << std::endl;
     ids[0]=idx;
     ids[1]=(ids[0]+1) % mNbOfPoints; //0-1,1-2,...,n-1-0
     mMesh->GetLines()->InsertNextCell(2,ids);
   }
+  //std::cout << std::endl;
   mMeshIsUpToDate = true;
 }
 //--------------------------------------------------------------------
index 78a5bfde6519030fa4393eb0247a87319bb37047..077599f29da2bc84024bdf04625b5b25d4c74cb0 100644 (file)
@@ -30,6 +30,7 @@
 #include <vtkPoints.h>
 #include <vtkPolyData.h>
 #include <vtkSmartPointer.h>
+#include <vtkMatrix4x4.h>
 
 namespace clitk {
 
@@ -54,6 +55,7 @@ public:
   void SetMesh(vtkPolyData * mesh);
   vtkPoints * GetPoints() {return mData;}
   double GetZ() const {return mZ;}
+  void SetTransformMatrix(vtkMatrix4x4* matrix);
   
   
 protected:
@@ -64,6 +66,7 @@ protected:
   vtkSmartPointer<vtkPoints> mData;
   vtkSmartPointer<vtkPolyData> mMesh;
   vtkSmartPointer<vtkPoints> mPoints;
+  vtkSmartPointer<vtkMatrix4x4> mTransformMatrix;
   bool mMeshIsUpToDate;
   ///Z location of the contour
   double mZ;
index b6af9c321d5d2a2921cc3e65b352d872b16cde48..4135b3178026941f492c1c5a0d021d8e22248719 100644 (file)
@@ -211,6 +211,7 @@ bool clitk::DicomRT_ROI::Read(gdcm::Item * itemInfo, gdcm::Item * itemContour)
     {
       gdcm::Item & j = sqi2->GetItem(i+1); // Item start at #1
       DicomRT_Contour::Pointer c = DicomRT_Contour::New();
+      c->SetTransformMatrix(mTransformMatrix);
       bool b = c->Read(&j);
       if (b) {
         mListOfContours.push_back(c);
@@ -237,6 +238,7 @@ void clitk::DicomRT_ROI::Read(std::map<int, std::string> & rois, gdcm::SQItem *
     int i=0;
     for(gdcm::SQItem* j=contours->GetFirstSQItem(); j!=0; j=contours->GetNextSQItem()) {
       DicomRT_Contour::Pointer c = DicomRT_Contour::New();
+      c->SetTransformMatrix(mTransformMatrix);
       bool b = c->Read(j);
       if (b) {
         mListOfContours.push_back(c);
@@ -360,7 +362,6 @@ void clitk::DicomRT_ROI::SetFromBinaryImage(vvImage::Pointer image, int n,
                                             std::vector<double> color, 
                                             std::string filename)
 {
-
   // ROI number [Referenced ROI Number]
   mNumber = n;
 
@@ -388,6 +389,14 @@ vvImage * clitk::DicomRT_ROI::GetImage() const
 //--------------------------------------------------------------------
 
 
+//--------------------------------------------------------------------
+void clitk::DicomRT_ROI::SetTransformMatrix(vtkMatrix4x4* matrix)
+{
+  mTransformMatrix = matrix;
+}
+//--------------------------------------------------------------------
+
+
 //--------------------------------------------------------------------
 void clitk::DicomRT_ROI::ComputeContoursFromImage()
 {
@@ -519,6 +528,7 @@ void clitk::DicomRT_ROI::Read(vtkSmartPointer<vtkGDCMPolyDataReader> & reader, i
   // Get the contour
   mMesh =  reader->GetOutput(roiindex);  
   DicomRT_Contour::Pointer c = DicomRT_Contour::New();
+  c->SetTransformMatrix(mTransformMatrix);
   c->SetMesh(mMesh); // FIXME no GetZ, not GetPoints  
   mMeshIsUpToDate = true;
   mListOfContours.push_back(c);
index 80cbacc03c2ed00c13a3605cd30df9adba5244ca..ec43538aefffc95ab9f0e18f40e025d1a204e2b8 100644 (file)
@@ -23,6 +23,7 @@
 #include "clitkDicomRT_Contour.h"
 #include "vvImage.h"
 
+#include <vtkMatrix4x4.h>
 #include "clitkConfiguration.h"
 #if CLITK_USE_SYSTEM_GDCM == 1
 #include <vtkGDCMPolyDataReader.h>
@@ -66,6 +67,8 @@ public:
   void SetImage(vvImage::Pointer im);
   DicomRT_Contour* GetContour(int n);
 
+  void SetTransformMatrix(vtkMatrix4x4* matrix);
+
   // Compute a vtk mesh from the dicom contours
   void ComputeMeshFromContour();
   void ComputeContoursFromImage();
@@ -94,6 +97,7 @@ protected:
   std::vector<double> mColor;
   std::vector<DicomRT_Contour::Pointer> mListOfContours;
   vtkSmartPointer<vtkPolyData> mMesh;
+  vtkSmartPointer<vtkMatrix4x4> mTransformMatrix;
   bool mMeshIsUpToDate;
   vvImage::Pointer mImage;
   double mBackgroundValue;
index 9fa96b39f2a55c0e25f9b0e97bfd64dca2718a8a..49d90bd78afbb926228dd0592ae4df4d0c79b4ee 100644 (file)
@@ -91,6 +91,14 @@ const std::string & clitk::DicomRT_StructureSet::GetDate() const
 //--------------------------------------------------------------------
 
 
+//--------------------------------------------------------------------
+void clitk::DicomRT_StructureSet::SetTransformMatrix(vtkMatrix4x4* matrix)
+{
+  mTransformMatrix = matrix;
+}
+//--------------------------------------------------------------------
+
+
 //--------------------------------------------------------------------
 const std::string & clitk::DicomRT_StructureSet::GetTime() const
 {
@@ -439,6 +447,7 @@ void clitk::DicomRT_StructureSet::Read(const std::string & filename)
     int nb = i->first;//ReadROINumber(i);//mROIIndex[i];
     // Create the roi
     mROIs[nb] = DicomRT_ROI::New();
+    mROIs[nb]->SetTransformMatrix(mTransformMatrix);
     mROIs[nb]->Read(mMapOfROIInfo[nb], mMapOfROIContours[nb]);
   }
     
index c26122ee1976884fe0fa8c0ce887bc370150ef69..7932051cc44aeffdeda4b530e934b7b42a333396 100644 (file)
@@ -24,6 +24,9 @@
 #include "clitkCommon.h" 
 #include "clitkDicomRT_ROI.h"
 
+//vtk
+#include "vtkMatrix4x4.h"
+
 // vv
 #include "vvImage.h"
 
@@ -58,6 +61,7 @@ public:
 
   void Print(std::ostream & os = std::cout) const;
   void Read(const std::string & filename);
+  void SetTransformMatrix(vtkMatrix4x4* matrix);
   bool IsDicomRTStruct(const std::string & filename);
   void Write(const std::string & filename);
 
@@ -90,6 +94,7 @@ protected:
   std::string mName;
   std::string mDate;
   std::string mTime;
+  vtkSmartPointer<vtkMatrix4x4> mTransformMatrix;
 
   std::map<int, clitk::DicomRT_ROI::Pointer> mROIs;
   std::map<int, std::string> mMapOfROIName;
index 9ebde04d0c878cd8dac885da978c7e9ea332af67..7325b69edfc81fd9bafb3d297ed09b40e51c7092 100644 (file)
@@ -114,7 +114,6 @@ void clitk::Image2DicomRTStructFilter<PixelType>::Update()
     std::cout << "Number of structures in the dicom-rt-struct : " 
               << p->GetNumberOfStructureSetROIs() << std::endl;
   }
-  
 
   // number of additional contours
   int m = m_InputFilenames.size();
@@ -138,14 +137,14 @@ void clitk::Image2DicomRTStructFilter<PixelType>::Update()
   roiNames->SetNumberOfValues(numMasks);
   roiAlgorithms->SetNumberOfValues(numMasks);
   roiTypes->SetNumberOfValues(numMasks);
-  
+
   // Convert the image into a mesh
   std::vector<vtkSmartPointer<vtkPolyData> > meshes;
   std::vector<std::string> m_ROINames;
   meshes.resize(m);
   m_ROINames.resize(m);
   for(unsigned int i=0; i<m; i++) {
-    
+
     // read image
     //    typedef float PixelType;
     //typedef itk::Image<PixelType, 3> ImageType;
@@ -156,7 +155,7 @@ void clitk::Image2DicomRTStructFilter<PixelType>::Update()
       GetFilenameName(vtksys::SystemTools::GetFilenameWithoutLastExtension(m_InputFilenames[i]));
     std::string name = oss.str();
     m_ROINames[i] = name;
-    
+
     // convert to mesh
     typedef clitk::BinaryImageToMeshFilter<ImageType> BinaryImageToMeshFilterType;
     typename BinaryImageToMeshFilterType::Pointer convert = BinaryImageToMeshFilterType::New();
@@ -167,7 +166,7 @@ void clitk::Image2DicomRTStructFilter<PixelType>::Update()
     if (GetVerboseFlag()) {
       std::cout << "Mesh has " << meshes[i]->GetNumberOfLines() << " lines." << std::endl;
     }
-    
+
     /*
     // debug mesh write  FIXME
     vtkSmartPointer<vtkPolyDataWriter> wr = vtkSmartPointer<vtkPolyDataWriter>::New();
@@ -177,7 +176,7 @@ void clitk::Image2DicomRTStructFilter<PixelType>::Update()
     wr->Write();
     */
   }
-    
+
   // Copy previous contours
   for (unsigned int i = 0; i < numMasks-m; ++i) {
 #if VTK_MAJOR_VERSION <= 5
@@ -191,7 +190,7 @@ void clitk::Image2DicomRTStructFilter<PixelType>::Update()
     roiAlgorithms->InsertValue(i, theString);
     theString = reader->GetRTStructSetProperties()->GetStructureSetRTROIInterpretedType(i);
     roiTypes->InsertValue(i, theString);
-  }  
+  }
 
   // Add new ones
   for (unsigned int i = numMasks-m; i < numMasks; ++i) {
@@ -204,7 +203,7 @@ void clitk::Image2DicomRTStructFilter<PixelType>::Update()
     roiAlgorithms->InsertValue(i, "CLITK_CREATED");
     roiTypes->InsertValue(i, m_ROIType);
   }
-    
+
   /*
   //  Visu DEBUG
   vtkPolyDataMapper *cubeMapper = vtkPolyDataMapper::New();
index 83b9435854ef3f7bec71e764ccee787ba314e0f9..9193160096169859f7a670bd8363eb1e44813669 100644 (file)
@@ -66,7 +66,7 @@ itk::ImageIOBase::Pointer clitk::readImageHeader(const std::string & filename, b
   if (!reader) {
     if (exit_on_error) { //default behavior for tools who don't handle the problem
       clitkExceptionMacro("Error reading file " << filename << ", exiting immediately");
-    } else return NULL;
+    } else return (itk::ImageIOBase *) ITK_NULLPTR;
   }
   reader->SetFileName(filename);
   reader->ReadImageInformation();
index 9db15b0902513c78b26992e788f43ac4cae18381..c538ac0d196a3773508918f110b093785a8be7e8 100644 (file)
@@ -412,7 +412,7 @@ typename ImageType::Pointer clitk::ImageToImageGenericFilterBase::GetInput(unsig
       return typename ImageType::Pointer(const_cast<ImageType*>(vvImageToITK<ImageType>(m_InputVVImages[n]).GetPointer()));
     else {
       assert(false); //No input, this shouldn't happen
-      return typename ImageType::Pointer(NULL);
+      return typename ImageType::Pointer((ImageType*)ITK_NULLPTR);
     }
   }
 }
index ea476d7c4dffa1e10c6d607d4f9d3182ec1aa875..2e5ad4f2300234e8cf4b040044bae6c8b1b6a1c7 100644 (file)
@@ -202,10 +202,10 @@ static char *scan_header(const char *file, const char *name, int offset, int rem
 
 static int get_nki_compressed_size(FILE *f)
 {
-  NKI_MODE2            Header;
-  int                  iMode;
+  NKI_MODE2            Header;
+  int                  iMode;
 
-  fread((void *)&Header, sizeof(Header), 1 , f);
+  int tempReturn = fread((void *)&Header, sizeof(Header), 1 , f);
 
   iMode = Header.iMode;
 
@@ -593,7 +593,7 @@ int clitk::XdrImageIO::ReadImageInformationWithError()
   fstream = fopen(file, "rt");
   if (fstream == NULL) return ER_XDR_OPEN;
 
-  fgets(temp, 500, fstream);
+  char* tempReturn = fgets(temp, 500, fstream);
   fclose(fstream);
 
   if (memcmp(temp, "# AVS field file (produced by avs_nfwrite.c)", 44)==0) forcenoswap=1;
@@ -938,7 +938,7 @@ bool clitk::XdrImageIO::CanReadFile(const char* FileNameToRead)
 //        AVSerror("Couldn't open file " << FileNameToRead);
     return false;
 //    }
-  fgets(temp, 500, fstream);
+  char* tempReturn = fgets(temp, 500, fstream);
   fclose(fstream);
 
   if (memcmp(temp, "# AVS", 5)==0)
diff --git a/common/clitkXml2DicomRTStructFilter.h b/common/clitkXml2DicomRTStructFilter.h
new file mode 100644 (file)
index 0000000..3202716
--- /dev/null
@@ -0,0 +1,64 @@
+/*=========================================================================
+  Program:         vv http://www.creatis.insa-lyon.fr/rio/vv
+  Main authors :   XX XX XX
+
+  Authors belongs to:
+  - University of LYON           http://www.universite-lyon.fr/
+  - Léon Bérard cancer center    http://www.centreleonberard.fr
+  - CREATIS CNRS laboratory      http://www.creatis.insa-lyon.fr
+
+  This software is distributed WITHOUT ANY WARRANTY; without even
+  the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+  PURPOSE.  See the copyright notices for more information.
+
+  It is distributed under dual licence
+  - BSD       http://www.opensource.org/licenses/bsd-license.php
+  - CeCILL-B  http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.html
+
+  =========================================================================*/
+
+#ifndef CLITKXML2DICOMRTSTRUCTFILTER_H
+#define CLITKXML2DICOMRTSTRUCTFILTER_H
+
+// clitk
+#include "clitkDicomRT_ROI.h"
+#include "clitkImageCommon.h"
+#include "clitkFilterBase.h"
+#include "clitkDicomRT_StructureSet.h"
+
+namespace clitk {
+
+  //--------------------------------------------------------------------
+  template<class PixelType>
+  class Xml2DicomRTStructFilter: public clitk::FilterBase {
+
+  public:
+    Xml2DicomRTStructFilter();
+    ~Xml2DicomRTStructFilter();
+
+    typedef itk::Image<PixelType, 3> ImageType;
+    typedef typename ImageType::Pointer ImagePointer;
+    typedef typename clitk::DicomRT_StructureSet::Pointer DicomRTStructPointer;
+
+    itkSetMacro(InputFilename, std::string);
+    itkSetMacro(StructureSetFilename, std::string);
+    itkSetMacro(DicomFolder, std::string);
+    itkSetMacro(OutputFilename, std::string);
+
+    // Run filter
+    void Update();
+
+  protected:
+    std::string m_StructureSetFilename;
+    std::string m_DicomFolder;
+    std::string m_OutputFilename;
+    std::string m_InputFilename;
+  };
+  //--------------------------------------------------------------------
+
+} // end namespace clitk
+
+#include "clitkXml2DicomRTStructFilter.txx"
+
+#endif // CLITKXML2DICOMRTSTRUCTFILTER_H
+
diff --git a/common/clitkXml2DicomRTStructFilter.txx b/common/clitkXml2DicomRTStructFilter.txx
new file mode 100644 (file)
index 0000000..9c106ec
--- /dev/null
@@ -0,0 +1,254 @@
+/*=========================================================================
+  Program:         vv http://www.creatis.insa-lyon.fr/rio/vv
+  Main authors :   XX XX XX
+
+  Authors belongs to:
+  - University of LYON           http://www.universite-lyon.fr/
+  - Léon Bérard cancer center    http://www.centreleonberard.fr
+  - CREATIS CNRS laboratory      http://www.creatis.insa-lyon.fr
+
+  This software is distributed WITHOUT ANY WARRANTY; without even
+  the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+  PURPOSE.  See the copyright notices for more information.
+
+  It is distributed under dual licence
+  - BSD       http://www.opensource.org/licenses/bsd-license.php
+  - CeCILL-B  http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.html
+
+  =========================================================================*/
+
+// clitk
+#include "clitkXml2DicomRTStructFilter.h"
+
+// xml parser
+#include "../utilities/pugixml/pugixml.hpp"
+
+// vtk
+#include <vtkSmartPointer.h>
+#include <vtkMetaImageWriter.h>
+#include <vtkImageData.h>
+#include <vtkPolygon.h>
+#include <vtkAppendPolyData.h>
+
+// FIXME to remove
+#include "vtkPolyDataMapper.h"
+#include "vtkPolyDataMapper2D.h"
+#include "vtkRenderWindowInteractor.h"
+#include "vtkPolyDataReader.h"
+#include "vtkRenderWindow.h"
+#include "vtkRenderer.h"
+#include "vtkCamera.h"
+#include "vtkProperty.h"
+#include "vtkProperty2D.h"
+#include <vtksys/SystemTools.hxx>
+
+// gdcm
+#include <vtkRTStructSetProperties.h>
+#include <vtkGDCMPolyDataReader.h>
+#include <vtkGDCMPolyDataWriter.h>
+
+//--------------------------------------------------------------------
+template<class PixelType>
+clitk::Xml2DicomRTStructFilter<PixelType>::Xml2DicomRTStructFilter()
+{
+  m_StructureSetFilename = "";
+  m_DicomFolder = "";
+  m_OutputFilename = "default-output.dcm";
+}
+//--------------------------------------------------------------------
+
+
+//--------------------------------------------------------------------
+template<class PixelType>
+clitk::Xml2DicomRTStructFilter<PixelType>::~Xml2DicomRTStructFilter()
+{
+}
+//--------------------------------------------------------------------
+
+
+//--------------------------------------------------------------------
+template<class PixelType>
+void clitk::Xml2DicomRTStructFilter<PixelType>::Update()
+{
+  // Check this is a RT-Struct
+  gdcm::Reader gdcm_reader;
+  gdcm_reader.SetFileName(m_StructureSetFilename.c_str());
+  if (!gdcm_reader.Read()) {
+    clitkExceptionMacro("Error could not open the file '" << m_StructureSetFilename << std::endl);
+  }
+  gdcm::MediaStorage ms;
+  ms.SetFromFile(gdcm_reader.GetFile());
+  if (ms != gdcm::MediaStorage::RTStructureSetStorage) {
+    clitkExceptionMacro("File '" << m_StructureSetFilename << "' is not a DICOM-RT-Struct file." << std::endl);
+  }
+
+  // Read rt struct
+  vtkSmartPointer<vtkGDCMPolyDataReader> reader = vtkGDCMPolyDataReader::New();
+  reader->SetFileName(m_StructureSetFilename.c_str());
+  reader->Update();
+
+  // Get properties
+  vtkRTStructSetProperties * p = reader->GetRTStructSetProperties();
+  if (GetVerboseFlag()) {
+    std::cout << "Number of structures in the dicom-rt-struct : "
+              << p->GetNumberOfStructureSetROIs() << std::endl;
+  }
+
+  std::map<std::string, vtkSmartPointer<vtkAppendPolyData> > mapName2Data;
+
+  pugi::xml_document doc;
+  pugi::xml_parse_result result = doc.load_file(m_InputFilename.c_str());
+  if (!result) {
+    std::cout << "ERROR: no result" << std::endl;
+    return;
+  }
+
+  //Take the main dict and look for point_mm in each struct in each slice
+  pugi::xml_node mainDict = doc.child("plist").child("dict").child("array");
+  for (pugi::xml_node_iterator mainDictIt = mainDict.begin(); mainDictIt != mainDict.end(); ++mainDictIt) { //Look for all slice (one slice per dict)
+    if (!std::strcmp(mainDictIt->name(), "dict")) {
+      for (pugi::xml_node_iterator sliceIt = mainDictIt->child("array").begin(); sliceIt != mainDictIt->child("array").end(); ++sliceIt) { //Look for all struct in the current slice (one struct per dict)
+        if (!std::strcmp(sliceIt->name(), "dict")) {
+          pugi::xml_node_iterator structureIt = sliceIt->begin();
+          while (structureIt != sliceIt->end() && std::abs(std::strcmp(structureIt->child_value(), "Name"))) //Look for the name for the current struct
+            ++structureIt;
+          if (structureIt != sliceIt->end()) { //take the following node to have the values
+            ++structureIt;
+            std::string name(structureIt->child_value());
+            while (structureIt != sliceIt->end() && std::abs(std::strcmp(structureIt->child_value(), "Point_mm"))) //Look for the Point_mm for the current struct
+              ++structureIt;
+            if (structureIt != sliceIt->end()) { //take the following node to have the values
+              ++structureIt;
+              // Insert all points for 1 struct into vtkPoints
+              vtkSmartPointer<vtkPoints> points = vtkSmartPointer<vtkPoints>::New();
+              for (pugi::xml_node_iterator pointIt = structureIt->begin(); pointIt != structureIt->end(); ++pointIt) { //Look for all points in mm in the current struct (one point per string)
+                if (!std::strcmp(pointIt->name(), "string")) {
+                  // Split the string to save the 3 values (remove the useless char) as double
+                  char* copy = (char*)pointIt->child_value();
+                  std::vector<char*> v;
+                  char* chars_array = strtok(copy, ", ");
+                  while(chars_array) {
+                      v.push_back(chars_array);
+                      chars_array = strtok(NULL, ", ");
+                  }
+                  v[0] = v[0] + 1;
+                  v[2][strlen(v[2])-1] = '\0';
+                  double x, y, z;
+                  x = std::atof(v[0]);
+                  y = std::atof(v[1]);
+                  z = std::atof(v[2]);
+                  points->InsertNextPoint(x, y, z);
+                }
+              }
+
+              //Create the polygon attached to the points
+              vtkSmartPointer<vtkPolygon> polygon = vtkSmartPointer<vtkPolygon>::New();
+              polygon->GetPointIds()->SetNumberOfIds(points->GetNumberOfPoints()); //make a quad
+              for (int pointIdx=0; pointIdx<points->GetNumberOfPoints(); ++pointIdx)
+                polygon->GetPointIds()->SetId(pointIdx, pointIdx);
+              // Add the polygon to a list of polygons
+              vtkSmartPointer<vtkCellArray> polygons = vtkSmartPointer<vtkCellArray>::New();
+              polygons->InsertNextCell(polygon);
+              // Create a PolyData
+              vtkSmartPointer<vtkPolyData> polygonPolyData = vtkSmartPointer<vtkPolyData>::New();
+              polygonPolyData->SetPoints(points);
+              polygonPolyData->SetPolys(polygons);
+
+              //Append the polyData into the map at the correct stuct name entry
+              std::map<std::string, vtkSmartPointer<vtkAppendPolyData> >::const_iterator it = mapName2Data.find(name);
+              if (it == mapName2Data.end())
+                mapName2Data[name] = vtkSmartPointer<vtkAppendPolyData>::New();
+#if VTK_MAJOR_VERSION <= 5
+              mapName2Data[name]->AddInput(polygonPolyData);
+#else
+              mapName2Data[name]->AddInputData(polygonPolyData);
+#endif
+            }
+          }
+        }
+      }
+    }
+  }
+
+  for (std::map<std::string, vtkSmartPointer<vtkAppendPolyData> >::iterator it = mapName2Data.begin(); it != mapName2Data.end(); ++it)
+    it->second->Update();
+
+  // number of contours
+  int numMasks = mapName2Data.size();
+
+  // Init writer
+  vtkGDCMPolyDataWriter * writer = vtkGDCMPolyDataWriter::New();
+  writer->SetNumberOfInputPorts(numMasks);
+  writer->SetFileName(m_OutputFilename.c_str());
+  writer->SetMedicalImageProperties(reader->GetMedicalImageProperties());
+
+  // List of already present rois
+  vtkStringArray* roiNames = vtkStringArray::New();
+  vtkStringArray* roiAlgorithms = vtkStringArray::New();
+  vtkStringArray* roiTypes = vtkStringArray::New();
+  roiNames->SetNumberOfValues(numMasks);
+  roiAlgorithms->SetNumberOfValues(numMasks);
+  roiTypes->SetNumberOfValues(numMasks);
+
+  // Add new structures
+  std::map<std::string, vtkSmartPointer<vtkAppendPolyData> >::iterator it = mapName2Data.begin();
+  for (unsigned int i = 0; i < numMasks; ++i) {
+#if VTK_MAJOR_VERSION <= 5
+    writer->SetInput(i, it->second->GetOutput());
+#else
+    writer->SetInputData(i, it->second->GetOutput());
+#endif
+    roiNames->InsertValue(i, it->first);
+    roiAlgorithms->InsertValue(i, "CLITK_CREATED");
+    roiTypes->InsertValue(i, "coucou"); //Roi type instead of coucou
+    ++it;
+  }
+
+  /*
+  //  Visu DEBUG
+  vtkPolyDataMapper *cubeMapper = vtkPolyDataMapper::New();
+  cubeMapper->SetInputData( mapName2Data[0]->GetOutput() );
+  cubeMapper->SetScalarRange(0,7);
+  vtkActor *cubeActor = vtkActor::New();
+  cubeActor->SetMapper(cubeMapper);
+  vtkProperty * property = cubeActor->GetProperty();
+  property->SetRepresentationToWireframe();
+
+  vtkRenderer *renderer = vtkRenderer::New();
+  vtkRenderWindow *renWin = vtkRenderWindow::New();
+  renWin->AddRenderer(renderer);
+
+  vtkRenderWindowInteractor *iren = vtkRenderWindowInteractor::New();
+  iren->SetRenderWindow(renWin);
+
+  renderer->AddActor(cubeActor);
+  renderer->ResetCamera();
+  renderer->SetBackground(1,1,1);
+
+  renWin->SetSize(300,300);
+
+  renWin->Render();
+  iren->Start();
+  */
+  // End visu
+
+  // Write (need to read dicom for slice id)
+  vtkRTStructSetProperties* theProperties = vtkRTStructSetProperties::New();
+  writer->SetRTStructSetProperties(theProperties);
+  if (GetVerboseFlag()) {
+    std::cout << "Looking for dicom info, study instance "
+              << p->GetStudyInstanceUID() << std::endl;
+  }
+  writer->InitializeRTStructSet(m_DicomFolder,
+                                reader->GetRTStructSetProperties()->GetStructureSetLabel(),
+                                reader->GetRTStructSetProperties()->GetStructureSetName(),
+                                roiNames, roiAlgorithms, roiTypes);
+  writer->Write();
+  reader->Delete();
+  roiNames->Delete();
+  roiTypes->Delete();
+  roiAlgorithms->Delete();
+  writer->Delete();
+}
+//--------------------------------------------------------------------
+
index c7e13cc60b12fe9b98193efb232c8b1792802371..f75d3212267441b0ee75e2a0ffe221d00ad9066a 100644 (file)
@@ -261,6 +261,47 @@ const std::vector< vtkSmartPointer<vtkTransform> >& vvImage::GetTransform()
 //--------------------------------------------------------------------
 
 
+//--------------------------------------------------------------------
+void vvImage::InitializeTransform()
+{
+  for (int i = 0; i < mTransform.size(); i++)
+    mTransform[i]->Identity();
+}
+//--------------------------------------------------------------------
+
+
+//--------------------------------------------------------------------
+std::vector< std::vector<double> > vvImage::GetDirection()
+{
+  int dim = this->GetNumberOfDimensions();
+  vtkSmartPointer<vtkTransform> transform = vtkSmartPointer<vtkTransform>::New();
+  transform->Identity();
+  for (int i = 0; i < mTransform.size(); i++)
+    transform->Concatenate(this->mTransform[i]);
+
+  vtkSmartPointer<vtkMatrix4x4> matrix = transform->GetMatrix();
+  matrix->Invert();
+  std::vector<std::vector<double> > direction0;
+  for (int i = 0; i < dim; i++) {
+    if (i != 3) {
+      std::vector<double> direction1;
+      for (int j = 0; j < dim; j++) {
+        if (j != 3)
+#if VTK_MAJOR_VERSION <= 6
+          direction1.push_back((*matrix)[i][j]);
+#else
+          direction1.push_back((*matrix).GetElement(i,j));
+#endif
+      }
+      direction0.push_back(direction1);
+    }
+  }
+  return direction0;
+}
+//--------------------------------------------------------------------
+
+
+
 //--------------------------------------------------------------------
 bool vvImage::HaveSameSizeAndSpacingThan(vvImage * other)
 {
index d18ee9865303e217099ba4c40b5247c437ed66e7..cbb0284ce0e925f706032825c7e8888da7744987 100644 (file)
@@ -60,6 +60,7 @@ public :
   std::vector<double> GetSpacing();
   std::vector<double> GetOrigin() const;
   std::vector<int> GetSize();
+  std::vector< std::vector<double> > GetDirection();
   std::string GetScalarTypeAsITKString();
   int GetNumberOfScalarComponents();
   int GetScalarSize();
@@ -67,6 +68,7 @@ public :
   bool IsScalarTypeInteger();
   bool IsScalarTypeInteger(int t);
   const std::vector< vtkSmartPointer<vtkTransform> >& GetTransform();
+  void InitializeTransform();
   void SetTimeSpacing(double s) { mTimeSpacing = s; }
   void SetTimeOrigin(double o) { mTimeOrigin = o; }
   bool HaveSameSizeAndSpacingThan(vvImage * other);
index 5e7d90364dc6ee28e067cfd18c7aa0a8169a07a4..a2fc4e964ab8edeeef1e57cb0f6a4b742c371d44 100644 (file)
@@ -33,6 +33,7 @@ vvImageReader::vvImageReader()
   mLastError = "";
   mType = UNDEFINEDIMAGETYPE;
   mSlice = 0;
+  mPatientCoordinateSystem = false;
 }
 //------------------------------------------------------------------------------
 
@@ -50,6 +51,14 @@ void vvImageReader::Update()
 //------------------------------------------------------------------------------
 
 
+//------------------------------------------------------------------------------
+void vvImageReader::SetPatientCoordinateSystem(bool patientCoordinateSystem)
+{
+  mPatientCoordinateSystem = patientCoordinateSystem;
+}
+//------------------------------------------------------------------------------
+
+
 //------------------------------------------------------------------------------
 void vvImageReader::Update(LoadedImageType type)
 {
index 4dc3f409d3d193520439e534e90e17309fdd8d98..6f3dbc3d0f3e4573f3fe6886f862c4e278dcf78a 100644 (file)
@@ -59,6 +59,7 @@ public:
   }
 
   void SetSlice(unsigned int i) { mSlice = i; }
+  void SetPatientCoordinateSystem(bool patientCoordinateSystem);
 
   //====================================================================
   // Main function
@@ -91,6 +92,7 @@ protected:
   void UpdateWithDimAndInputVectorPixelType();
   ///Input dimension and pixel type
   int mDim;
+  bool mPatientCoordinateSystem;
   std::string mInputPixelType;
 
   //====================================================================
index a9f17ab6afe7c8d08aec0ffa2ba2613885304983..2eba11c81bc628f107d7f738c0a6d750fba7ef8f 100644 (file)
@@ -257,6 +257,9 @@ void vvImageReader::UpdateWithDimAndInputPixelType()
       }
     }
   }
+
+  if (mType == DICOM && !mPatientCoordinateSystem)
+    mImage->InitializeTransform();
 }
 //----------------------------------------------------------------------------
 
diff --git a/docWiki.sh b/docWiki.sh
new file mode 100755 (executable)
index 0000000..e766e99
--- /dev/null
@@ -0,0 +1,111 @@
+#Write all help of syd tools in a file sydTool.md
+#Execute it in src folder but the tools have to be into ../bin/bin (or change it)
+#Results are in src folder
+
+#!/bin/bash
+set -ev
+currentFolder=$PWD
+#Copy all help into a temporary file
+tempFile=tempDoc.txt
+cd ./vv_bin/bin
+clitkTools=`ls clitk*`
+echo "This page contains additional information on how to use clitk tools:" > $tempFile
+echo "<<TableOfContents()>>" >> $tempFile
+echo "" >> $tempFile
+for tool in $clitkTools
+do
+  echo "==== " $tool " ====" >> $tempFile
+  $tool -h >> $tempFile
+  echo "" >> $tempFile
+  echo "" >> $tempFile
+  echo "" >> $tempFile
+  echo "" >> $tempFile
+done
+
+#Write the .md file
+docFile="$currentFolder/clitkTool.txt"
+rm $docFile
+helpLine=false  #Boolean to know if we are reading the command lines starting with '-h, --help' and finishing with '##' or just the help
+echoLine=""
+writeLine=false;
+while IFS='' read -r line || [[ -n "$line" ]]; do #read all lines
+    copyLine=$line;
+    line=`echo "$line" | sed -e 's/^[ \t]*//'`; # prevent whitespace at the begining of the line
+    if [[ $line == *"-h, --help"* ]]; then
+      helpLine=true;
+    fi
+    if [[ $line == *"===="* ]]; then
+      if $writeLine; then
+        echo "$echoLine||" >> $docFile;
+      fi
+      echoLine="";
+      echo "" >> $docFile;
+      echo "" >> $docFile;
+      echo "" >> $tempFile
+      echo "" >> $tempFile
+      writeLine=false;
+      helpLine=false;
+    fi
+
+    if $helpLine; then
+      if [[ $line == "" ]]; then
+        continue;
+      fi
+      #Create the string according to the different case of ggo
+      #Start with -h
+      #Start with --help
+      #Start with comment
+      #Start with title
+      if [[ $line == "-"[a-zA-Z]* ]]; then
+        if $writeLine; then
+          echoLine="$echoLine||";
+          echo "$echoLine" >> $docFile;
+        fi
+        writeLine=true;
+        echoLine="||";
+        tempString=`echo "$line" | cut -c1-2`; # eg take the -h
+        echoLine="$echoLine$tempString";
+        line=`echo "$line" | cut -d' ' -f2-`; # remove -h, (first word)
+        line=`echo "$line" | sed -e 's/^[ \t]*//'`; # prevent whitespace at the begining of the line
+        echoLine="$echoLine||";
+        tempString=`echo "$line" | cut -f 1 -d " "`; # eg take the --help (first word)
+        echoLine="$echoLine$tempString";
+        line=`echo "$line" | cut -d' ' -f2-`; # remove --help (first word)
+        line=`echo "$line" | sed -e 's/^[ \t]*//'`; # prevent whitespace at the begining of the line
+        echoLine="$echoLine||$line";
+      elif [[ $line == "--"* ]]; then
+        if $writeLine; then
+          echoLine="$echoLine||";
+          echo "$echoLine" >> $docFile;
+        fi
+        writeLine=true;
+        echoLine="|| ||";
+        tempString=`echo "$line" | cut -f 1 -d " "`; # eg take the --help (first word)
+        echoLine="$echoLine$tempString";
+        line=`echo "$line" | cut -d' ' -f2-`; # remove --help (first word)
+        line=`echo "$line" | sed -e 's/^[ \t]*//'`; # prevent whitespace at the begining of the line
+        echoLine="$echoLine||$line";
+      elif [[ $copyLine == " "* ]]; then
+        echoLine="$echoLine$line";
+      else
+        if $writeLine; then
+          echoLine="$echoLine||";
+          echo "$echoLine" >> $docFile;
+        fi
+        echoLine="||||||<style=\"&quot; &amp; quot;text-align:center&amp; quot; &quot;\">$line||";
+        echo "$echoLine" >> $docFile;
+        echoLine="";
+        writeLine=false;
+      fi
+    else
+      if [[ `echo "$line" | cut -c1-1` = "-" ]]; then
+        echo "  " >> $docFile;
+        echo "\\$line" >> $docFile;
+      else
+        echo "$line" >> $docFile;
+      fi
+    fi
+done < "$tempFile"
+echo "$echoLine||" >> $docFile
+rm $tempFile
+cd $currentFolder
index 1c7d3c003ca417a670be4f5703017aa48845f979..4ad12a4da9cad661689599df63e966ffd7f7ddb1 100644 (file)
@@ -43,7 +43,7 @@ if(CLITK_BUILD_REGISTRATION)
     target_link_libraries(clitkConvertBSplineDeformableTransformToVF clitkCommon)
     set(REGISTRATION_INSTALL ${REGISTRATION_INSTALL} clitkConvertBSplineDeformableTransformToVF)
 
-    install(TARGETS ${REGISTRATION_INSTALL} DESTINATION bin PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_EXECUTE WORLD_EXECUTE)
+    install(TARGETS ${REGISTRATION_INSTALL} DESTINATION bin PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE)
 
 endif(CLITK_BUILD_REGISTRATION)
 
index f369e0506e838492f560f5f8e525771bf8cd377c..17ebca95a720d5ec84b78286672fbdae2340dd05 100644 (file)
@@ -321,7 +321,7 @@ void AffineRegistrationGenericFilter::UpdateWithInputImageType()
   // If given, we connect a mask to reference or target
   //============================================================================
   typedef itk::ImageMaskSpatialObject<  InputImageType::ImageDimension >   MaskType;
-  typename MaskType::Pointer  fixedMask=NULL;
+  typename MaskType::Pointer  fixedMask=ITK_NULLPTR;
   if (m_ArgsInfo.referenceMask_given) {
     fixedMask= MaskType::New();
     typedef itk::Image< unsigned char, InputImageType::ImageDimension >   ImageMaskType;
@@ -342,7 +342,7 @@ void AffineRegistrationGenericFilter::UpdateWithInputImageType()
   }
 
   typedef itk::ImageMaskSpatialObject<  InputImageType::ImageDimension >   MaskType;
-  typename MaskType::Pointer  movingMask=NULL;
+  typename MaskType::Pointer  movingMask=ITK_NULLPTR;
   if (m_ArgsInfo.targetMask_given) {
     movingMask= MaskType::New();
     typedef itk::Image< unsigned char, InputImageType::ImageDimension >   ImageMaskType;
index 9f7b84f456f36496f1be156400a0c43441dd2983..35ac6eb9e58b52d5baa09d255108657b83beaf8b 100644 (file)
@@ -30,14 +30,11 @@ It is distributed under dual licence
 #include "clitkBLUTDIRGenericFilter.h"
 #include "clitkBLUTDIRCommandIterationUpdateDVF.h"
 #include "itkCenteredTransformInitializer.h"
-#if ITK_VERSION_MAJOR >= 4
-#  if ITK_VERSION_MINOR < 6
-#    include "itkTransformToDisplacementFieldSource.h"
-#  else
-#    include "itkTransformToDisplacementFieldFilter.h"
-#  endif
+#include "itkLabelStatisticsImageFilter.h"
+#if (ITK_VERSION_MAJOR == 4) && (ITK_VERSION_MINOR < 6)
+# include "itkTransformToDisplacementFieldSource.h"
 #else
-#  include "itkTransformToDeformationFieldSource.h"
+# include "itkTransformToDisplacementFieldFilter.h"
 #endif
 
 namespace clitk
@@ -249,7 +246,7 @@ namespace clitk
           m_CommandIterationUpdate->SetOptimizer(m_GenericOptimizer);
 
           // Set the previous transform parameters to the registration
-          // if(m_Initializer->m_Parameters!=NULL )delete m_Initializer->m_Parameters;
+          // if(m_Initializer->m_Parameters!=ITK_NULLPTR )delete m_Initializer->m_Parameters;
           m_Initializer->SetInitialParameters(currentCoefficientImages, *newParameters);
           registration->SetInitialTransformParametersOfNextLevel(*newParameters);
         }
@@ -341,8 +338,8 @@ namespace clitk
       //============================================================================
       typedef itk::ImageMaskSpatialObject< InputImageType::ImageDimension >   MaskType;
       typedef itk::Image< unsigned char, InputImageType::ImageDimension >   ImageLabelType;
-      typename MaskType::Pointer        fixedMask = NULL;
-      typename ImageLabelType::Pointer  labels = NULL;
+      typename MaskType::Pointer        fixedMask = ITK_NULLPTR;
+      typename ImageLabelType::Pointer  labels = ITK_NULLPTR;
       if (m_ArgsInfo.referenceMask_given)
       {
         fixedMask = MaskType::New();
@@ -377,11 +374,12 @@ namespace clitk
         fixedMask->SetImage(labels);
 
         // Find the bounding box of the "inside" label
-        typedef itk::LabelGeometryImageFilter<ImageLabelType> GeometryImageFilterType;
-        typename GeometryImageFilterType::Pointer geometryImageFilter=GeometryImageFilterType::New();
-        geometryImageFilter->SetInput(labels);
-        geometryImageFilter->Update();
-        typename GeometryImageFilterType::BoundingBoxType boundingBox = geometryImageFilter->GetBoundingBox(1);
+        typedef itk::LabelStatisticsImageFilter<ImageLabelType, ImageLabelType> StatisticsImageFilterType;
+        typename StatisticsImageFilterType::Pointer statisticsImageFilter=StatisticsImageFilterType::New();
+        statisticsImageFilter->SetInput(labels);
+        statisticsImageFilter->SetLabelInput(labels);
+        statisticsImageFilter->Update();
+        typename StatisticsImageFilterType::BoundingBoxType boundingBox = statisticsImageFilter->GetBoundingBox(1);
 
         // Limit the transform region to the mask
         for (unsigned int i=0; i<InputImageType::ImageDimension; i++)
@@ -415,7 +413,7 @@ namespace clitk
       }
 
       typedef itk::ImageMaskSpatialObject< InputImageType::ImageDimension >   MaskType;
-      typename MaskType::Pointer  movingMask=NULL;
+      typename MaskType::Pointer  movingMask=ITK_NULLPTR;
       if (m_ArgsInfo.targetMask_given)
       {
         movingMask= MaskType::New();
@@ -488,7 +486,7 @@ namespace clitk
       // Rigid or Affine Transform
       //=======================================================
       typedef itk::AffineTransform <double,3> RigidTransformType;
-      RigidTransformType::Pointer rigidTransform=NULL;
+      RigidTransformType::Pointer rigidTransform=ITK_NULLPTR;
       if (m_ArgsInfo.initMatrix_given)
       {
         if(m_Verbose) std::cout<<"Reading the prior transform matrix "<< m_ArgsInfo.initMatrix_arg<<"..."<<std::endl;
@@ -625,7 +623,7 @@ namespace clitk
         sTransform->SetGridRegion( transform->GetTransforms()[0]->GetGridRegion() );
         sTransform->SetParameters( transform->GetTransforms()[0]->GetParameters() );
         regTransform = sTransform;
-        transform = NULL; // free memory
+        transform = ITK_NULLPTR; // free memory
       }
 
       //=======================================================
@@ -789,19 +787,17 @@ namespace clitk
       //=======================================================
       typedef itk::Vector< float, SpaceDimension >  DisplacementType;
       typedef itk::Image< DisplacementType, InputImageType::ImageDimension >  DisplacementFieldType;
-#if ITK_VERSION_MAJOR >= 4
-#  if ITK_VERSION_MINOR < 6
+#if (ITK_VERSION_MAJOR == 4) && (ITK_VERSION_MINOR < 6)
       typedef itk::TransformToDisplacementFieldSource<DisplacementFieldType, double> ConvertorType;
-#  else
+#else
       typedef itk::TransformToDisplacementFieldFilter<DisplacementFieldType, double> ConvertorType;
-#  endif
 #endif
       typename ConvertorType::Pointer filter= ConvertorType::New();
       filter->SetNumberOfThreads(1);
       if(m_ArgsInfo.itkbspline_flag)
-        sTransform->SetBulkTransform(NULL);
+        sTransform->SetBulkTransform(ITK_NULLPTR);
       else
-        transform->SetBulkTransform(NULL);
+        transform->SetBulkTransform(ITK_NULLPTR);
       filter->SetTransform(regTransform);
 #if ITK_VERSION_MAJOR > 4 || (ITK_VERSION_MAJOR == 4 && ITK_VERSION_MINOR >= 6)
       filter->SetReferenceImage(fixedImage);
index 18e1600ee9f12827730873a177325ebb3d00a338..419af6a6f8e43319cf142a8fba746f718d956e4d 100644 (file)
@@ -57,7 +57,6 @@
 #include "itkLightObject.h"
 #include "itkImageToImageMetric.h"
 #include "itkInterpolateImageFunction.h"
-#include "itkLabelGeometryImageFilter.h"
 #include "itkImageDuplicator.h"
 #include "itkExtractImageFilter.h"
 
index dc85c7a445db478bf15294d68870d80b9d26decf..7ae33eb084baf8d057309d7f38e46ffe064c2f85 100644 (file)
@@ -7,12 +7,10 @@
 #include "clitkImageCommon.h"
 #include "clitkBSplineDeformableTransform.h"
 #include "itkBSplineDeformableTransform.h"
-#if ITK_VERSION_MAJOR >= 4
-#  if ITK_VERSION_MINOR < 6
-#    include "itkTransformToDisplacementFieldSource.h"
-#  else
-#    include "itkTransformToDisplacementFieldFilter.h"
-#  endif
+#if (ITK_VERSION_MAJOR == 4) && (ITK_VERSION_MINOR < 6)
+#  include "itkTransformToDisplacementFieldSource.h"
+#else
+#  include "itkTransformToDisplacementFieldFilter.h"
 #endif
 
 namespace clitk 
@@ -51,12 +49,10 @@ namespace clitk
 
     typedef itk::Transform< double, TDVFType::ImageDimension, TDVFType::ImageDimension> GenericTransformType;
     
-#if ITK_VERSION_MAJOR >= 4
-#  if ITK_VERSION_MINOR < 6
-    typedef itk::TransformToDisplacementFieldSource<OutputImageType, double> ConvertorType;
-#  else
-    typedef itk::TransformToDisplacementFieldFilter<OutputImageType, double> ConvertorType;
-#  endif
+#if (ITK_VERSION_MAJOR == 4) && (ITK_VERSION_MINOR < 6)
+  typedef itk::TransformToDisplacementFieldSource<OutputImageType, double> ConvertorType;
+#else
+  typedef itk::TransformToDisplacementFieldFilter<OutputImageType, double> ConvertorType;
 #endif
 
     itkNewMacro(Self);
index 9bda80f8ec0aa03d38b9031875955bc6fc5b584d..da20e6b9879e310a37a4072467de76a77d551e78 100644 (file)
@@ -107,7 +107,7 @@ namespace clitk
 
       // Mask
       typedef itk::ImageMaskSpatialObject<BLUTCoefficientImageType::ImageDimension >   MaskType;
-      typename MaskType::Pointer  spatialObjectMask=NULL;
+      typename MaskType::Pointer  spatialObjectMask=ITK_NULLPTR;
       if (!m_MaskFileName.empty())
       {
           typedef itk::Image< unsigned char, BLUTCoefficientImageType::ImageDimension >   ImageMaskType;
index 852c19f858b833fef34d584642e6b14ff52e50ae..d4a19c26d5f15a733e4838b3b6cfa1f110e78d72 100644 (file)
@@ -31,13 +31,13 @@ namespace clitk
 template <class args_info_type, class FixedImageType, class MovingImageType>
 GenericMetric<args_info_type, FixedImageType, MovingImageType>::GenericMetric()
 {
-  m_Metric=NULL;
+  m_Metric=ITK_NULLPTR;
   m_Maximize=false;
   m_Verbose=false;
   m_FixedImageRegionGiven=false;
   m_FixedImageSamplesIntensityThreshold=0;
   m_UseFixedImageSamplesIntensityThreshold=false;
-  m_FixedImageMask=NULL;
+  m_FixedImageMask=ITK_NULLPTR;
 }
 
 
@@ -255,7 +255,7 @@ GenericMetric<args_info_type,FixedImageType, MovingImageType>::GetMetricPointer(
 
 
   typedef itk::ImageMaskSpatialObject<itkGetStaticConstMacro(FixedImageDimension)> ImageMaskSpatialObjectType;
-  typename ImageMaskSpatialObjectType::ConstPointer mask = NULL;
+  typename ImageMaskSpatialObjectType::ConstPointer mask = ITK_NULLPTR;
   if (m_FixedImageMask.IsNotNull())
     mask = dynamic_cast<const ImageMaskSpatialObjectType*>(m_FixedImageMask.GetPointer());
 
index 9aa54895d3fb409b4144843b4e607e56c8ef2298..4f8fa7ceac08e6795503f0027c6b7581f2b46de9 100644 (file)
 
 //itk include
 #include "itkLightObject.h"
-#if ITK_VERSION_MAJOR >= 4
-#  if ITK_VERSION_MINOR < 6
-#    include "itkTransformToDisplacementFieldSource.h"
-#  else
-#    include "itkTransformToDisplacementFieldFilter.h"
-#  endif
+#if (ITK_VERSION_MAJOR == 4) && (ITK_VERSION_MINOR < 6)
+# include "itkTransformToDisplacementFieldSource.h"
+#else
+# include "itkTransformToDisplacementFieldFilter.h"
 #endif
 #include "itkAffineTransform.h"
 
index 8a36a882b91017a7c4d1e69cc1f34cd73cdcedd7..8bc8877cf9757239091cf126382cdfcc2a99397a 100644 (file)
@@ -78,12 +78,10 @@ namespace clitk
     typedef itk::Image<Displacement, Dimension> OutputImageType;
     
     // Filter
-#if ITK_VERSION_MAJOR >= 4
-#  if ITK_VERSION_MINOR < 6
+#if (ITK_VERSION_MAJOR == 4) && (ITK_VERSION_MINOR < 6)
     typedef itk::TransformToDisplacementFieldSource<OutputImageType, double> ConvertorType;
-#  else
+#else
     typedef itk::TransformToDisplacementFieldFilter<OutputImageType, double> ConvertorType;
-#  endif
 #endif
 
     typename   ConvertorType::Pointer filter= ConvertorType::New();
index 43fd978b92e8e39bcc8020a30ef33ded70c86855..c8312e6e173085225a1a53181e01897c83123a6c 100644 (file)
@@ -43,7 +43,7 @@ MultiResolutionPDEDeformableRegistration<TFixedImage,TMovingImage,TDeformationFi
   m_MovingImagePyramid  = MovingImagePyramidType::New();
   m_FixedImagePyramid     = FixedImagePyramidType::New();
   m_FieldExpander     = FieldExpanderType::New();
-  m_InitialDeformationField = NULL;
+  m_InitialDeformationField = ITK_NULLPTR;
 
   m_NumberOfLevels = 3;
   m_NumberOfIterations.resize( m_NumberOfLevels );
@@ -219,7 +219,7 @@ void
 MultiResolutionPDEDeformableRegistration<TFixedImage,TMovingImage,TDeformationField,TRealType>
 ::GenerateData()
 {
-  // Check for NULL images and pointers
+  // Check for ITK_NULLPTR images and pointers
   MovingImageConstPointer movingImage = this->GetMovingImage();
   FixedImageConstPointer  fixedImage = this->GetFixedImage();
 
@@ -266,7 +266,7 @@ MultiResolutionPDEDeformableRegistration<TFixedImage,TMovingImage,TDeformationFi
   unsigned int fixedLevel = vnl_math_min( (int) m_CurrentLevel, 
                                          (int) m_FixedImagePyramid->GetNumberOfLevels() );
 
-  DeformationFieldPointer tempField = NULL;
+  DeformationFieldPointer tempField = ITK_NULLPTR;
 
   DeformationFieldPointer inputPtr =
     const_cast< DeformationFieldType * >( this->GetInput(0) );
@@ -323,7 +323,7 @@ MultiResolutionPDEDeformableRegistration<TFixedImage,TMovingImage,TDeformationFi
     m_FieldExpander->SetOutputDirection( fi->GetDirection());
 
     m_FieldExpander->UpdateLargestPossibleRegion();
-    m_FieldExpander->SetInput( NULL );
+    m_FieldExpander->SetInput( ITK_NULLPTR );
     tempField = m_FieldExpander->GetOutput();
     tempField->DisconnectPipeline();
     }
@@ -335,7 +335,7 @@ MultiResolutionPDEDeformableRegistration<TFixedImage,TMovingImage,TDeformationFi
       
       if( tempField.IsNull() )
        {
-         m_RegistrationFilter->SetInitialDisplacementField( NULL );
+         m_RegistrationFilter->SetInitialDisplacementField( ITK_NULLPTR );
        }
       else
        {
@@ -353,7 +353,7 @@ MultiResolutionPDEDeformableRegistration<TFixedImage,TMovingImage,TDeformationFi
       m_FieldExpander->SetOutputSpacing( fi->GetSpacing());
 
       m_FieldExpander->UpdateLargestPossibleRegion();
-      m_FieldExpander->SetInput( NULL );
+      m_FieldExpander->SetInput( ITK_NULLPTR );
       tempField = m_FieldExpander->GetOutput();
       tempField->DisconnectPipeline();
 
@@ -430,9 +430,9 @@ MultiResolutionPDEDeformableRegistration<TFixedImage,TMovingImage,TDeformationFi
       }
 
     // Release memory
-    m_FieldExpander->SetInput( NULL );
+    m_FieldExpander->SetInput( ITK_NULLPTR );
     m_FieldExpander->GetOutput()->ReleaseData();
-    m_RegistrationFilter->SetInput( NULL );
+    m_RegistrationFilter->SetInput( ITK_NULLPTR );
     m_RegistrationFilter->GetOutput()->ReleaseData();
 
 }
index ecc136b62f217fae0f4bbf22d3698b65c46ceb66..4560b185e93c7969135372e17ad15a6e8c4afccc 100644 (file)
@@ -51,7 +51,7 @@ if(CLITK_BUILD_SEGMENTATION)
     add_executable(clitkRegionGrowing clitkRegionGrowing.cxx ${clitkRegionGrowing_GGO_C} ${clitkRelativePosition_GGO_C})
     target_link_libraries(clitkRegionGrowing clitkCommon)
 
-    install(TARGETS ${SEGMENTATION_INSTALL} DESTINATION bin PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_EXECUTE WORLD_EXECUTE)
+    install(TARGETS ${SEGMENTATION_INSTALL} DESTINATION bin PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE)
 
 endif(CLITK_BUILD_SEGMENTATION)
 
index c2efe5222ef30612463251afc27f16a231594996..47734300ea11185ba25dbcbb2509a8179a268944 100644 (file)
@@ -38,7 +38,7 @@ template<class ArgsInfoType>
 template<unsigned int Dim>
 void clitk::ExtractBonesGenericFilter<ArgsInfoType>::InitializeImageType() 
 {  
-  ADD_IMAGE_TYPE(Dim, short);
+  //ADD_IMAGE_TYPE(Dim, short);
   // ADD_IMAGE_TYPE(Dim, int);
   ADD_IMAGE_TYPE(Dim, float);
 }
index f4944409a1b10e6286a9930b1046b659a2301ece..5b695aef4e7b3449eb1095fec2e47cfa81d4e40c 100644 (file)
@@ -1,7 +1,8 @@
 #File clitkExtractPatient.ggo
 package "clitkExtractPatient"
 version "1.0"
-purpose "Input is binarized using initial thresholds, connected components are labeled (firstLabel). The air label (1) is removed. The remaining is binarized and relabeled, patient should now be the principal label (secondLabel). Two mechanismes are provided to influence the label images. Crop to reduce connectivity (image is restored to original size), eg for SBF. Decomposition through ersion and reconstruction through dilation (slow), eg for Pulmo bellows. Choose which labels to keep from second Label image. Final mask is cleaned by opening and closing."
+purpose "Input is binarized using initial thresholds, connected components are labeled (firstLabel). The air label (1) is removed. The remaining is binarized and relabeled, patient should now be the principal label (secondLabel). Two mechanismes are provided to influence the label images. Crop to reduce connectivity (image is restored to original size), eg for SBF. Decomposition through erosion and reconstruction through dilation (slow), eg for Pulmo bellows. Choose which labels to keep from second Label image. Final mask is cleaned by opening and closing.
+The image is padded first with air. If lungs are touching the border (so the air), set openingRadius to 1 in order to have lungs segmented inside the patient"
 
 option "config"                -  "Config file"                  string        no
 option "imagetypes"     -  "Display allowed image types"  flag          off
index fedf853c82590918849ddc8df2e9e58ec06a7f9e..97f79c5e9050bdcf1b5b4d2275ad0a32e73c60f8 100644 (file)
@@ -117,6 +117,7 @@ GenerateOutputInformation() {
   StartNewStep("Find low densities areas");
 
   // Pad images with air to prevent patient touching the image border
+  // But the lungs can touch the outside air. In such case, use primaryOpeningRadius with 1 kernel
   typedef itk::ConstantPadImageFilter<InputImageType, InputImageType> PadFilterType;
   typename PadFilterType::Pointer padFilter = PadFilterType::New();
   padFilter->SetInput(input);
diff --git a/sonar-project.properties b/sonar-project.properties
new file mode 100644 (file)
index 0000000..a463e17
--- /dev/null
@@ -0,0 +1,25 @@
+sonar.projectKey=vv
+sonar.projectName=vv
+sonar.projectVersion=1.4
+
+# =====================================================
+#   Meta-data for the project
+# =====================================================
+
+sonar.links.homepage=https://github.com/open-vv/vv
+sonar.links.ci=https://travis-ci.org/open-vv/vv
+sonar.links.scm=https://github.com/open-vv/vv
+sonar.links.issue=https://github.com/open-vv/vv/issues
+sonar.host.url=https://sonarcloud.io
+
+
+# =====================================================
+#   Properties that will be shared amongst all modules
+# =====================================================
+
+# SQ standard properties
+sonar.sources=.
+
+# Properties specific to the C/C++ analyzer:
+sonar.cfamily.build-wrapper-output=bw-output
+sonar.cfamily.gcov.reportsPath=.
\ No newline at end of file
index 515e99a9aef9ecda238d7938e663a0d9f3664822..9af9f16bc0e3741c44198c3f421c99b291c5fe7c 100644 (file)
@@ -21,9 +21,6 @@ add_library(clitkImageArithmImageLib clitkImageArithmGenericFilter.cxx ${clitkIm
 WRAP_GGO(clitkVectorArithm_GGO_C clitkVectorArithm.ggo)
 add_library(clitkVectorArithmLib clitkVectorArithmGenericFilter.cxx ${clitkVectorArithm_GGO_C})
 
-WRAP_GGO(clitkResampleImage_GGO_C clitkResampleImage.ggo)
-add_library(clitkResampleImageLib clitkResampleImageGenericFilter.cxx ${clitkResampleImage_GGO_C})
-
 WRAP_GGO(clitkMIP_GGO_C clitkMIP.ggo)
 add_library(clitkMIPLib clitkMIPGenericFilter.cxx ${clitkMIP_GGO_C})
 
@@ -64,11 +61,15 @@ if(CLITK_BUILD_TOOLS)
   add_executable(clitkBinarizeImage clitkBinarizeImage.cxx)
   target_link_libraries(clitkBinarizeImage clitkBinarizeImageLib clitkCommon)
   set(TOOLS_INSTALL ${TOOLS_INSTALL} clitkBinarizeImage)
-  
+
   add_executable(clitkProfileImage clitkProfileImage.cxx)
   target_link_libraries(clitkProfileImage clitkProfileImageLib clitkCommon)
   set(TOOLS_INSTALL ${TOOLS_INSTALL} clitkProfileImage)
 
+  add_executable(clitkHistogramImage clitkHistogramImage.cxx)
+  target_link_libraries(clitkHistogramImage clitkHistogramImageLib clitkCommon)
+  set(TOOLS_INSTALL ${TOOLS_INSTALL} clitkHistogramImage)
+
   WRAP_GGO(clitkVFResample_GGO_C clitkVFResample.ggo)
   add_executable(clitkVFResample clitkVFResample.cxx clitkVFResampleGenericFilter.cxx ${clitkVFResample_GGO_C})
   target_link_libraries(clitkVFResample clitkCommon)
@@ -104,6 +105,31 @@ if(CLITK_BUILD_TOOLS)
   target_link_libraries(clitkImage2DicomSeries clitkCommon )
   set(TOOLS_INSTALL ${TOOLS_INSTALL} clitkImage2DicomSeries)
 
+  WRAP_GGO(clitkChangeDicomTag_GGO_C clitkChangeDicomTag.ggo)
+  add_executable(clitkChangeDicomTag clitkChangeDicomTag.cxx ${clitkChangeDicomTag_GGO_C})
+  target_link_libraries(clitkChangeDicomTag clitkCommon )
+  set(TOOLS_INSTALL ${TOOLS_INSTALL} clitkChangeDicomTag)
+
+  WRAP_GGO(clitkGateSimulation2Dicom_GGO_C clitkGateSimulation2Dicom.ggo)
+  add_executable(clitkGateSimulation2Dicom clitkGateSimulation2Dicom.cxx ${clitkGateSimulation2Dicom_GGO_C})
+  target_link_libraries(clitkGateSimulation2Dicom clitkCommon )
+  set(TOOLS_INSTALL ${TOOLS_INSTALL} clitkGateSimulation2Dicom)
+
+  WRAP_GGO(clitkPartitionEnergyWindowDicom_GGO_C clitkPartitionEnergyWindowDicom.ggo)
+  add_executable(clitkPartitionEnergyWindowDicom clitkPartitionEnergyWindowDicom.cxx ${clitkPartitionEnergyWindowDicom_GGO_C})
+  target_link_libraries(clitkPartitionEnergyWindowDicom clitkCommon )
+  set(TOOLS_INSTALL ${TOOLS_INSTALL} clitkPartitionEnergyWindowDicom)
+
+  WRAP_GGO(clitkUpdateVRTagDicom_GGO_C clitkUpdateVRTagDicom.ggo)
+  add_executable(clitkUpdateVRTagDicom clitkUpdateVRTagDicom.cxx ${clitkUpdateVRTagDicom_GGO_C})
+  target_link_libraries(clitkUpdateVRTagDicom clitkCommon )
+  set(TOOLS_INSTALL ${TOOLS_INSTALL} clitkUpdateVRTagDicom)
+
+  WRAP_GGO(clitkImage2DicomDose_GGO_C clitkImage2DicomDose.ggo)
+  add_executable(clitkImage2DicomDose clitkImage2DicomDose.cxx ${clitkImage2DicomDose_GGO_C})
+  target_link_libraries(clitkImage2DicomDose clitkCommon)
+  set(TOOLS_INSTALL ${TOOLS_INSTALL} clitkImage2DicomDose)
+
   WRAP_GGO(clitkMedianTemporalDimension_GGO_C clitkMedianTemporalDimension.ggo)
   add_executable(clitkMedianTemporalDimension clitkMedianTemporalDimension.cxx
     ${clitkMedianTemporalDimension_GGO_C})
@@ -173,10 +199,6 @@ if(CLITK_BUILD_TOOLS)
   target_link_libraries(clitkMedianImageFilter clitkMedianImageFilterLib clitkCommon)
   set(TOOLS_INSTALL ${TOOLS_INSTALL} clitkMedianImageFilter)
 
-  add_executable(clitkResampleImage clitkResampleImage.cxx)
-  target_link_libraries(clitkResampleImage clitkResampleImageLib clitkCommon)
-  set(TOOLS_INSTALL ${TOOLS_INSTALL} clitkResampleImage)
-
   WRAP_GGO(clitkMinMaxMask_GGO_C clitkMinMaxMask.ggo)
   add_executable(clitkMinMaxMask clitkMinMaxMask.cxx ${clitkMinMaxMask_GGO_C})
   target_link_libraries(clitkMinMaxMask clitkCommon  )
@@ -192,11 +214,16 @@ if(CLITK_BUILD_TOOLS)
   target_link_libraries(clitkDicomRTStruct2Image clitkDicomRTStruct)
   set(TOOLS_INSTALL ${TOOLS_INSTALL} clitkDicomRTStruct2Image)
 
-  #if(CLITK_USE_SYSTEM_GDCM)
+  if(CLITK_USE_SYSTEM_GDCM)
     WRAP_GGO(clitkImage2DicomRTStruct_GGO_C clitkImage2DicomRTStruct.ggo)
     add_executable(clitkImage2DicomRTStruct clitkImage2DicomRTStruct.cxx ${clitkImage2DicomRTStruct_GGO_C})
     target_link_libraries(clitkImage2DicomRTStruct clitkDicomRTStruct)
-  #endif()
+
+    #include pugiXML to parse XML
+    WRAP_GGO(clitkXml2DicomRTStruct_GGO_C clitkXml2DicomRTStruct.ggo)
+    add_executable(clitkXml2DicomRTStruct ../utilities/pugixml/pugixml.cpp clitkXml2DicomRTStruct.cxx ${clitkXml2DicomRTStruct_GGO_C})
+    target_link_libraries(clitkXml2DicomRTStruct clitkDicomRTStruct)
+  endif()
 
   WRAP_GGO(clitkComposeVF_GGO_C clitkComposeVF.ggo)
   add_executable(clitkComposeVF clitkComposeVFGenericFilter.cxx clitkComposeVF.cxx ${clitkComposeVF_GGO_C})
@@ -345,6 +372,12 @@ if(CLITK_BUILD_TOOLS)
   target_link_libraries(clitkImageIntensityWindowing clitkCommon )
   set(TOOLS_INSTALL ${TOOLS_INSTALL} clitkImageIntensityWindowing)
 
+  WRAP_GGO(clitkMaskOfIntegratedIntensity_GGO_C clitkMaskOfIntegratedIntensity.ggo)
+  add_executable(clitkMaskOfIntegratedIntensity clitkMaskOfIntegratedIntensity.cxx ${clitkMaskOfIntegratedIntensity_GGO_C})
+  target_link_libraries(clitkMaskOfIntegratedIntensity clitkCommon )
+  set(TOOLS_INSTALL ${TOOLS_INSTALL} clitkMaskOfIntegratedIntensity)
+
+
   WRAP_GGO(clitkBlurImage_GGO_C clitkBlurImage.ggo)
   add_executable(clitkBlurImage clitkBlurImage.cxx ${clitkBlurImage_GGO_C})
   target_link_libraries(clitkBlurImage clitkCommon )
index bb74448cead8ee4624a9087bd5958ecdaa3e29d5..1d4c43ad85a4370e7d1b9b3a24497f4f150129d5 100644 (file)
@@ -17,11 +17,13 @@ option "size"               - "New output size if different from input"     int     no      multiple
 option "spacing"       - "New output spacing if different from input"  double  no      multiple
 option "spacinglike"   - "New output spacing like this image"          string  no
 option "origin"                - "New output origin if different from input"   double  no      multiple
+option "direction"     - "New output direction if different from input" double no      multiple
 option "matrix"                m "Affine matrix (homogene) filename"           string  no
 option "elastix"       e "Read EulerTransform from elastix output file (combine if multiple)"  string  no
 option "rotate"                r "Rotation to apply (radians)"                 double  no      multiple
 option "translate"     t "Translation to apply (mm)"                   double  no      multiple
 option "pad"           - "Edge padding value"                          double  no      default="0.0"
+option "adaptive"              - "Adapt the size, spacing or the origin when one of the previous tag is on (use previous clitkResampleImage)" flag off
 
 section "Interpolation"
 option "interp"                - "Interpolation: 0=NN, 1=Linear, 2=BSpline, 3=BLUT"    int     no  default="1"
@@ -31,6 +33,6 @@ option "interpVF"     - "Interpolation: 0=NN, 1=Linear, 2=BSpline, 3=BLUT"    int     no
 option "interpVFOrder" - "Order if BLUT or BSpline (0-5)"                      int     no  default="3"
 option "interpVFSF"    - "Sampling factor if BLUT"                             int     no  default="20"
 
-
-
-
+section "Gaussian filtering"
+option "gauss" g "Apply gaussian filter before (sigma in mm) (default=0.0)" double no multiple
+option "autogauss" - "Apply gaussian filter before with auto sigma when downsampling (default=off)" flag off
\ No newline at end of file
index 613e1cc677b868305c2cf5e5513baacb7026751a..482114b0adfd4d9e342855a7817c20cbdf8e1076 100644 (file)
@@ -73,7 +73,7 @@ namespace clitk
 
     //----------------------------------------
     // Set & Get
-    //----------------------------------------    
+    //----------------------------------------
     void SetArgsInfo(const args_info_type & a)
     {
       m_ArgsInfo=a;
@@ -82,28 +82,34 @@ namespace clitk
     }
     
     
-    //----------------------------------------  
+    //----------------------------------------
     // Update
-    //----------------------------------------  
+    //----------------------------------------
     void Update();
 
+
+    //----------------------------------------
+    // Compute bounding box
+    //----------------------------------------
+    vnl_vector<double> ComputeSize(vnl_vector<double> inputSize, vnl_matrix<double> transformationMatrix, bool returnMin);
+
   protected:
 
-    //----------------------------------------  
+    //----------------------------------------
     // Constructor & Destructor
-    //----------------------------------------  
+    //----------------------------------------
     AffineTransformGenericFilter();
     ~AffineTransformGenericFilter() {};
 
     
-    //----------------------------------------  
+    //----------------------------------------
     // Templated members
-    //----------------------------------------  
+    //----------------------------------------
     template <unsigned int Dimension>  void UpdateWithDim(std::string PixelType, int Components);
     template <unsigned int Dimension, class PixelType>  void UpdateWithDimAndPixelType();
     template <unsigned int Dimension, class PixelType>  void UpdateWithDimAndVectorType();
 
-    //----------------------------------------  
+    //----------------------------------------
     // Data members
     //----------------------------------------
     args_info_type m_ArgsInfo;
index 93d43571b11d170825ad6f31e09b5646e653f086..ab488f3710edcddb8952f142a53d051acf157827 100644 (file)
@@ -22,7 +22,9 @@
 #include <istream>
 #include <iterator>
 #include <itkCenteredEuler3DTransform.h>
+#include <itkRecursiveGaussianImageFilter.h>
 #include "clitkElastix.h"
+#include "clitkResampleImageWithOptionsFilter.h"
 
 namespace clitk
 {
@@ -107,6 +109,95 @@ namespace clitk
 
   }
   //-------------------------------------------------------------------
+
+
+  //-------------------------------------------------------------------
+  // Compute updated bounding box
+  //-------------------------------------------------------------------
+  template<class args_info_type>
+  vnl_vector<double>
+  AffineTransformGenericFilter<args_info_type>::ComputeSize(vnl_vector<double> inputSize, vnl_matrix<double> transformationMatrix, bool returnMin)
+  {
+    //Compute input corners
+    int Dimension = inputSize.size();
+    vnl_matrix<double> vnlOutputSize(std::pow(2, Dimension), Dimension);
+    vnlOutputSize.fill(0);
+    if (Dimension == 2) {
+      for(unsigned int i=0; i< Dimension; i++)
+        vnlOutputSize[3][i] = inputSize[i];
+      vnlOutputSize[1][0] = inputSize[0];
+      vnlOutputSize[2][1] = inputSize[1];
+    } else if (Dimension == 3) {
+      for(unsigned int i=0; i< Dimension; i++)
+        vnlOutputSize[7][i] = inputSize[i];
+      vnlOutputSize[1][0] = inputSize[0];
+      vnlOutputSize[2][1] = inputSize[1];
+      vnlOutputSize[3][2] = inputSize[2];
+      vnlOutputSize[4][0] = inputSize[0];
+      vnlOutputSize[4][1] = inputSize[1];
+      vnlOutputSize[5][1] = inputSize[1];
+      vnlOutputSize[5][2] = inputSize[2];
+      vnlOutputSize[6][0] = inputSize[0];
+      vnlOutputSize[6][2] = inputSize[2];
+    } else { //Dimension ==4
+      for(unsigned int i=0; i< Dimension; i++)
+        vnlOutputSize[15][i] = inputSize[i];
+      vnlOutputSize[1][0] = inputSize[0];
+      vnlOutputSize[2][1] = inputSize[1];
+      vnlOutputSize[3][2] = inputSize[2];
+      vnlOutputSize[4][3] = inputSize[3];
+      vnlOutputSize[5][0] = inputSize[0];
+      vnlOutputSize[5][1] = inputSize[1];
+      vnlOutputSize[6][0] = inputSize[0];
+      vnlOutputSize[6][2] = inputSize[2];
+      vnlOutputSize[7][0] = inputSize[0];
+      vnlOutputSize[7][3] = inputSize[3];
+      vnlOutputSize[8][1] = inputSize[1];
+      vnlOutputSize[8][2] = inputSize[2];
+      vnlOutputSize[9][1] = inputSize[1];
+      vnlOutputSize[9][3] = inputSize[3];
+      vnlOutputSize[10][2] = inputSize[2];
+      vnlOutputSize[10][3] = inputSize[3];
+      vnlOutputSize[11][0] = inputSize[0];
+      vnlOutputSize[11][1] = inputSize[1];
+      vnlOutputSize[11][2] = inputSize[2];
+      vnlOutputSize[12][0] = inputSize[0];
+      vnlOutputSize[12][1] = inputSize[1];
+      vnlOutputSize[12][3] = inputSize[3];
+      vnlOutputSize[13][0] = inputSize[0];
+      vnlOutputSize[13][2] = inputSize[2];
+      vnlOutputSize[13][3] = inputSize[3];
+      vnlOutputSize[14][1] = inputSize[1];
+      vnlOutputSize[14][2] = inputSize[2];
+      vnlOutputSize[14][3] = inputSize[3];
+    }
+
+    //Compute the transformation of all corner
+    for (unsigned int i=0; i< std::pow(2, Dimension); ++i)
+      vnlOutputSize.set_row(i, transformationMatrix*vnlOutputSize.get_row(i));
+
+    //Compute the bounding box taking the max and the min
+    vnl_vector<double> minBB(vnlOutputSize.get_row(0)), maxBB(vnlOutputSize.get_row(0));
+    for (unsigned int i=0; i< std::pow(2, Dimension); ++i) {
+      for (unsigned int j=0; j< Dimension; ++j) {
+        if (vnlOutputSize[i][j] < minBB[j])
+          minBB[j] = vnlOutputSize[i][j];
+        if (vnlOutputSize[i][j] > maxBB[j])
+          maxBB[j] = vnlOutputSize[i][j];
+      }
+    }
+
+    //Compute the size
+    if (returnMin)
+      return minBB;
+    else {
+      vnl_vector<double> size;
+      size = maxBB - minBB;
+
+      return size;
+    }
+  }
+  //-------------------------------------------------------------------
  
 
   //-------------------------------------------------------------------
@@ -129,6 +220,173 @@ namespace clitk
     reader->Update();
     typename InputImageType::Pointer input= reader->GetOutput();
 
+    //Adaptative size, spacing origin (use previous clitkResampleImage)
+    if (m_ArgsInfo.adaptive_given) {
+      // Filter
+      typedef clitk::ResampleImageWithOptionsFilter<InputImageType, OutputImageType> ResampleImageFilterType;
+      typename ResampleImageFilterType::Pointer filter = ResampleImageFilterType::New();
+      filter->SetInput(input);
+
+      // Set Verbose
+      filter->SetVerboseOptions(m_ArgsInfo.verbose_flag);
+
+      // Set size / spacing
+      static const unsigned int dim = OutputImageType::ImageDimension;
+      typename OutputImageType::SpacingType spacing;
+      typename OutputImageType::SizeType size;
+      typename OutputImageType::PointType origin;
+      typename OutputImageType::DirectionType direction;
+
+      if (m_ArgsInfo.like_given) {
+        itk::ImageIOBase::Pointer header = clitk::readImageHeader(m_ArgsInfo.like_arg);
+        if (header) {
+          for(unsigned int i=0; i<dim; i++){
+            spacing[i] = header->GetSpacing(i);
+            size[i] = header->GetDimensions(i);
+            origin[i] = header->GetOrigin(i);
+          }
+          for(unsigned int i=0; i<dim; i++) {
+            for(unsigned int j=0;j<dim;j++) {
+                direction(i,j) = header->GetDirection(i)[j];
+            }
+          }
+          filter->SetOutputSpacing(spacing);
+          filter->SetOutputSize(size);
+          filter->SetOutputOrigin(origin);
+          filter->SetOutputDirection(direction);
+        }
+        else {
+          std::cerr << "*** Warning : I could not read '" << m_ArgsInfo.like_arg << "' ***" << std::endl;
+          exit(0);
+        }
+      }
+      else {
+        if (m_ArgsInfo.spacing_given == 1) {
+          filter->SetOutputIsoSpacing(m_ArgsInfo.spacing_arg[0]);
+        }
+        else if ((m_ArgsInfo.spacing_given != 0) && (m_ArgsInfo.size_given != 0)) {
+          std::cerr << "Error: use spacing or size, not both." << std::endl;
+          exit(0);
+        }
+        else if (m_ArgsInfo.spacing_given) {
+          if ((m_ArgsInfo.spacing_given != 0) && (m_ArgsInfo.spacing_given != dim)) {
+            std::cerr << "Error: spacing should have one or " << dim << " values." << std::endl;
+            exit(0);
+          }
+          for(unsigned int i=0; i<dim; i++)
+            spacing[i] = m_ArgsInfo.spacing_arg[i];
+          filter->SetOutputSpacing(spacing);
+        }
+        else if (m_ArgsInfo.size_given) {
+          if ((m_ArgsInfo.size_given != 0) && (m_ArgsInfo.size_given != dim)) {
+            std::cerr << "Error: size should have " << dim << " values." << std::endl;
+            exit(0);
+          }
+          for(unsigned int i=0; i<dim; i++)
+            size[i] = m_ArgsInfo.size_arg[i];
+          filter->SetOutputSize(size);
+        }
+        for(unsigned int i=0; i<dim; i++){
+          origin[i] = input->GetOrigin()[i];
+        }
+        for(unsigned int i=0; i<dim; i++) {
+          for(unsigned int j=0;j<dim;j++) {
+              direction(i,j) = input->GetDirection()[i][j];
+          }
+        }
+        filter->SetOutputOrigin(origin);
+        filter->SetOutputDirection(direction);
+      }
+
+      // Set temporal dimension
+      //filter->SetLastDimensionIsTime(m_ArgsInfo.time_flag);
+
+      // Set Gauss
+      filter->SetGaussianFilteringEnabled(m_ArgsInfo.autogauss_flag);
+      if (m_ArgsInfo.gauss_given != 0) {
+        typename ResampleImageFilterType::GaussianSigmaType g;
+        for(unsigned int i=0; i<dim; i++) {
+          g[i] = m_ArgsInfo.gauss_arg[i];
+        }
+        filter->SetGaussianSigma(g);
+      }
+
+      // Set Interpolation
+      int interp = m_ArgsInfo.interp_arg;
+      if (interp == 0) {
+        filter->SetInterpolationType(ResampleImageFilterType::NearestNeighbor);
+      } else {
+        if (interp == 1) {
+          filter->SetInterpolationType(ResampleImageFilterType::Linear);
+        } else {
+          if (interp == 2) {
+            filter->SetInterpolationType(ResampleImageFilterType::BSpline);
+          } else {
+            if (interp == 3) {
+              filter->SetInterpolationType(ResampleImageFilterType::B_LUT);
+            } else {
+                std::cerr << "Error. I do not know interpolation '" << m_ArgsInfo.interp_arg
+                          << "'. Choose among: nn, linear, bspline, blut, windowed sinc" << std::endl;
+                exit(0);
+            }
+          }
+        }
+      }
+
+      // Set default pixel value
+      filter->SetDefaultPixelValue(m_ArgsInfo.pad_arg);
+
+      // Set thread
+      //if (m_ArgsInfo.thread_given) {
+      //  filter->SetNumberOfThreads(m_ArgsInfo.thread_arg);
+      //}
+
+      // Go !
+      filter->Update();
+      typename OutputImageType::Pointer output = filter->GetOutput();
+      //this->template SetNextOutput<OutputImageType>(outputImage);
+
+      // Output
+      typedef itk::ImageFileWriter<OutputImageType> WriterType;
+      typename WriterType::Pointer writer = WriterType::New();
+      writer->SetFileName(m_ArgsInfo.output_arg);
+      writer->SetInput(output);
+      writer->Update();
+
+      return;
+    }
+
+    //Gaussian pre-filtering
+    typename itk::Vector<double, Dimension> gaussianSigma;
+    gaussianSigma.Fill(0);
+    bool gaussianFilteringEnabled(false);
+    bool autoGaussEnabled(false);
+    if (m_ArgsInfo.autogauss_given) { // Gaussian filter auto
+      autoGaussEnabled = m_ArgsInfo.autogauss_flag;
+    }
+    if (m_ArgsInfo.gauss_given) { // Gaussian filter set by user
+      gaussianFilteringEnabled = true;
+      if (m_ArgsInfo.gauss_given == 1)
+      {
+        for (unsigned int i=0; i<Dimension; i++)
+        {
+          gaussianSigma[i] = m_ArgsInfo.gauss_arg[0];
+        }
+      }
+      else if (m_ArgsInfo.gauss_given == Dimension)
+      {
+        for (unsigned int i=0; i<Dimension; i++)
+        {
+          gaussianSigma[i] = m_ArgsInfo.gauss_arg[i];
+        }
+      }
+      else
+      {
+        std::cerr << "Gaussian sigma dimension is incorrect" << std::endl;
+        return;
+      }
+    }
+
     //Filter
     typedef  itk::ResampleImageFilter< InputImageType,OutputImageType >  ResampleFilterType;
     typename ResampleFilterType::Pointer resampler = ResampleFilterType::New();
@@ -213,6 +471,14 @@ namespace clitk
       likeReader->Update();
       resampler->SetOutputParametersFromImage(likeReader->GetOutput());
       resampler->SetOutputDirection(likeReader->GetOutput()->GetDirection());
+      if (autoGaussEnabled) { // Automated sigma when downsample
+        for(unsigned int i=0; i<Dimension; i++) {
+          if (likeReader->GetOutput()->GetSpacing()[i] > input->GetSpacing()[i]) { // downsample
+            gaussianSigma[i] = 0.5*likeReader->GetOutput()->GetSpacing()[i];// / inputSpacing[i]);
+          }
+          else gaussianSigma[i] = 0; // will be ignore after
+        }
+      }
     } else if(m_ArgsInfo.transform_grid_flag) {
       typename itk::Matrix<double, Dimension+1, Dimension+1> invMatrix( matrix.GetInverse() );
       typename itk::Matrix<double, Dimension, Dimension> invRotMatrix( clitk::GetRotationalPartMatrix(invMatrix) );
@@ -224,12 +490,6 @@ namespace clitk
       if (m_ArgsInfo.origin_given)
         std::cout << "Warning --origin ignored (because --transform_grid_flag)" << std::endl;
 
-      // Spacing is influenced by affine transform matrix and input direction
-      typename InputImageType::SpacingType outputSpacing;
-      outputSpacing = invRotMatrix *
-        input->GetDirection() *
-        input->GetSpacing();
-
       // Origin is influenced by translation but not by input direction
       typename InputImageType::PointType outputOrigin;
       outputOrigin = invRotMatrix *
@@ -238,13 +498,30 @@ namespace clitk
 
       // Size is influenced by affine transform matrix and input direction
       // Size is converted to double, transformed and converted back to size type.
-      vnl_vector<double> vnlOutputSize(Dimension);
+      // Determine the bounding box tranforming all corners
+      vnl_vector<double> vnlOutputSize(Dimension), vnlOutputmmSize(Dimension), vnlOutputOffset(Dimension);
+      typename InputImageType::SpacingType outputSpacing;
       for(unsigned int i=0; i< Dimension; i++) {
         vnlOutputSize[i] = input->GetLargestPossibleRegion().GetSize()[i];
+        vnlOutputmmSize[i] = input->GetLargestPossibleRegion().GetSize()[i]*input->GetSpacing()[i];
+        vnlOutputOffset[i] = input->GetLargestPossibleRegion().GetSize()[i]*input->GetSpacing()[i];
+      }
+      vnlOutputSize = ComputeSize(vnlOutputSize, invRotMatrix.GetVnlMatrix() * input->GetDirection().GetVnlMatrix(), 0);
+      vnlOutputmmSize = ComputeSize(vnlOutputmmSize, invRotMatrix.GetVnlMatrix() * input->GetDirection().GetVnlMatrix(), 0);
+      vnlOutputOffset = ComputeSize(vnlOutputOffset, invRotMatrix.GetVnlMatrix() * input->GetDirection().GetVnlMatrix(), 1);
+      for(unsigned int i=0; i< Dimension; i++) {
+        outputSpacing[i] = vnlOutputmmSize[i]/lrint(vnlOutputSize[i]);
+        outputOrigin[i] += vnlOutputOffset[i];
       }
-      vnlOutputSize = invRotMatrix *
-        input->GetDirection().GetVnlMatrix() *
-        vnlOutputSize;
+      if (autoGaussEnabled) { // Automated sigma when downsample
+        for(unsigned int i=0; i<Dimension; i++) {
+          if (outputSpacing[i] > input->GetSpacing()[i]) { // downsample
+            gaussianSigma[i] = 0.5*outputSpacing[i];// / inputSpacing[i]);
+          }
+          else gaussianSigma[i] = 0; // will be ignore after
+        }
+      }
+
       typename OutputImageType::SizeType outputSize;
       for(unsigned int i=0; i< Dimension; i++) {
         // If the size is negative, we have a flip and we must modify
@@ -273,6 +550,14 @@ namespace clitk
         for(unsigned int i=0; i< Dimension; i++)
           outputSpacing[i]=m_ArgsInfo.spacing_arg[i];
       } else outputSpacing=input->GetSpacing();
+      if (autoGaussEnabled) { // Automated sigma when downsample
+        for(unsigned int i=0; i<Dimension; i++) {
+          if (outputSpacing[i] > input->GetSpacing()[i]) { // downsample
+            gaussianSigma[i] = 0.5*outputSpacing[i];// / inputSpacing[i]);
+          }
+          else gaussianSigma[i] = 0; // will be ignore after
+        }
+      }
 
       //Origin
       typename OutputImageType::PointType outputOrigin;
@@ -281,10 +566,19 @@ namespace clitk
           outputOrigin[i]=m_ArgsInfo.origin_arg[i];
       } else outputOrigin=input->GetOrigin();
 
+      //Direction
+      typename OutputImageType::DirectionType outputDirection;
+      if (m_ArgsInfo.direction_given) {
+        for(unsigned int j=0; j< Dimension; j++)
+            for(unsigned int i=0; i< Dimension; i++)
+                outputDirection[j][i]=m_ArgsInfo.direction_arg[i+Dimension*j];
+      } else outputDirection=input->GetDirection();
+
       // Set
       resampler->SetSize( outputSize );
       resampler->SetOutputSpacing( outputSpacing );
       resampler->SetOutputOrigin(  outputOrigin );
+      resampler->SetOutputDirection( outputDirection );
 
     }
 
@@ -315,9 +609,31 @@ namespace clitk
       std::cout << "Setting the output size to " << resampler->GetSize() << "..." << std::endl;
       std::cout << "Setting the output spacing to " << resampler->GetOutputSpacing() << "..." << std::endl;
       std::cout << "Setting the output origin to " << resampler->GetOutputOrigin() << "..." << std::endl;
+      std::cout << "Setting the output direction to " << resampler->GetOutputDirection() << "..." << std::endl;
     }
 
-    resampler->SetInput( input );
+    typedef itk::RecursiveGaussianImageFilter<InputImageType, InputImageType> GaussianFilterType;
+    std::vector<typename GaussianFilterType::Pointer> gaussianFilters;
+    if (gaussianFilteringEnabled || autoGaussEnabled) {
+      for(unsigned int i=0; i<Dimension; i++) {
+        if (gaussianSigma[i] != 0) {
+          gaussianFilters.push_back(GaussianFilterType::New());
+          gaussianFilters[i]->SetDirection(i);
+          gaussianFilters[i]->SetOrder(GaussianFilterType::ZeroOrder);
+          gaussianFilters[i]->SetNormalizeAcrossScale(false);
+          gaussianFilters[i]->SetSigma(gaussianSigma[i]); // in millimeter !
+          if (gaussianFilters.size() == 1) { // first
+            gaussianFilters[0]->SetInput(input);
+          } else {
+            gaussianFilters[i]->SetInput(gaussianFilters[i-1]->GetOutput());
+          }
+        }
+      }
+      if (gaussianFilters.size() > 0) {
+        resampler->SetInput(gaussianFilters[gaussianFilters.size()-1]->GetOutput());
+      } else resampler->SetInput(input);
+    } else resampler->SetInput(input);
+
     resampler->SetTransform( affineTransform );
     resampler->SetInterpolator( genericInterpolator->GetInterpolatorPointer());
     resampler->SetDefaultPixelValue( static_cast<PixelType>(m_ArgsInfo.pad_arg) );
@@ -359,6 +675,37 @@ namespace clitk
     reader->Update();
     typename InputImageType::Pointer input= reader->GetOutput();
 
+    //Gaussian pre-filtering
+    typename itk::Vector<double, Dimension> gaussianSigma;
+    gaussianSigma.Fill(0);
+    bool gaussianFilteringEnabled(false);
+    bool autoGaussEnabled(false);
+    if (m_ArgsInfo.autogauss_given) { // Gaussian filter auto
+      autoGaussEnabled = m_ArgsInfo.autogauss_flag;
+    }
+    if (m_ArgsInfo.gauss_given) { // Gaussian filter set by user
+      gaussianFilteringEnabled = true;
+      if (m_ArgsInfo.gauss_given == 1)
+      {
+        for (unsigned int i=0; i<Dimension; i++)
+        {
+          gaussianSigma[i] = m_ArgsInfo.gauss_arg[0];
+        }
+      }
+      else if (m_ArgsInfo.gauss_given == Dimension)
+      {
+        for (unsigned int i=0; i<Dimension; i++)
+        {
+          gaussianSigma[i] = m_ArgsInfo.gauss_arg[i];
+        }
+      }
+      else
+      {
+        std::cerr << "Gaussian sigma dimension is incorrect" << std::endl;
+        return;
+      }
+    }
+
     //Filter
     typedef  itk::VectorResampleImageFilter< InputImageType,OutputImageType, double >  ResampleFilterType;
     typename ResampleFilterType::Pointer resampler = ResampleFilterType::New();
@@ -438,6 +785,15 @@ namespace clitk
       resampler->SetSize( likeReader->GetOutput()->GetLargestPossibleRegion().GetSize() );
       resampler->SetOutputSpacing( likeReader->GetOutput()->GetSpacing() );
       resampler->SetOutputOrigin(  likeReader->GetOutput()->GetOrigin() );
+      resampler->SetOutputDirection( likeReader->GetOutput()->GetDirection() );
+      if (autoGaussEnabled) { // Automated sigma when downsample
+        for(unsigned int i=0; i<Dimension; i++) {
+          if (likeReader->GetOutput()->GetSpacing()[i] > input->GetSpacing()[i]) { // downsample
+            gaussianSigma[i] = 0.5*likeReader->GetOutput()->GetSpacing()[i];// / inputSpacing[i]);
+          }
+          else gaussianSigma[i] = 0; // will be ignore after
+        }
+      }
     } else {
       //Size
       typename OutputImageType::SizeType outputSize;
@@ -453,6 +809,14 @@ namespace clitk
         for(unsigned int i=0; i< Dimension; i++)
           outputSpacing[i]=m_ArgsInfo.spacing_arg[i];
       } else outputSpacing=input->GetSpacing();
+      if (autoGaussEnabled) { // Automated sigma when downsample
+        for(unsigned int i=0; i<Dimension; i++) {
+          if (outputSpacing[i] > input->GetSpacing()[i]) { // downsample
+            gaussianSigma[i] = 0.5*outputSpacing[i];// / inputSpacing[i]);
+          }
+          else gaussianSigma[i] = 0; // will be ignore after
+        }
+      }
       std::cout<<"Setting the spacing to "<<outputSpacing<<"..."<<std::endl;
 
       //Origin
@@ -463,13 +827,45 @@ namespace clitk
       } else outputOrigin=input->GetOrigin();
       std::cout<<"Setting the origin to "<<outputOrigin<<"..."<<std::endl;
 
+      //Direction
+      typename OutputImageType::DirectionType outputDirection;
+      if (m_ArgsInfo.direction_given) {
+        for(unsigned int j=0; j< Dimension; j++)
+            for(unsigned int i=0; i< Dimension; i++)
+                outputDirection[j][i]=m_ArgsInfo.direction_arg[i+Dimension*j];
+      } else outputDirection=input->GetDirection();
+      std::cout<<"Setting the direction to "<<outputDirection<<"..."<<std::endl;
+
       // Set
       resampler->SetSize( outputSize );
       resampler->SetOutputSpacing( outputSpacing );
       resampler->SetOutputOrigin(  outputOrigin );
+      resampler->SetOutputDirection( outputDirection );
 
     }
 
+    typedef itk::RecursiveGaussianImageFilter<InputImageType, InputImageType> GaussianFilterType;
+    std::vector<typename GaussianFilterType::Pointer> gaussianFilters;
+    if (gaussianFilteringEnabled || autoGaussEnabled) {
+      for(unsigned int i=0; i<Dimension; i++) {
+        if (gaussianSigma[i] != 0) {
+          gaussianFilters.push_back(GaussianFilterType::New());
+          gaussianFilters[i]->SetDirection(i);
+          gaussianFilters[i]->SetOrder(GaussianFilterType::ZeroOrder);
+          gaussianFilters[i]->SetNormalizeAcrossScale(false);
+          gaussianFilters[i]->SetSigma(gaussianSigma[i]); // in millimeter !
+          if (gaussianFilters.size() == 1) { // first
+            gaussianFilters[0]->SetInput(input);
+          } else {
+            gaussianFilters[i]->SetInput(gaussianFilters[i-1]->GetOutput());
+          }
+        }
+      }
+      if (gaussianFilters.size() > 0) {
+        resampler->SetInput(gaussianFilters[gaussianFilters.size()-1]->GetOutput());
+      } else resampler->SetInput(input);
+    } else resampler->SetInput(input);
+
     resampler->SetInput( input );
     resampler->SetTransform( affineTransform );
     resampler->SetInterpolator( genericInterpolator->GetInterpolatorPointer());
index 408fe7fc654a58f3a49d56496588c33cd463a959..0960891f236f340898174be5c605f555ab3467f2 100644 (file)
@@ -44,7 +44,8 @@ AnisotropicDiffusionGenericFilter::AnisotropicDiffusionGenericFilter():
 template<unsigned int Dim>
 void AnisotropicDiffusionGenericFilter::InitializeImageType()
 {
-  ADD_DEFAULT_IMAGE_TYPES(Dim);
+  ADD_IMAGE_TYPE(Dim, float);
+  ADD_IMAGE_TYPE(Dim, double);
 }
 //--------------------------------------------------------------------
 
index dfc37b8021555f843eb454afaa9c29e01b1f8ee2..3808d33f30967b4e51ba60caeac87ca71771bf2b 100644 (file)
@@ -17,3 +17,4 @@ option "bg"                   -       "Background (BG) or 'ouside' value"               double        no      default
 option "mode"          -       "Use FG and/or BG values (if FG, the BG is replaced by the input image values)" values="FG","BG","both"  no default="both"
 
 
+option "percentage"                p   "Percentage of total pixels values (in %)" double no
index 46f846b0e772cc2ed3aad24ac683b445f5225da7..f94cf1a09206c0404fc2ba004a2195638b9b1cec 100644 (file)
@@ -74,6 +74,9 @@ void BinarizeImageGenericFilter::SetArgsInfo(const args_info_type & a)
   if (mArgsInfo.output_given) {
     SetOutputFilename(mArgsInfo.output_arg);
   }
+  if (mArgsInfo.percentage_given) {
+    SetPercentage(mArgsInfo.percentage_arg);
+  }
 }
 //--------------------------------------------------------------------
 
@@ -85,60 +88,170 @@ template<class InputImageType>
 void
 BinarizeImageGenericFilter::UpdateWithInputImageType()
 {
+  if (mArgsInfo.percentage_given)
+    MaskOfIntegratedIntensity<InputImageType>();
+  else {
+    // Reading input
+    typename InputImageType::Pointer input = this->template GetInput<InputImageType>(0);
 
-  // Reading input
-  typename InputImageType::Pointer input = this->template GetInput<InputImageType>(0);
+    // Main filter
+    typedef typename InputImageType::PixelType PixelType;
+    typedef itk::Image<uchar, InputImageType::ImageDimension> OutputImageType;
 
-  // Main filter
-  typedef typename InputImageType::PixelType PixelType;
-  typedef itk::Image<uchar, InputImageType::ImageDimension> OutputImageType;
-
-  // Filter
-  typedef itk::BinaryThresholdImageFilter<InputImageType, OutputImageType> BinaryThresholdImageFilterType;
-  typename BinaryThresholdImageFilterType::Pointer thresholdFilter=BinaryThresholdImageFilterType::New();
-  thresholdFilter->SetInput(input);
-  thresholdFilter->SetInsideValue(mArgsInfo.fg_arg);
-
-  if (mArgsInfo.lower_given) thresholdFilter->SetLowerThreshold(static_cast<PixelType>(mArgsInfo.lower_arg));
-  if (mArgsInfo.upper_given) thresholdFilter->SetUpperThreshold(static_cast<PixelType>(mArgsInfo.upper_arg));
-
-  /* Three modes :
-     - FG -> only use FG value for pixel in the Foreground (or Inside), keep input values for outside
-     - BG -> only use BG value for pixel in the Background (or Outside), keep input values for inside
-     - both -> use FG and BG (real binary image)
-  */
-  if (mArgsInfo.mode_arg == std::string("both")) {
-    thresholdFilter->SetOutsideValue(mArgsInfo.bg_arg);
-    thresholdFilter->Update();
-    typename OutputImageType::Pointer outputImage = thresholdFilter->GetOutput();
-    this->template SetNextOutput<OutputImageType>(outputImage);
-  } else {
-    typename InputImageType::Pointer outputImage;
-    thresholdFilter->SetOutsideValue(0);
-    if (mArgsInfo.mode_arg == std::string("BG")) {
-      typedef itk::MaskImageFilter<InputImageType,OutputImageType> maskFilterType;
-      typename maskFilterType::Pointer maskFilter = maskFilterType::New();
-      maskFilter->SetInput1(input);
-      maskFilter->SetInput2(thresholdFilter->GetOutput());
-      maskFilter->SetOutsideValue(mArgsInfo.bg_arg);
-      maskFilter->Update();
-      outputImage = maskFilter->GetOutput();
+    // Filter
+    typedef itk::BinaryThresholdImageFilter<InputImageType, OutputImageType> BinaryThresholdImageFilterType;
+    typename BinaryThresholdImageFilterType::Pointer thresholdFilter=BinaryThresholdImageFilterType::New();
+    thresholdFilter->SetInput(input);
+    thresholdFilter->SetInsideValue(mArgsInfo.fg_arg);
+
+    if (mArgsInfo.lower_given) thresholdFilter->SetLowerThreshold(static_cast<PixelType>(mArgsInfo.lower_arg));
+    if (mArgsInfo.upper_given) thresholdFilter->SetUpperThreshold(static_cast<PixelType>(mArgsInfo.upper_arg));
+
+    /* Three modes :
+       - FG -> only use FG value for pixel in the Foreground (or Inside), keep input values for outside
+       - BG -> only use BG value for pixel in the Background (or Outside), keep input values for inside
+       - both -> use FG and BG (real binary image)
+    */
+    if (mArgsInfo.mode_arg == std::string("both")) {
+      thresholdFilter->SetOutsideValue(mArgsInfo.bg_arg);
+      thresholdFilter->Update();
+      typename OutputImageType::Pointer outputImage = thresholdFilter->GetOutput();
+      this->template SetNextOutput<OutputImageType>(outputImage);
     } else {
-      typedef itk::MaskNegatedImageFilter<InputImageType,OutputImageType> maskFilterType;
-      typename maskFilterType::Pointer maskFilter = maskFilterType::New();
-      maskFilter->SetInput1(input);
-      maskFilter->SetInput2(thresholdFilter->GetOutput());
-      maskFilter->SetOutsideValue(mArgsInfo.fg_arg);
-      maskFilter->Update();
-      outputImage = maskFilter->GetOutput();
+      typename InputImageType::Pointer outputImage;
+      thresholdFilter->SetOutsideValue(0);
+      if (mArgsInfo.mode_arg == std::string("BG")) {
+        typedef itk::MaskImageFilter<InputImageType,OutputImageType> maskFilterType;
+        typename maskFilterType::Pointer maskFilter = maskFilterType::New();
+        maskFilter->SetInput1(input);
+        maskFilter->SetInput2(thresholdFilter->GetOutput());
+        maskFilter->SetOutsideValue(mArgsInfo.bg_arg);
+        maskFilter->Update();
+        outputImage = maskFilter->GetOutput();
+      } else {
+        typedef itk::MaskNegatedImageFilter<InputImageType,OutputImageType> maskFilterType;
+        typename maskFilterType::Pointer maskFilter = maskFilterType::New();
+        maskFilter->SetInput1(input);
+        maskFilter->SetInput2(thresholdFilter->GetOutput());
+        maskFilter->SetOutsideValue(mArgsInfo.fg_arg);
+        maskFilter->Update();
+        outputImage = maskFilter->GetOutput();
+      }
+      // Write/Save results
+      this->template SetNextOutput<InputImageType>(outputImage);
     }
-    // Write/Save results
-    this->template SetNextOutput<InputImageType>(outputImage);
   }
 }
 //--------------------------------------------------------------------
 
 
+//--------------------------------------------------------------------
+//  https://stackoverflow.com/questions/1577475/c-sorting-and-keeping-track-of-indexes
+template <typename T>
+std::vector<size_t> sort_indexes(const std::vector<T> &v) {
+
+  // initialize original index locations
+  std::vector<size_t> idx(v.size());
+  std::vector<std::pair<T, size_t> > compVector(v.size());
+  for (size_t i = 0; i < v.size(); ++i) {
+    compVector[i].first = v[i];
+    compVector[i].second = i;
+  }
+
+  // sort indexes based on comparing values in v
+  std::sort(compVector.begin(), compVector.end(), comparator<T>);
+  for (size_t i = 0; i < v.size(); ++i) {
+    idx[i] = compVector[i].second;
+  }
+
+  return idx;
+}
+//--------------------------------------------------------------------
+
+
+//--------------------------------------------------------------------
+// Update with the number of dimensions and the pixeltype
+//--------------------------------------------------------------------
+template<class InputImageType>
+void
+BinarizeImageGenericFilter::MaskOfIntegratedIntensity()
+{
+  // Main filter
+  typedef typename InputImageType::PixelType InputPixelType;
+  typedef itk::Image<unsigned char, InputImageType::ImageDimension> MaskImageType;
+
+  // Reading input
+  typename InputImageType::Pointer input = this->template GetInput<InputImageType>(0);
+
+  typename MaskImageType::Pointer mask;
+  mask = MaskImageType::New();
+  mask->SetRegions(input->GetLargestPossibleRegion());
+  mask->SetOrigin(input->GetOrigin());
+  mask->SetSpacing(input->GetSpacing());
+  mask->Allocate();
+  mask->FillBuffer(0);
+
+  // Get a vector of all values (will be easier to sort)
+  // And compute total sum of values
+  std::vector<double> values;
+  typedef itk::ImageRegionIterator<InputImageType> IteratorInputType;
+  IteratorInputType iter(input, input->GetLargestPossibleRegion());
+  iter.GoToBegin();
+  double total = 0.0;
+  while (!iter.IsAtEnd()) {
+    values.push_back(iter.Get());
+    total += iter.Get();
+    ++iter;
+  }
+
+  // Sort (reverse)
+  std::vector<size_t> indices = sort_indexes(values);
+
+  // Get max index of pixel to reach xx percent
+  double current = 0.0;
+  double max = GetPercentage()/100.0*total;
+  int i=0;
+  int n = input->GetLargestPossibleRegion().GetNumberOfPixels();
+  std::vector<int> should_keep(values.size());;
+  std::fill(should_keep.begin(), should_keep.end(), 0);
+  while (current<max && i<n) { // loop by decreasing pixel values
+    current += values[indices[i]];
+    should_keep[indices[i]] = 1.0;
+    ++i;
+  }
+  int nb = i;
+
+  // Set mask values
+  typedef itk::ImageRegionIterator<MaskImageType> IteratorMaskType;
+  IteratorMaskType itm(mask, mask->GetLargestPossibleRegion());
+  iter.GoToBegin();
+  itm.GoToBegin();
+  i = 0;
+  while (!iter.IsAtEnd()) {
+    if (should_keep[i]) itm.Set(1);
+    ++iter;
+    ++itm;
+    ++i;
+  }
+
+  // Verbose option
+  if (this->m_IOVerbose)
+    std::cout << "Sum of pixel values : " << total << std::endl
+              << "Percentage          : " << GetPercentage() << "%" << std::endl
+              << "Number of pixels    : " << nb << "/" << n << std::endl
+              << "Number of pixels    : " << nb/n*100.0 << "%" << std::endl;
+
+  // Write/Save results
+  this->template SetNextOutput<MaskImageType>(mask);
+}
+//--------------------------------------------------------------------
+
+//--------------------------------------------------------------------
+template <typename T>
+bool comparator ( const std::pair<T, size_t>& l, const std::pair<T, size_t>& r)
+ { return l.first > r.first; }
+//--------------------------------------------------------------------
+
 }//end clitk
 
 #endif  //#define clitkBinarizeImageGenericFilter_cxx
index c21e31e2b4de1c7548f3301522cb5498b3c0f54e..4a919f5e7f80f51018358d74430159a551806210 100644 (file)
@@ -46,20 +46,28 @@ namespace clitk
 
     //--------------------------------------------------------------------
     void SetArgsInfo(const args_info_type & a);
+    void SetPercentage(double p) { mPercentage = p; }
+    double GetPercentage() const { return mPercentage; }
 
     //--------------------------------------------------------------------
     // Main function called each time the filter is updated
     template<class InputImageType>  
     void UpdateWithInputImageType();
 
+    template<class InputImageType>
+    void MaskOfIntegratedIntensity();
+
   protected:
     BinarizeImageGenericFilter();
     template<unsigned int Dim> void InitializeImageType();
     args_info_type mArgsInfo;
+    double mPercentage;
     
   }; // end class
   //--------------------------------------------------------------------
     
+//Implementation of the pair comparative function
+template <typename T> bool comparator ( const std::pair<T, size_t>& l, const std::pair<T, size_t>& r);
 } // end namespace clitk
 //--------------------------------------------------------------------
 
index cbcd40c3f1586553cee823e00080353492ec33c7..caa44d571904e46ab53d3b1d0af76ad641fa6014 100644 (file)
@@ -11,4 +11,4 @@ option "imagetypes"   -       "Display allowed image types"     flag          off
 option "input"         i       "Input image filename"            string        required
 option "output"        o       "Output image filename"           string        required
 
-option "variance"       -      "value of the gaussian variance (multiple values=number of image dimension) - default=1.0" double  optional  multiple
+option "variance"       -      "value of the gaussian variance (multiple values=number of image dimension) in mm² - default=1.0" double  optional  multiple
index 307ddb695f97916bc327a3f4cf8dbae2d5863925..ac0a459b1f320548f28f7f11c9a0b94bf122e18a 100644 (file)
@@ -93,10 +93,14 @@ BlurImageGenericFilter<args_info_type>::UpdateWithInputImageType()
   //}
 
   if (mArgsInfo.variance_given && mArgsInfo.variance_given == dim) {
-  for (unsigned int i = 0; i < dim; i++) {
-    //std::cout<<"mArgsInfo.variance_arg[i]"<<mArgsInfo.variance_arg[i]<<std::endl;
-    varianceArray[i] = mArgsInfo.variance_arg[i];
-  }
+    for (unsigned int i = 0; i < dim; i++) {
+      //std::cout<<"mArgsInfo.variance_arg[i]"<<mArgsInfo.variance_arg[i]<<std::endl;
+      varianceArray[i] = mArgsInfo.variance_arg[i];
+    }
+  } else if (mArgsInfo.variance_given && mArgsInfo.variance_given == 1) {
+      for (unsigned int i = 0; i < dim; i++) {
+          varianceArray[i] = mArgsInfo.variance_arg[0];
+      }
   } else {
       for (unsigned int i = 0; i < dim; i++) {
           varianceArray[i] = 1.0;
@@ -114,6 +118,7 @@ BlurImageGenericFilter<args_info_type>::UpdateWithInputImageType()
   typename DiscreteGaussianImageFilterType::Pointer gaussianFilter=DiscreteGaussianImageFilterType::New();
   gaussianFilter->SetInput(input);
   gaussianFilter->SetVariance(varianceArray);
+  gaussianFilter->SetUseImageSpacing(true);
   gaussianFilter->Update();
 
   //std::cout<<"variance value="<<gaussianFilter->GetVariance()<<std::endl;
diff --git a/tools/clitkChangeDicomTag.cxx b/tools/clitkChangeDicomTag.cxx
new file mode 100644 (file)
index 0000000..17b65ed
--- /dev/null
@@ -0,0 +1,53 @@
+/*=========================================================================
+  Program:   vv                     http://www.creatis.insa-lyon.fr/rio/vv
+
+  Authors belong to:
+  - University of LYON              http://www.universite-lyon.fr/
+  - Léon Bérard cancer center       http://www.centreleonberard.fr
+  - CREATIS CNRS laboratory         http://www.creatis.insa-lyon.fr
+
+  This software is distributed WITHOUT ANY WARRANTY; without even
+  the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+  PURPOSE.  See the copyright notices for more information.
+
+  It is distributed under dual licence
+
+  - BSD        See included LICENSE.txt file
+  - CeCILL-B   http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.html
+===========================================================================**/
+
+/* =================================================
+ * @file   clitkChangeDicomTag.cxx
+ * @author TB
+ * @date   07/18
+ *
+ * @brief Change dicom tag value
+ *
+ ===================================================*/
+
+
+// clitk
+#include "clitkChangeDicomTag_ggo.h"
+#include "clitkIO.h"
+#include "clitkChangeDicomTagGenericFilter.h"
+#include "clitkCommon.h"
+
+//--------------------------------------------------------------------
+int main(int argc, char * argv[])
+{
+
+  // Init command line
+  GGO(clitkChangeDicomTag, args_info);
+  CLITK_INIT;
+
+  // Filter
+  typedef clitk::ChangeDicomTagGenericFilter<args_info_clitkChangeDicomTag> FilterType;
+  FilterType::Pointer genericFilter = FilterType::New();
+
+  genericFilter->SetArgsInfo(args_info);
+  genericFilter->Update();
+
+  return EXIT_SUCCESS;
+}// end main
+
+//--------------------------------------------------------------------
diff --git a/tools/clitkChangeDicomTag.ggo b/tools/clitkChangeDicomTag.ggo
new file mode 100644 (file)
index 0000000..cd97cba
--- /dev/null
@@ -0,0 +1,12 @@
+#File clitkChangeDicomTag.ggo
+package "clitkChangeDicomTag"
+version "1.0"
+description "Change Dicom tag -k value to -t"
+
+option "config"   - "Config file"             string  no
+option "verbose"  v "Verbose"                 flag    off
+
+option "input"    i "Input dicom file"        string  yes
+option "output"   o "Output dicom file"       string  yes
+option "key"      k "Keys of tags to modify"  string  no  multiple default="0008|103e"
+option "tag"      t "Tags values"             string  no  multiple default="MIDPOSITION"
index 0e2818ac67e4d3584e20891545aa37d641ce0fba..4adcb9bfadbae4ea0f1b5bc7fa6b9a385990f42e 100644 (file)
@@ -69,7 +69,7 @@ typename clitk::CropImageGenericFilter::AutoCrop<ImageType>::ImagePointer
 clitk::CropImageGenericFilter::AutoCrop<ImageType>::Do(args_info_type &, ImagePointer, PixelDimType<Dim> *)
 {
   clitkExceptionMacro("Autocrop is not implemented for vector fields");
-  return NULL;
+  return ITK_NULLPTR;
 }
 //--------------------------------------------------------------------
 
@@ -182,10 +182,12 @@ void clitk::CropImageGenericFilter::UpdateWithInputImageType()
   typename ImageType::IndexType index = region.GetIndex();
   typename ImageType::PointType origin = output->GetOrigin();
   typename ImageType::SpacingType spacing = output->GetSpacing();
-  if (mArgsInfo.verbose_flag) std::cout << "origin before crop " << origin << std::endl;
-  input->TransformIndexToPhysicalPoint(index,origin);
-  if (mArgsInfo.verbose_flag) std::cout << "origin after crop " << origin << std::endl;
-  output->SetOrigin(origin);
+  if (!mArgsInfo.BG_given) {
+    if (mArgsInfo.verbose_flag) std::cout << "origin before crop " << origin << std::endl;
+    input->TransformIndexToPhysicalPoint(index,origin);
+    if (mArgsInfo.verbose_flag) std::cout << "origin after crop " << origin << std::endl;
+    output->SetOrigin(origin);
+  }
 
   index.Fill(itk::NumericTraits<double>::Zero);
   region.SetIndex(index);
index a6323da9de5ba6ad89c75ab8372049ef569b3200..51a32d15550ff3a1afe2dff199d97514e81ccbd0 100644 (file)
@@ -17,6 +17,7 @@
 ===========================================================================**/
 
 // clitk includes
+#include "clitkIO.h"
 #include "clitkDicom2Image_ggo.h"
 #include "clitkCommon.h"
 #include "clitkImageCommon.h"
@@ -38,6 +39,8 @@ int main(int argc, char * argv[])
 {
   // init command line
   GGO(clitkDicom2Image, args_info);
+  CLITK_INIT;
+
   std::vector<std::string> input_files;
   ///if std_input is given, read the input files from stdin
   if (args_info.std_input_given) {
@@ -58,13 +61,22 @@ int main(int argc, char * argv[])
   int series_number = -1;
   std::set<int> series_numbers;
   std::map< int, std::vector<double> > theorigin;
+  std::map< int, std::vector<double> > theorientation;
   std::map< int, std::vector<double> > sliceLocations;
   std::map< int, std::vector<std::string> > seriesFiles;
-  for(unsigned int i=0; i<args_info.inputs_num; i++) {
-    //std::cout << "Reading <" << input_files[i] << std::endl;
 #if GDCM_MAJOR_VERSION == 2
+  if (args_info.verbose_flag)
+    std::cout << "Using GDCM-2.x" << std::endl;
+#else
+  if (args_info.verbose_flag) {
+    std::cout << "Not using GDCM-2.x" << std::endl;
+    std::cout<< "The image orientation is not supported with this version of GDCM" <<std::endl;
+  }
+#endif
+  for(unsigned int i=0; i<args_info.inputs_num; i++) {
     if (args_info.verbose_flag)
-      std::cout << "Using GDCM-2.x" << std::endl;
+        std::cout << "Reading <" << input_files[i] << std::endl;
+#if GDCM_MAJOR_VERSION == 2
     gdcm::Reader hreader;
     hreader.SetFileName(input_files[i].c_str());
     hreader.Read();
@@ -72,15 +84,28 @@ int main(int argc, char * argv[])
 
     if (args_info.extract_series_flag) {
       gdcm::Attribute<0x20,0x11> series_number_att;
-      series_number_att.SetFromDataSet(hreader.GetFile().GetDataSet());
+      series_number_att.SetFromDataSet(ds);
       series_number = series_number_att.GetValue();
     }
-    
+
     series_numbers.insert(series_number);
     theorigin[series_number] = gdcm::ImageHelper::GetOriginValue(hreader.GetFile());
-    sliceLocations[series_number].push_back(theorigin[series_number][2]);
+    theorientation[series_number] = gdcm::ImageHelper::GetDirectionCosinesValue(hreader.GetFile());
+    if (args_info.extract_series_flag) {
+      double n1 = theorientation[series_number][1]*theorientation[series_number][5]-
+                  theorientation[series_number][2]*theorientation[series_number][4];
+      double n2 = theorientation[series_number][3]*theorientation[series_number][2]-
+                  theorientation[series_number][5]*theorientation[series_number][0];
+      double n3 = theorientation[series_number][0]*theorientation[series_number][4]-
+                  theorientation[series_number][1]*theorientation[series_number][3];
+      double sloc = theorigin[series_number][0]*n1+
+                    theorigin[series_number][1]*n2+
+                    theorigin[series_number][2]*n3;
+      sliceLocations[series_number].push_back(sloc);
+    } else
+      sliceLocations[series_number].push_back(theorigin[series_number][2]);
     seriesFiles[series_number].push_back(input_files[i]);
-    
+
     gdcm::Attribute<0x28, 0x100> pixel_size;
     pixel_size.SetFromDataSet(ds);
     /* if (pixel_size.GetValue() != 16)
@@ -91,8 +116,6 @@ int main(int argc, char * argv[])
        }
     */
 #else
-    if (args_info.verbose_flag)
-      std::cout << "Not using GDCM-2.x" << std::endl;
   gdcm::File *header = new gdcm::File();
   header->SetFileName(input_files[i]);
   header->SetMaxSizeLoadEntry(16384); // required ?
@@ -101,7 +124,7 @@ int main(int argc, char * argv[])
   if (args_info.extract_series_flag) {
     series_number = atoi(header->GetEntryValue(0x20,0x11).c_str());
   }
-  
+
   series_numbers.insert(series_number);
   theorigin[series_number].resize(3);
   theorigin[series_number][0] = header->GetXOrigin();
@@ -127,7 +150,7 @@ int main(int argc, char * argv[])
     std::vector<std::string> files = seriesFiles[*sn];
     std::vector<int> sliceIndex;
     clitk::GetSortedIndex(locs, sliceIndex);
-    if (args_info.verboseSliceLocation_flag) {
+    if (args_info.verbose_flag) {
       std::cout << locs[sliceIndex[0]] << " -> "
                 << sliceIndex[0] << " / " << 0 << " => "
                 << "0 mm "
@@ -183,7 +206,7 @@ int main(int argc, char * argv[])
       std::cerr << reader->GetLastError() << std::endl;
       return 1;
     }
-    
+
     vvImage::Pointer image = reader->GetOutput();
     vtkImageData* vtk_image = image->GetFirstVTKImageData();
     vtkImageChangeInformation* modifier = vtkImageChangeInformation::New();
@@ -209,18 +232,24 @@ int main(int argc, char * argv[])
       outfile = args_info.output_arg;
     else {
       std::ostringstream name;
-      name << *sn << "_" << args_info.output_arg;
+      std::vector<std::string> directory = clitk::SplitFilename(args_info.output_arg);
+      if (directory.size() == 2)
+        name << directory[0] << "/" << *sn << "_" << directory[1];
+      else
+        name << *sn << "_" << args_info.output_arg;
       outfile = name.str();
     }
     vvImageWriter::Pointer writer = vvImageWriter::New();
     writer->SetInput(image);
+    if (args_info.extract_series_flag && !image->GetTransform().empty())
+      writer->SetSaveTransform(true);
     writer->SetOutputFileName(outfile);
     writer->Update();
 
     modifier->Delete();
-    
+
     sn++;
   }
-  
+
   return 0;
 }
index a2d3a89beb6cb8b89f1d80ee392752606683c8a2..7cf454f87ac8c0c6458ba3a2a182ffe047f71c2a 100644 (file)
@@ -2,12 +2,11 @@
 package "clitk"
 version "Try to convert a DICOM into an image (.hdr, .vox...)"
 
-option "config"                 -      "Config file"            string         no
-option "verbose"        v "Verbose"                     flag off
-option "verboseSliceLocation"   - "Verbose slices locations"            flag off
-option "name"           n "Display filename"    flag off
-option "tolerance"      t "Tolerance for slice position"        double default="0" no
-option "output"      o "Output image filename"         string  yes
-option "std_input"   - "Take the like of input file from stdin, to support huge lists of filenames" flag off
-option "focal_origin" - "Output files with FOCAL-like origin, instead of the origin present in the dicom files" flag off
+option "config"                    -   "Config file"            string         no
+option "verbose"             v "Verbose"                        flag off
+option "tolerance"         t "Tolerance for slice position"     double default="0" no
+option "output"         o "Output image filename"              string  yes
+option "std_input"      - "Take the like of input file from stdin, to support huge lists of filenames" flag off
+option "focal_origin"   - "Output files with FOCAL-like origin, instead of the origin present in the dicom files" flag off
 option "extract_series" s "Identify different series in the file list and create the MHDs accordinly" flag off
+option "patientSystem"  p "Open the image with patient coordinate system" flag off
index 302f65ced9ed72ec44964b0650b4e5b01c480832..3790ab7b3100e335da027148e0d797d2ba20f59a 100644 (file)
@@ -40,6 +40,9 @@ int main(int argc, char * argv[]) {
   clitk::DicomRTStruct2ImageFilter filter;
   filter.SetCropMaskEnabled(args_info.crop_flag);
   filter.SetImageFilename(args_info.image_arg);  // Used to get spacing + origin
+  if (args_info.vtk_flag) {
+    filter.SetWriteMesh(true);
+  }
   if (args_info.roiName_given) {
     filter.SetROI(s->GetROIFromROIName(args_info.roiName_arg)); 
     filter.SetOutputImageFilename(args_info.output_arg);
@@ -67,6 +70,9 @@ int main(int argc, char * argv[]) {
         filter.SetROI(roi); 
         filter.SetCropMaskEnabled(args_info.crop_flag);
         filter.SetImageFilename(args_info.image_arg);  // Used to get spacing + origin
+        if (args_info.vtk_flag) {
+          filter.SetWriteMesh(true);
+        }
         name.erase(remove_if(name.begin(), name.end(), isspace), name.end());
         std::string n;
         if (args_info.mha_flag) {
index 980f8a92bcba6322586a158c0f4a8da3d5d695cc..8e6159a16ff837ef4148d14193d27017db2abe4a 100644 (file)
@@ -1,24 +1,21 @@
 # file clitkDicomRTStruct2BinaryImage.ggo
 package "clitk"
-version "Convert DICOM RT Structure Set (contours) to binary image"
+version "1.0"
+description "Convert DICOM RT Structure Set (contours) to binary image"
 
-option "config"                 - "Config file"                     string     no
-option "verbose"         v "Verbose"                        flag       off
-option "verboseFile"     - "Verbose file content"            flag      off
-option "input"          i "Input Dicom file"                string     yes
-option "image"          j "Used to read image info (spacing, origin)"    string        yes
-option "output"                 o "Output image base filename (roi number and extension will be append)"        string yes
+option "config"       - "Config file"                                                           string  no
+option "verbose"      v "Verbose"                                                               flag    off
+option "verboseFile"  - "Verbose file content"                                                  flag    off
+option "input"        i "Input Dicom file"                                                      string  yes
+option "image"        j "Used to read image info (spacing, origin)"                             string  yes
+option "output"       o "Output image base filename (roi number and extension will be append)"  string  yes
 
 defgroup "ROIoption" groupdesc="an option of this group is required" 
-groupoption "roi"     r "ROI to binarize (if -1 = all roi)"            int    no default="-1" group="ROIoption"
-groupoption "roiName"     n "ROI name to binarize (be wary of spaces in ROI names; if blank, use given 'roi' value)"            string    no default="" group="ROIoption"
-groupoption "roiNameSubstr"     s "Substring of ROI name to binarize (reuturns all matches; if blank, use given 'roiName' value)"            string    no default="" group="ROIoption"
+groupoption "roi"           r "ROI to binarize (if -1 = all roi)"                                                             int     no default="-1" group="ROIoption"
+groupoption "roiName"       n "ROI name to binarize (be wary of spaces in ROI names; if blank, use given 'roi' value)"        string  no default=""   group="ROIoption"
+groupoption "roiNameSubstr" s "Substring of ROI name to binarize (reuturns all matches; if blank, use given 'roiName' value)" string  no default=""   group="ROIoption"
 
-option "crop"           c "Crop binary mask"            flag off
-
-option "mha"  - "Write the RTStruct as a mha image to avoid special character problems"                 flag off
-
-#option "roi"           r "ROI to print (ID)"           int            no
-#option "contour"       c "contour to print (ID)"       int            no
-#option "offset"                o "to display points as image offsets" flag    off
+option "crop"         c "Crop binary mask"                                                      flag off
+option "mha"          - "Write the RTStruct as a mha image to avoid special character problems" flag off
+option "vtk"          - "Write the vtk Mesh as a vtk file"                                      flag off
 
diff --git a/tools/clitkGateSimulation2Dicom.cxx b/tools/clitkGateSimulation2Dicom.cxx
new file mode 100644 (file)
index 0000000..bf9cc01
--- /dev/null
@@ -0,0 +1,53 @@
+/*=========================================================================
+  Program:   vv                     http://www.creatis.insa-lyon.fr/rio/vv
+
+  Authors belong to:
+  - University of LYON              http://www.universite-lyon.fr/
+  - Léon Bérard cancer center       http://www.centreleonberard.fr
+  - CREATIS CNRS laboratory         http://www.creatis.insa-lyon.fr
+
+  This software is distributed WITHOUT ANY WARRANTY; without even
+  the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+  PURPOSE.  See the copyright notices for more information.
+
+  It is distributed under dual licence
+
+  - BSD        See included LICENSE.txt file
+  - CeCILL-B   http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.html
+===========================================================================**/
+
+/* =================================================
+ * @file   clitkGateSimulation2Dicom.cxx
+ * @author Jef Vandemeulebroucke
+ * @date   4th of August
+ *
+ * @brief Write a volume into a dicom with the header of another dicom
+ *
+ ===================================================*/
+
+
+// clitk
+#include "clitkGateSimulation2Dicom_ggo.h"
+#include "clitkIO.h"
+#include "clitkGateSimulation2DicomGenericFilter.h"
+#include "clitkCommon.h"
+
+//--------------------------------------------------------------------
+int main(int argc, char * argv[])
+{
+
+  // Init command line
+  GGO(clitkGateSimulation2Dicom, args_info);
+  CLITK_INIT;
+
+  // Filter
+  typedef clitk::GateSimulation2DicomGenericFilter<args_info_clitkGateSimulation2Dicom> FilterType;
+  FilterType::Pointer genericFilter = FilterType::New();
+
+  genericFilter->SetArgsInfo(args_info);
+  genericFilter->Update();
+
+  return EXIT_SUCCESS;
+}// end main
+
+//--------------------------------------------------------------------
diff --git a/tools/clitkGateSimulation2Dicom.ggo b/tools/clitkGateSimulation2Dicom.ggo
new file mode 100644 (file)
index 0000000..54fa600
--- /dev/null
@@ -0,0 +1,11 @@
+#File clitkGateSimulation2Dicom.ggo
+package "clitkGateSimulation2Dicom"
+version "1.0"
+purpose ""
+
+option "config"                -       "Config file"                     string        no
+option "verbose"       v       "Verbose"                         flag          off
+
+option "input"         i       "Input image filename"            string        yes
+option "inputModelFilename"    d       "Input dicom model"               string        yes
+option "outputFilename"        o       "Output dicom directory/filename"         string        yes 
diff --git a/tools/clitkGateSimulation2DicomGenericFilter.cxx b/tools/clitkGateSimulation2DicomGenericFilter.cxx
new file mode 100644 (file)
index 0000000..e7cdd2f
--- /dev/null
@@ -0,0 +1,39 @@
+/*=========================================================================
+  Program:   vv                     http://www.creatis.insa-lyon.fr/rio/vv
+
+  Authors belong to:
+  - University of LYON              http://www.universite-lyon.fr/
+  - Léon Bérard cancer center       http://www.centreleonberard.fr
+  - CREATIS CNRS laboratory         http://www.creatis.insa-lyon.fr
+
+  This software is distributed WITHOUT ANY WARRANTY; without even
+  the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+  PURPOSE.  See the copyright notices for more information.
+
+  It is distributed under dual licence
+
+  - BSD        See included LICENSE.txt file
+  - CeCILL-B   http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.html
+===========================================================================**/
+#ifndef clitkGateSimulation2DicomGenericFilter_cxx
+#define clitkGateSimulation2DicomGenericFilter_cxx
+
+/* =================================================
+ * @file   clitkGateSimulation2DicomGenericFilter.cxx
+ * @author
+ * @date
+ *
+ * @brief
+ *
+ ===================================================*/
+
+#include "clitkGateSimulation2DicomGenericFilter.h"
+
+
+namespace clitk
+{
+
+
+} //end clitk
+
+#endif  //#define clitkGateSimulation2DicomGenericFilter_cxx
diff --git a/tools/clitkGateSimulation2DicomGenericFilter.h b/tools/clitkGateSimulation2DicomGenericFilter.h
new file mode 100644 (file)
index 0000000..7dbc725
--- /dev/null
@@ -0,0 +1,128 @@
+/*=========================================================================
+  Program:   vv                     http://www.creatis.insa-lyon.fr/rio/vv
+
+  Authors belong to: 
+  - University of LYON              http://www.universite-lyon.fr/
+  - Léon Bérard cancer center       http://www.centreleonberard.fr
+  - CREATIS CNRS laboratory         http://www.creatis.insa-lyon.fr
+
+  This software is distributed WITHOUT ANY WARRANTY; without even
+  the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+  PURPOSE.  See the copyright notices for more information.
+
+  It is distributed under dual licence
+
+  - BSD        See included LICENSE.txt file
+  - CeCILL-B   http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.html
+===========================================================================**/
+#ifndef clitkGateSimulation2DicomGenericFilter_h
+#define clitkGateSimulation2DicomGenericFilter_h
+
+/* =================================================
+ * @file   clitkGateSimulation2DicomGenericFilter.h
+ * @author 
+ * @date   
+ * 
+ * @brief 
+ * 
+ ===================================================*/
+
+
+// clitk include
+#include "clitkIO.h"
+#include "clitkImageCommon.h"
+#include "clitkGateSimulation2Dicom_ggo.h"
+
+//itk include
+#include "itkLightObject.h"
+#include "itkGDCMImageIO.h"
+#include "itkMetaDataDictionary.h"
+#include "itkGDCMSeriesFileNames.h"
+#include "itkImageSeriesReader.h"
+#include "itkImageSeriesWriter.h"
+#include "itkImageFileReader.h"
+#include "itkImageFileWriter.h"
+#include <vector>
+#include <itksys/SystemTools.hxx>
+
+namespace clitk 
+{
+  template<class args_info_type>
+  class ITK_EXPORT GateSimulation2DicomGenericFilter : public itk::LightObject
+  {
+  public:
+    //----------------------------------------
+    // ITK
+    //----------------------------------------
+    typedef GateSimulation2DicomGenericFilter   Self;
+    typedef itk::LightObject                          Superclass;
+    typedef itk::SmartPointer<Self>                   Pointer;
+    typedef itk::SmartPointer<const Self>             ConstPointer;
+   
+    // Method for creation through the object factory
+    itkNewMacro(Self);  
+
+    // Run-time type information (and related methods)
+    itkTypeMacro( GateSimulation2DicomGenericFilter, LightObject );
+
+
+    //----------------------------------------
+    // Typedefs
+    //----------------------------------------
+
+
+    //----------------------------------------
+    // Set & Get
+    //----------------------------------------    
+    void SetArgsInfo(const args_info_type & a)
+    {
+      m_ArgsInfo=a;
+      m_Verbose=m_ArgsInfo.verbose_flag;
+      m_InputFileName=m_ArgsInfo.input_arg;
+    }
+    
+    
+    //----------------------------------------  
+    // Update
+    //----------------------------------------  
+    void Update();
+
+  protected:
+
+    //----------------------------------------  
+    // Constructor & Destructor
+    //----------------------------------------  
+    GateSimulation2DicomGenericFilter();
+    ~GateSimulation2DicomGenericFilter() {};
+
+    
+    //----------------------------------------  
+    // Templated members
+    //----------------------------------------  
+    template <unsigned int Dimension>  void UpdateWithDim(std::string PixelType);
+    template <unsigned int Dimension, class PixelType>  void UpdateWithDimAndPixelType();
+
+
+    //----------------------------------------  
+    // Data members
+    //----------------------------------------
+    args_info_type m_ArgsInfo;
+    bool m_Verbose;
+    std::string m_InputFileName;
+
+
+  };
+
+//Copy dicom dictionary
+void CopyDictionary (itk::MetaDataDictionary &fromDict, itk::MetaDataDictionary &toDict);
+
+//convert to std::string
+template <typename T> std::string NumberToString ( T Number );
+
+} // end namespace clitk
+
+#ifndef ITK_MANUAL_INSTANTIATION
+#include "clitkGateSimulation2DicomGenericFilter.txx"
+#endif
+
+#endif // #define clitkGateSimulation2DicomGenericFilter_h
diff --git a/tools/clitkGateSimulation2DicomGenericFilter.txx b/tools/clitkGateSimulation2DicomGenericFilter.txx
new file mode 100644 (file)
index 0000000..fe849b3
--- /dev/null
@@ -0,0 +1,426 @@
+/*=========================================================================
+  Program:   vv                     http://www.creatis.insa-lyon.fr/rio/vv
+
+  Authors belong to:
+  - University of LYON              http://www.universite-lyon.fr/
+  - Léon Bérard cancer center       http://www.centreleonberard.fr
+  - CREATIS CNRS laboratory         http://www.creatis.insa-lyon.fr
+
+  This software is distributed WITHOUT ANY WARRANTY; without even
+  the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+  PURPOSE.  See the copyright notices for more information.
+
+  It is distributed under dual licence
+
+  - BSD        See included LICENSE.txt file
+  - CeCILL-B   http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.html
+===========================================================================**/
+#ifndef clitkGateSimulation2DicomGenericFilter_txx
+#define clitkGateSimulation2DicomGenericFilter_txx
+
+/* =================================================
+ * @file   clitkGateSimulation2DicomGenericFilter.txx
+ * @author
+ * @date
+ *
+ * @brief
+ *
+ ===================================================*/
+
+#include <sstream>
+// clitk
+#include "clitkResampleImageWithOptionsFilter.h"
+#if GDCM_MAJOR_VERSION >= 2
+#include "gdcmUIDGenerator.h"
+#include <gdcmImageHelper.h>
+#include <gdcmAttribute.h>
+#include <gdcmReader.h>
+#include <gdcmWriter.h>
+#else
+#include "gdcmFile.h"
+#include "gdcmUtil.h"
+#endif
+
+#include "itkImageRegionIterator.h"
+#include "itkMetaImageIO.h"
+#include "itkMetaDataDictionary.h"
+
+
+namespace clitk
+{
+
+
+//-----------------------------------------------------------
+// Constructor
+//-----------------------------------------------------------
+template<class args_info_type>
+GateSimulation2DicomGenericFilter<args_info_type>::GateSimulation2DicomGenericFilter()
+{
+  m_Verbose=false;
+  m_InputFileName="";
+}
+
+
+//-----------------------------------------------------------
+// Update
+//-----------------------------------------------------------
+template<class args_info_type>
+void GateSimulation2DicomGenericFilter<args_info_type>::Update()
+{
+  // Read the Dimension and PixelType
+  int Dimension;
+  std::string PixelType;
+  ReadImageDimensionAndPixelType(m_InputFileName, Dimension, PixelType);
+
+
+  // Call UpdateWithDim
+  if(Dimension==2) UpdateWithDim<2>(PixelType);
+  else if(Dimension==3) UpdateWithDim<3>(PixelType);
+  // else if (Dimension==4)UpdateWithDim<4>(PixelType);
+  else {
+    std::cout<<"Error, Only for 2 or 3  Dimensions!!!"<<std::endl ;
+    return;
+  }
+}
+
+//-------------------------------------------------------------------
+// Update with the number of dimensions
+//-------------------------------------------------------------------
+template<class args_info_type>
+template<unsigned int Dimension>
+void
+GateSimulation2DicomGenericFilter<args_info_type>::UpdateWithDim(std::string PixelType)
+{
+  if (m_Verbose) std::cout << "Image was detected to be "<<Dimension<<"D and "<< PixelType<<"..."<<std::endl;
+
+  if(PixelType == "short") {
+    if (m_Verbose) std::cout << "Launching filter in "<< Dimension <<"D and signed short..." << std::endl;
+    UpdateWithDimAndPixelType<Dimension, signed short>();
+  }
+  else if(PixelType == "unsigned_short"){
+    if (m_Verbose) std::cout  << "Launching filter in "<< Dimension <<"D and unsigned_short..." << std::endl;
+    UpdateWithDimAndPixelType<Dimension, unsigned short>();
+  }
+
+  else if (PixelType == "unsigned_char") {
+    if (m_Verbose) std::cout  << "Launching filter in "<< Dimension <<"D and unsigned_char..." << std::endl;
+    UpdateWithDimAndPixelType<Dimension, unsigned char>();
+  }
+
+  //     else if (PixelType == "char"){
+  //       if (m_Verbose) std::cout  << "Launching filter in "<< Dimension <<"D and signed_char..." << std::endl;
+  //       UpdateWithDimAndPixelType<Dimension, signed char>();
+  //     }
+  else if (PixelType == "double") {
+    if (m_Verbose) std::cout  << "Launching filter in "<< Dimension <<"D and double..." << std::endl;
+    UpdateWithDimAndPixelType<Dimension, double>();
+  }
+  else {
+    if (m_Verbose) std::cout  << "Launching filter in "<< Dimension <<"D and float..." << std::endl;
+    UpdateWithDimAndPixelType<Dimension, float>();
+  }
+}
+
+//-------------------------------------------------------------------
+// Update with the number of dimensions and the pixeltype read from
+// the dicom files. The MHD files may be resampled to match the
+// dicom spacing (and number of slices). Rounding errors in resampling
+// are handled by removing files when generating the output dicom
+// series.
+//-------------------------------------------------------------------
+template<class args_info_type>
+template <unsigned int Dimension, class  PixelType>
+void
+GateSimulation2DicomGenericFilter<args_info_type>::UpdateWithDimAndPixelType()
+{
+
+#if GDCM_MAJOR_VERSION == 2
+  // ImageTypes
+  typedef itk::Image<PixelType, Dimension> InputImageType;
+  typedef itk::Image<PixelType, Dimension> OutputImageType;
+  typedef itk::ImageFileReader< InputImageType >     ReaderType;
+  typedef itk::ImageSeriesReader< InputImageType >     ReaderSeriesType;
+  typedef itk::GDCMImageIO ImageIOType;
+
+
+  // Read Dicom model file
+  typename ReaderSeriesType::Pointer readerSeries = ReaderSeriesType::New();
+  ImageIOType::Pointer gdcmIO = ImageIOType::New();
+  std::string filename_out = m_ArgsInfo.outputFilename_arg;
+  gdcmIO->LoadPrivateTagsOn();
+  gdcmIO->KeepOriginalUIDOn();
+  typename ReaderSeriesType::FileNamesContainer fileNames;
+  fileNames.push_back(m_ArgsInfo.inputModelFilename_arg);
+  readerSeries->SetImageIO( gdcmIO );
+  readerSeries->SetFileNames( fileNames );
+  try {
+    readerSeries->Update();
+  } catch (itk::ExceptionObject &excp) {
+    std::cerr << "Error: Exception thrown while reading the DICOM model file !!" << std::endl;
+    std::cerr << excp << std::endl;
+  }
+
+
+  // Read the input (MHD file)
+  typedef typename InputImageType::RegionType RegionType;
+  typedef typename RegionType::SizeType SizeType;
+  typedef itk::ImageFileReader<InputImageType> InputReaderType;
+  typename InputReaderType::Pointer volumeReader = InputReaderType::New();
+  volumeReader->SetFileName( m_InputFileName);
+  volumeReader->Update();
+  typename InputImageType::Pointer input = volumeReader->GetOutput();
+
+
+  //Read the metadata informations in the mhd
+  MetaObject tObj(3);
+  tObj.AddUserField("NumberOfEnergyWindows", MET_STRING);
+  tObj.AddUserField("NbOfHeads", MET_STRING);
+  tObj.AddUserField("NbOfProjections", MET_STRING);
+  tObj.Read(m_InputFileName.c_str());
+  char *p_energyNumber, *p_headNumber, *p_rotationNumber;
+  int energyNumber, headNumber, rotationNumber;
+  p_energyNumber = static_cast<char*>(tObj.GetUserField("NumberOfEnergyWindows"));
+  p_headNumber = static_cast<char*>(tObj.GetUserField("NbOfHeads"));
+  p_rotationNumber = static_cast<char*>(tObj.GetUserField("NbOfProjections"));
+  energyNumber = atoi(p_energyNumber);
+  headNumber = atoi(p_headNumber);
+  rotationNumber = atoi(p_rotationNumber);
+  for (unsigned int i=0; i<energyNumber; ++i) {
+    std::string tagEnergyName("EnergyWindow_");
+    tagEnergyName += NumberToString(i).c_str();
+    std::string tagEnergyThreshold(tagEnergyName), tagEnergyUphold(tagEnergyName);
+    tagEnergyThreshold += "_threshold";
+    tagEnergyUphold += "_uphold";
+    tObj.AddUserField(tagEnergyName.c_str(), MET_STRING);
+    tObj.AddUserField(tagEnergyThreshold.c_str(), MET_STRING);
+    tObj.AddUserField(tagEnergyUphold.c_str(), MET_STRING);
+  }
+  tObj.Read(m_InputFileName.c_str());
+  std::vector<char*> p_EnergyWindowName(energyNumber), p_EnergyWindowThreshold(energyNumber), p_EnergyWindowUphold(energyNumber);
+  std::vector<int> energyWindowThreshold(energyNumber), energyWindowUphold(energyNumber);
+  for (unsigned int i=0; i<energyNumber; ++i) {
+    std::string tagEnergyName("EnergyWindow_");
+    tagEnergyName += NumberToString(i).c_str();
+    std::string tagEnergyThreshold(tagEnergyName), tagEnergyUphold(tagEnergyName);
+    tagEnergyThreshold += "_threshold";
+    tagEnergyUphold += "_uphold";
+    p_EnergyWindowName[i] = static_cast<char*>(tObj.GetUserField(tagEnergyName.c_str()));
+    p_EnergyWindowThreshold[i] = static_cast<char*>(tObj.GetUserField(tagEnergyThreshold.c_str()));
+    p_EnergyWindowUphold[i] = static_cast<char*>(tObj.GetUserField(tagEnergyUphold.c_str()));
+    energyWindowThreshold[i] = atoi(p_EnergyWindowThreshold[i]);
+    energyWindowUphold[i] = atoi(p_EnergyWindowUphold[i]);
+  }
+
+//TODO if the input size/spacing and dicom model ones are different
+
+  // Create a new mhd image with the correct dicom order slices
+  typename InputImageType::Pointer mhdCorrectOrder = InputImageType::New();
+  mhdCorrectOrder->SetRegions(input->GetLargestPossibleRegion());
+  mhdCorrectOrder->Allocate();
+  unsigned int zAxis(0); //z value for the input mhd image
+  for (unsigned int energy = 0; energy < energyNumber; ++energy) {
+    for (unsigned int head = 0; head < headNumber; ++head) {
+      for (unsigned int rotation = 0; rotation < rotationNumber; ++rotation) {
+        std::cout << "Energy " << energy << " Head " << head << " Rotation " << rotation << std::endl;
+
+        typename InputImageType::IndexType startIteratorIndexCorrectOrder; //pixel index of mhdCorrectOrder
+        startIteratorIndexCorrectOrder[0] = 0;
+        startIteratorIndexCorrectOrder[1] = 0;
+        startIteratorIndexCorrectOrder[2] = rotation + head*rotationNumber + energy*headNumber;
+
+        typename InputImageType::IndexType startIteratorIndexOriginalOrder; //pixel index of input mhd
+        startIteratorIndexOriginalOrder[0] = 0;
+        startIteratorIndexOriginalOrder[1] = 0;
+        startIteratorIndexOriginalOrder[2] = head + energy*headNumber + rotation*energyNumber;
+
+        typename InputImageType::SizeType regionSizeIterator;
+        regionSizeIterator[0] = input->GetLargestPossibleRegion().GetSize()[0];
+        regionSizeIterator[1] = input->GetLargestPossibleRegion().GetSize()[1];
+        regionSizeIterator[2] = 1;
+
+        typename InputImageType::RegionType regionIteratorCorrectOrder;
+        regionIteratorCorrectOrder.SetSize(regionSizeIterator);
+        regionIteratorCorrectOrder.SetIndex(startIteratorIndexCorrectOrder);
+
+        typename InputImageType::RegionType regionIteratorOriginalOrder;
+        regionIteratorOriginalOrder.SetSize(regionSizeIterator);
+        regionIteratorOriginalOrder.SetIndex(startIteratorIndexOriginalOrder);
+
+        itk::ImageRegionIterator<InputImageType> CorrectOrderIterator(mhdCorrectOrder,regionIteratorCorrectOrder);
+        itk::ImageRegionIterator<InputImageType> OriginalOrderIterator(input,regionIteratorOriginalOrder);
+        while(!CorrectOrderIterator.IsAtEnd()) {
+          CorrectOrderIterator.Set(OriginalOrderIterator.Get());
+          ++CorrectOrderIterator;
+          ++OriginalOrderIterator;
+        }
+
+        ++zAxis;
+      }
+    }
+  }
+
+
+  // update output dicom keys/tags
+  // string for distinguishing items inside sequence:
+  const std::string ITEM_ENCAPSULATE_STRING("DICOM_ITEM_ENCAPSULATE");
+  std::string tempString = ITEM_ENCAPSULATE_STRING + "01";
+  typename ReaderSeriesType::DictionaryRawPointer inputDict = (*(readerSeries->GetMetaDataDictionaryArray()))[0];
+  typename ReaderSeriesType::DictionaryArrayType outputArray;
+  typename ReaderSeriesType::DictionaryRawPointer outputDict = new typename ReaderSeriesType::DictionaryType;
+  CopyDictionary (*inputDict, *outputDict);
+  itk::EncapsulateMetaData<std::string>(*outputDict, "0054|0011", NumberToString(energyNumber));
+  itk::EncapsulateMetaData<std::string>(*outputDict, "0054|0021", NumberToString(headNumber));
+  outputArray.push_back(outputDict);
+
+
+  // Output directory and filenames
+  typedef itk::ImageSeriesWriter<OutputImageType, OutputImageType>  WriterSerieType;
+  typename WriterSerieType::Pointer writerSerie = WriterSerieType::New();
+  writerSerie->SetInput( mhdCorrectOrder );
+  writerSerie->SetImageIO( gdcmIO );
+  typename ReaderSeriesType::FileNamesContainer fileNamesOutput;
+  fileNamesOutput.push_back(filename_out);
+  writerSerie->SetFileNames( fileNamesOutput );
+  writerSerie->SetMetaDataDictionaryArray(&outputArray);
+
+
+  // Write
+  try {
+    if (m_ArgsInfo.verbose_flag)
+      std::cout << writerSerie << std::endl;
+    writerSerie->Update();
+  } catch( itk::ExceptionObject & excp ) {
+    std::cerr << "Error: Exception thrown while writing the series!!" << std::endl;
+    std::cerr << excp << std::endl;
+  }
+
+
+  //Write sequence dicom tag with gdcm
+  gdcm::Reader reader;
+  reader.SetFileName( fileNamesOutput[0].c_str() );
+  reader.Read();
+  gdcm::File &file = reader.GetFile();
+  gdcm::DataSet &ds2 = file.GetDataSet();
+  const unsigned int ptr_len = 42;
+  char *ptr = new char[ptr_len];
+  memset(ptr,0,ptr_len);
+
+  //Write rotation tag
+  // Create a Sequence
+  gdcm::SmartPointer<gdcm::SequenceOfItems> rotationSq = new gdcm::SequenceOfItems();
+  rotationSq->SetLengthToUndefined();
+  // Create a dataelement
+  gdcm::DataElement rotationDE( gdcm::Tag(0x54, 0x53) );
+  rotationDE.SetVR( gdcm::VR::US );
+  char essai = (char)rotationNumber;
+  char *p_essai = &essai;
+  rotationDE.SetByteValue(p_essai, 1);
+  // Create an item
+  gdcm::Item rotationIt;
+  rotationIt.SetVLToUndefined();
+  gdcm::DataSet &rotationDS = rotationIt.GetNestedDataSet();
+  rotationDS.Insert(rotationDE);
+  rotationSq->AddItem(rotationIt);
+  // Insert sequence into data set
+  gdcm::DataElement rotationDEParent( gdcm::Tag(0x54, 0x52) );
+  rotationDEParent.SetVR(gdcm::VR::SQ);
+  rotationDEParent.SetValue(*rotationSq);
+  rotationDEParent.SetVLToUndefined();
+
+  //Write energy
+  gdcm::DataElement energyDEParent( gdcm::Tag(0x54, 0x12) );
+  energyDEParent.SetVR(gdcm::VR::SQ);
+  // Create a Sequence
+  gdcm::SmartPointer<gdcm::SequenceOfItems> energySq = new gdcm::SequenceOfItems();
+  energySq->SetLengthToUndefined();
+  for (unsigned int i=0; i<energyNumber; ++i) {
+    gdcm::SmartPointer<gdcm::SequenceOfItems> energyThresholdSq = new gdcm::SequenceOfItems();
+    energyThresholdSq->SetLengthToUndefined();
+    // Create a dataelement
+    gdcm::DataElement energyThresholdDE( gdcm::Tag(0x54, 0x14) );
+    gdcm::DataElement energyUpholdDE( gdcm::Tag(0x54, 0x15) );
+    energyThresholdDE.SetVR( gdcm::VR::DS );
+    energyUpholdDE.SetVR( gdcm::VR::DS );
+    energyThresholdDE.SetByteValue(p_EnergyWindowThreshold[i], (uint32_t)strlen(p_EnergyWindowThreshold[i]));
+    energyUpholdDE.SetByteValue(p_EnergyWindowUphold[i], (uint32_t)strlen(p_EnergyWindowUphold[i]));
+    // Create an item
+    gdcm::Item energyThresholdIt;
+    energyThresholdIt.SetVLToUndefined();
+    gdcm::DataSet &energyThresholdDS = energyThresholdIt.GetNestedDataSet();
+    energyThresholdDS.Insert(energyThresholdDE);
+    energyThresholdDS.Insert(energyUpholdDE);
+    energyThresholdSq->AddItem(energyThresholdIt);
+    // Insert sequence into data set
+    gdcm::DataElement energyThresholdDEParent( gdcm::Tag(0x54, 0x13) );
+    energyThresholdDEParent.SetVR(gdcm::VR::SQ);
+    energyThresholdDEParent.SetValue(*energyThresholdSq);
+    energyThresholdDEParent.SetVLToUndefined();
+    // Create a dataelement
+    gdcm::DataElement energyNameDE( gdcm::Tag(0x54, 0x18) );
+    energyNameDE.SetVR( gdcm::VR::SH );
+    energyNameDE.SetByteValue(p_EnergyWindowName[i], (uint32_t)strlen(p_EnergyWindowName[i]));
+    // Create an item
+    gdcm::Item energyIt;
+    energyIt.SetVLToUndefined();
+    gdcm::DataSet &energyDS = energyIt.GetNestedDataSet();
+    energyDS.Insert(energyNameDE);
+    energyDS.Insert(energyThresholdDEParent);
+    energySq->AddItem(energyIt);
+  }
+  // Insert sequence into data set
+  energyDEParent.SetValue(*energySq);
+  energyDEParent.SetVLToUndefined();
+  ds2.Insert(energyDEParent);
+  ds2.Insert(rotationDEParent);
+
+  gdcm::Writer w;
+  w.SetFile( file );
+  w.SetFileName( fileNamesOutput[0].c_str() );
+  w.Write();
+
+#else
+  std::cout << "Use GDCM2" << std::endl;
+#endif
+}
+//---------------------------------------------------------------------------
+
+
+//---------------------------------------------------------------------------
+void CopyDictionary (itk::MetaDataDictionary &fromDict, itk::MetaDataDictionary &toDict)
+{
+  typedef itk::MetaDataDictionary DictionaryType;
+
+  DictionaryType::ConstIterator itr = fromDict.Begin();
+  DictionaryType::ConstIterator end = fromDict.End();
+  typedef itk::MetaDataObject< std::string > MetaDataStringType;
+
+  while( itr != end )
+    {
+    itk::MetaDataObjectBase::Pointer  entry = itr->second;
+
+    MetaDataStringType::Pointer entryvalue =
+      dynamic_cast<MetaDataStringType *>( entry.GetPointer() ) ;
+    if( entryvalue )
+      {
+      std::string tagkey   = itr->first;
+      std::string tagvalue = entryvalue->GetMetaDataObjectValue();
+      itk::EncapsulateMetaData<std::string>(toDict, tagkey, tagvalue);
+      }
+    ++itr;
+    }
+}
+//---------------------------------------------------------------------------
+
+
+//---------------------------------------------------------------------------
+template <typename T> std::string NumberToString ( T Number )
+{
+   std::ostringstream ss;
+   ss << Number;
+   return ss.str();
+}
+//---------------------------------------------------------------------------
+
+}//end clitk
+
+#endif //#define clitkGateSimulation2DicomGenericFilter_txx
index 0cb5ac9129c707097d2428cf8cda178b3d49343b..d853abf77d0123a4ef4b54fa43142036c2ed8d6f 100644 (file)
@@ -35,6 +35,7 @@ int main(int argc, char * argv[])
   filter->SetArgsInfo(args_info);
 
   CLITK_TRY_CATCH_EXIT(filter->Update());
+  filter->SaveAs();
 
   return EXIT_SUCCESS;
 } // This is the end, my friend
index 49e3fa0b687d0bbc454d1a4adab06bac0f08ea4e..f93f213138cd74a3533bd3a5addca55242d8e79a 100644 (file)
@@ -130,7 +130,10 @@ HistogramImageGenericFilter::UpdateWithInputImageType()
   //compute bin number
   typedef typename HistogramFilterType::HistogramSizeType SizeType;
   SizeType binNumber(1);
-  mBinSize = std::log(range);
+  if (!mArgsInfo.size_given)
+    mBinSize = std::log(range);
+  if (mBinSize == 0)
+    mBinSize = 1;
   binNumber[0] = (int)(range/mBinSize);
   if (binNumber[0] == 0)
     binNumber[0] = 1;
@@ -159,6 +162,31 @@ HistogramImageGenericFilter::UpdateWithInputImageType()
 }
 //--------------------------------------------------------------------
 
+//------------------------------------------------------------------------------
+void HistogramImageGenericFilter::SaveAs()
+{
+  // Output
+  std::string textFileName = GetOutputFilename();
+  ofstream fileOpen(textFileName.c_str(), std::ofstream::trunc);
+
+  if(!fileOpen) {
+      cerr << "Error during saving" << endl;
+      return;
+  }
+
+  int i(0);
+  fileOpen << "Value represents the number of voxels around the corresponding intensity (by default the windows size around intensity is log(range))" << endl;
+  fileOpen << "Intensity" << "\t" << "Value" << endl;
+
+  while (i<mArrayX->GetNumberOfTuples()) {
+      fileOpen << mArrayX->GetTuple(i)[0] << "\t" << mArrayY->GetTuple(i)[0] << endl;
+      ++i;
+  }
+
+  fileOpen.close();
+}
+//------------------------------------------------------------------------------
+
 
 }//end clitk
 
index d963fbc2954761863601aa4bbdb346fca7ef2928..999eda924109c72d8ea2f13c1f0edde6fd029387 100644 (file)
@@ -52,6 +52,9 @@ namespace clitk
     void SetArgsInfo(const args_info_type & a);
     void SetSizeBin (const double size);
 
+    // Save the histogram to txt file
+    void SaveAs();
+
     //--------------------------------------------------------------------
     // Main function called each time the filter is updated
     template<class InputImageType>  
diff --git a/tools/clitkImage2DicomDose.cxx b/tools/clitkImage2DicomDose.cxx
new file mode 100644 (file)
index 0000000..6347489
--- /dev/null
@@ -0,0 +1,53 @@
+/*=========================================================================
+  Program:   vv                     http://www.creatis.insa-lyon.fr/rio/vv
+
+  Authors belong to:
+  - University of LYON              http://www.universite-lyon.fr/
+  - Léon Bérard cancer center       http://www.centreleonberard.fr
+  - CREATIS CNRS laboratory         http://www.creatis.insa-lyon.fr
+
+  This software is distributed WITHOUT ANY WARRANTY; without even
+  the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+  PURPOSE.  See the copyright notices for more information.
+
+  It is distributed under dual licence
+
+  - BSD        See included LICENSE.txt file
+  - CeCILL-B   http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.html
+===========================================================================**/
+
+/* =================================================
+ * @file   clitkImage2DicomDose.cxx
+ * @author Jef Vandemeulebroucke
+ * @date   4th of August
+ *
+ * @brief Write a volume into a dicom with the header of another dicom
+ *
+ ===================================================*/
+
+
+// clitk
+#include "clitkImage2DicomDose_ggo.h"
+#include "clitkIO.h"
+#include "clitkImage2DicomDoseGenericFilter.h"
+#include "clitkCommon.h"
+
+//--------------------------------------------------------------------
+int main(int argc, char * argv[])
+{
+
+  // Init command line
+  GGO(clitkImage2DicomDose, args_info);
+  CLITK_INIT;
+
+  // Filter
+  typedef clitk::Image2DicomDoseGenericFilter<args_info_clitkImage2DicomDose> FilterType;
+  FilterType::Pointer genericFilter = FilterType::New();
+
+  genericFilter->SetArgsInfo(args_info);
+  genericFilter->Update();
+
+  return EXIT_SUCCESS;
+}// end main
+
+//--------------------------------------------------------------------
diff --git a/tools/clitkImage2DicomDose.ggo b/tools/clitkImage2DicomDose.ggo
new file mode 100644 (file)
index 0000000..04c7f56
--- /dev/null
@@ -0,0 +1,11 @@
+#File clitkImage2DicomDose.ggo
+package "clitkImage2DicomDose"
+version "1.0"
+purpose "Convert mhd file into Dicom RTDose fil using a model"
+
+option "config"         -         "Config file"                string  no
+option "input"          i    "inputfile name"             string  yes
+option "DicomInputFile" m    "dicom model name"           string  no
+option "output"         o    "output directory/filename"  string  yes
+option "verbose"        v    "Verbose"                    flag    off
+
diff --git a/tools/clitkImage2DicomDoseGenericFilter.cxx b/tools/clitkImage2DicomDoseGenericFilter.cxx
new file mode 100644 (file)
index 0000000..f7ce6b6
--- /dev/null
@@ -0,0 +1,39 @@
+/*=========================================================================
+  Program:   vv                     http://www.creatis.insa-lyon.fr/rio/vv
+
+  Authors belong to:
+  - University of LYON              http://www.universite-lyon.fr/
+  - Léon Bérard cancer center       http://www.centreleonberard.fr
+  - CREATIS CNRS laboratory         http://www.creatis.insa-lyon.fr
+
+  This software is distributed WITHOUT ANY WARRANTY; without even
+  the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+  PURPOSE.  See the copyright notices for more information.
+
+  It is distributed under dual licence
+
+  - BSD        See included LICENSE.txt file
+  - CeCILL-B   http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.html
+===========================================================================**/
+#ifndef clitkImage2DicomDoseGenericFilter_cxx
+#define clitkImage2DicomDoseGenericFilter_cxx
+
+/* =================================================
+ * @file   clitkImage2DicomDoseGenericFilter.cxx
+ * @author
+ * @date
+ *
+ * @brief
+ *
+ ===================================================*/
+
+#include "clitkImage2DicomDoseGenericFilter.h"
+
+
+namespace clitk
+{
+
+
+} //end clitk
+
+#endif  //#define clitkImage2DicomDoseGenericFilter_cxx
diff --git a/tools/clitkImage2DicomDoseGenericFilter.h b/tools/clitkImage2DicomDoseGenericFilter.h
new file mode 100644 (file)
index 0000000..f94d08a
--- /dev/null
@@ -0,0 +1,120 @@
+/*=========================================================================
+  Program:   vv                     http://www.creatis.insa-lyon.fr/rio/vv
+
+  Authors belong to: 
+  - University of LYON              http://www.universite-lyon.fr/
+  - Léon Bérard cancer center       http://www.centreleonberard.fr
+  - CREATIS CNRS laboratory         http://www.creatis.insa-lyon.fr
+
+  This software is distributed WITHOUT ANY WARRANTY; without even
+  the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+  PURPOSE.  See the copyright notices for more information.
+
+  It is distributed under dual licence
+
+  - BSD        See included LICENSE.txt file
+  - CeCILL-B   http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.html
+===========================================================================**/
+#ifndef clitkImage2DicomDoseGenericFilter_h
+#define clitkImage2DicomDoseGenericFilter_h
+
+/* =================================================
+ * @file   clitkImage2DicomDoseGenericFilter.h
+ * @author 
+ * @date   
+ * 
+ * @brief 
+ * 
+ ===================================================*/
+
+
+// clitk include
+#include "clitkIO.h"
+#include "clitkImageCommon.h"
+#include "clitkImage2DicomDose_ggo.h"
+
+//itk include
+#include "itkLightObject.h"
+#include "itkGDCMImageIO.h"
+#include "itkMetaDataDictionary.h"
+#include "itkGDCMSeriesFileNames.h"
+#include "itkImageSeriesReader.h"
+#include "itkImageSeriesWriter.h"
+#include "itkImageFileReader.h"
+#include "itkImageFileWriter.h"
+#include <vector>
+#include <itksys/SystemTools.hxx>
+
+namespace clitk 
+{
+  template<class args_info_type>
+  class ITK_EXPORT Image2DicomDoseGenericFilter : public itk::LightObject
+  {
+
+  public:
+    //----------------------------------------
+    // ITK
+    //----------------------------------------
+    typedef Image2DicomDoseGenericFilter   Self;
+    typedef itk::LightObject                          Superclass;
+    typedef itk::SmartPointer<Self>                   Pointer;
+    typedef itk::SmartPointer<const Self>             ConstPointer;
+   
+    // Method for creation through the object factory
+    itkNewMacro(Self);  
+
+    // Run-time type information (and related methods)
+    itkTypeMacro( Image2DicomDoseGenericFilter, LightObject );
+
+    //methods
+    //----------------------------------------
+    // Set & Get
+    //----------------------------------------    
+    void SetArgsInfo(const args_info_type & a)
+    {
+      m_ArgsInfo=a;
+      m_Verbose=m_ArgsInfo.verbose_flag;
+      m_InputFileName=m_ArgsInfo.input_arg;
+    }
+
+    //----------------------------------------  
+    // Update
+    //----------------------------------------  
+    void Update();
+
+  protected:
+    //----------------------------------------  
+    // Constructor & Destructor
+    //----------------------------------------  
+    Image2DicomDoseGenericFilter();
+    ~Image2DicomDoseGenericFilter() {};
+
+    //----------------------------------------  
+    // Templated members
+    //----------------------------------------  
+    template <unsigned int Dimension>  void UpdateWithDim(std::string PixelType);
+    template <unsigned int Dimension, class PixelType>  void UpdateWithDimAndPixelType();
+
+
+    gdcm::File * mDCMFile;
+
+    //----------------------------------------  
+    // Data members
+    //----------------------------------------
+    args_info_type m_ArgsInfo;
+    bool m_Verbose;
+    std::string m_InputFileName;
+
+  };
+
+  //Usefull functions
+  void CopyDictionary (itk::MetaDataDictionary &fromDict, itk::MetaDataDictionary &toDict);
+  template <typename T> std::string NumberToString ( T Number );
+
+} // end namespace clitk
+
+#ifndef ITK_MANUAL_INSTANTIATION
+#include "clitkImage2DicomDoseGenericFilter.txx"
+#endif
+
+#endif // #define clitkImage2DicomDoseGenericFilter_h
diff --git a/tools/clitkImage2DicomDoseGenericFilter.txx b/tools/clitkImage2DicomDoseGenericFilter.txx
new file mode 100644 (file)
index 0000000..1c4e88f
--- /dev/null
@@ -0,0 +1,484 @@
+/*=========================================================================
+  Program:   vv                     http://www.creatis.insa-lyon.fr/rio/vv
+
+  Authors belong to:
+  - University of LYON              http://www.universite-lyon.fr/
+  - Léon Bérard cancer center       http://www.centreleonberard.fr
+  - CREATIS CNRS laboratory         http://www.creatis.insa-lyon.fr
+
+  This software is distributed WITHOUT ANY WARRANTY; without even
+  the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+  PURPOSE.  See the copyright notices for more information.
+
+  It is distributed under dual licence
+
+  - BSD        See included LICENSE.txt file
+  - CeCILL-B   http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.html
+===========================================================================**/
+#ifndef clitkImage2DicomDoseGenericFilter_txx
+#define clitkImage2DicomDoseGenericFilter_txx
+
+/* =================================================
+ * @file   clitkImage2DicomDosemGenericFilter.txx
+ * @author
+ * @date
+ *
+ * @brief
+ *
+ ===================================================*/
+
+#include "math.h"
+
+#include "clitkImage2DicomDoseGenericFilter.h"
+#include "clitkCommon.h"
+#include "itkMinimumMaximumImageCalculator.h"
+#include "itkImageRegionIterator.h"
+#include "itkImageRegionConstIterator.h"
+#if GDCM_MAJOR_VERSION >= 2
+#include "gdcmUIDGenerator.h"
+#include <gdcmImageHelper.h>
+#include <gdcmAttribute.h>
+#include <gdcmReader.h>
+#include <gdcmWriter.h>
+#else
+#include "gdcmFile.h"
+#include "gdcmUtil.h"
+#endif
+
+//#include "gdcmBase.h"
+//#include "gdcmDocEntry.h"
+
+
+
+namespace clitk
+{
+
+
+//-----------------------------------------------------------
+// Constructor
+//-----------------------------------------------------------
+template<class args_info_type>
+Image2DicomDoseGenericFilter<args_info_type>::Image2DicomDoseGenericFilter()
+{
+  m_Verbose=false;
+  m_InputFileName="";
+}
+
+
+//-----------------------------------------------------------
+// Update
+//-----------------------------------------------------------
+template<class args_info_type>
+void Image2DicomDoseGenericFilter<args_info_type>::Update()
+{
+  // Read the Dimension and PixelType
+  int Dimension;
+  std::string PixelType;
+  ReadImageDimensionAndPixelType(m_InputFileName, Dimension, PixelType);
+
+
+  // Call UpdateWithDim
+  if(Dimension==2) UpdateWithDim<2>(PixelType);
+  else if(Dimension==3) UpdateWithDim<3>(PixelType);
+  // else if (Dimension==4)UpdateWithDim<4>(PixelType);
+  else {
+    std::cout<<"Error, Only for 2 or 3  Dimensions!!!"<<std::endl ;
+    return;
+  }
+}
+
+//-------------------------------------------------------------------
+// Update with the number of dimensions
+//-------------------------------------------------------------------
+template<class args_info_type>
+template<unsigned int Dimension>
+void
+Image2DicomDoseGenericFilter<args_info_type>::UpdateWithDim(std::string PixelType)
+{
+  if (m_Verbose) std::cout << "Image was detected to be "<<Dimension<<"D and "<< PixelType<<"..."<<std::endl;
+
+  if(PixelType == "short") {
+    if (m_Verbose) std::cout << "Launching filter in "<< Dimension <<"D and signed short..." << std::endl;
+    UpdateWithDimAndPixelType<Dimension, signed short>();
+  }
+  else if(PixelType == "unsigned_short"){
+    if (m_Verbose) std::cout  << "Launching filter in "<< Dimension <<"D and unsigned_short..." << std::endl;
+    UpdateWithDimAndPixelType<Dimension, unsigned short>();
+  }
+
+  else if (PixelType == "unsigned_char") {
+    if (m_Verbose) std::cout  << "Launching filter in "<< Dimension <<"D and unsigned_char..." << std::endl;
+    UpdateWithDimAndPixelType<Dimension, unsigned char>();
+  }
+
+  //     else if (PixelType == "char"){
+  //       if (m_Verbose) std::cout  << "Launching filter in "<< Dimension <<"D and signed_char..." << std::endl;
+  //       UpdateWithDimAndPixelType<Dimension, signed char>();
+  //     }
+  else if (PixelType == "double") {
+    if (m_Verbose) std::cout  << "Launching filter in "<< Dimension <<"D and double..." << std::endl;
+    UpdateWithDimAndPixelType<Dimension, double>();
+  }
+  else {
+    if (m_Verbose) std::cout  << "Launching filter in "<< Dimension <<"D and float..." << std::endl;
+    UpdateWithDimAndPixelType<Dimension, float>();
+  }
+}
+
+//-------------------------------------------------------------------
+// Update with the number of dimensions and the pixeltype read from
+// the dicom files. The MHD files may be resampled to match the
+// dicom spacing (and number of slices). Rounding errors in resampling
+// are handled by removing files when generating the output dicom
+// series.
+//-------------------------------------------------------------------
+template<class args_info_type>
+template <unsigned int Dimension, class  PixelType>
+void
+Image2DicomDoseGenericFilter<args_info_type>::UpdateWithDimAndPixelType()
+{
+
+#if GDCM_MAJOR_VERSION == 2
+  // ImageTypes
+  typedef itk::Image<PixelType, Dimension> InputImageType;
+  typedef unsigned short int OutputPixelType;
+  typedef itk::Image<OutputPixelType, Dimension> OutputImageType;
+  typedef itk::ImageFileReader< InputImageType >     ReaderType;
+  typedef itk::ImageSeriesReader< InputImageType >     ReaderSeriesType;
+  typedef itk::ImageSeriesWriter<OutputImageType, OutputImageType>  WriterSerieType;
+  typedef itk::ImageRegionIterator< InputImageType > IteratorType;
+  typedef itk::MinimumMaximumImageCalculator <InputImageType> ImageCalculatorFilterType;
+  typedef itk::GDCMImageIO ImageIOType;
+
+  //-----------------------------------------------------------------------------
+  // opening image input file
+  typename ReaderType::Pointer reader = ReaderType::New();
+  const char * filename = m_ArgsInfo.input_arg;
+  reader->SetFileName( filename );
+  reader->Update();
+  typename InputImageType::Pointer image = reader->GetOutput();
+  std::ostringstream value;
+
+  // Read Dicom model file
+  typename ReaderSeriesType::Pointer readerSeries = ReaderSeriesType::New();
+  ImageIOType::Pointer gdcmIO = ImageIOType::New();
+  std::string filename_out = m_ArgsInfo.output_arg;
+  gdcmIO->LoadPrivateTagsOn();
+  gdcmIO->KeepOriginalUIDOn();
+  typename ReaderSeriesType::FileNamesContainer fileNames;
+  fileNames.push_back(m_ArgsInfo.DicomInputFile_arg);
+  readerSeries->SetImageIO( gdcmIO );
+  readerSeries->SetFileNames( fileNames );
+  try {
+    readerSeries->Update();
+  } catch (itk::ExceptionObject &excp) {
+    std::cerr << "Error: Exception thrown while reading the DICOM model file !!" << std::endl;
+    std::cerr << excp << std::endl;
+  }
+
+  // update output dicom keys/tags
+  typename ReaderSeriesType::DictionaryRawPointer inputDict = (*(readerSeries->GetMetaDataDictionaryArray()))[0];
+  typename ReaderSeriesType::DictionaryArrayType outputArray;
+  typename ReaderSeriesType::DictionaryRawPointer outputDict = new typename ReaderSeriesType::DictionaryType;
+  CopyDictionary (*inputDict, *outputDict);
+
+  // origin
+  typename InputImageType::PointType origin = image->GetOrigin();
+  value.str("");
+  value<<origin[0]<<'\\'<<origin[1]<<'\\'<<origin[2];
+  itk::EncapsulateMetaData<std::string>(*outputDict, "0020|0032", value.str());
+  DD(origin);
+
+  // orientation
+  typename InputImageType::DirectionType direction = image->GetDirection();
+  value.str("");
+  value<<direction[0][0]<<'\\'<<direction[0][1]<<'\\'<<direction[0][2]<<'\\'<<direction[1][0]<<'\\'<<direction[1][1]<<'\\'<<direction[1][2];
+  itk::EncapsulateMetaData<std::string>(*outputDict, "0020|0037", value.str());
+  DD(direction);
+
+  // size
+  typename InputImageType::SizeType imageSize = image->GetLargestPossibleRegion().GetSize();
+  //DD(imageSize);
+  int NbCols=imageSize[0];     // col
+  int NbRows=imageSize[1];     // row
+  int NbFrames=imageSize[2];   // frame
+  itk::EncapsulateMetaData<std::string>(*outputDict, "0028|0008", NumberToString(NbFrames));
+  itk::EncapsulateMetaData<std::string>(*outputDict, "0028|0010", NumberToString(NbRows));
+  itk::EncapsulateMetaData<std::string>(*outputDict, "0028|0011", NumberToString(NbCols));
+  DD(NbCols);
+  DD(NbRows);
+  DD(NbFrames);
+
+  // spacing
+  typename InputImageType::SpacingType Spacing = image->GetSpacing();
+  value.str("");
+  value<<Spacing[0]<<'\\'<<Spacing[1];
+  itk::EncapsulateMetaData<std::string>(*outputDict, "0028|0030", value.str());
+  value.str("");
+  value<<Spacing[2];
+  itk::EncapsulateMetaData<std::string>(*outputDict, "0018|0050", value.str());
+  DD(Spacing);
+
+  // offset
+  float offset = 0.;
+  value.str("");
+  value << offset;
+  for (int i=1; i<NbFrames ; i++){
+    offset+=Spacing[2];
+    value << '\\';
+    value << offset;
+  }
+  itk::EncapsulateMetaData<std::string>(*outputDict, "3004|000c", value.str());
+  DD(value.str());
+
+  // scaling
+  typename ImageCalculatorFilterType::Pointer imageCalculatorFilter = ImageCalculatorFilterType::New();
+  imageCalculatorFilter->SetImage(image);
+  imageCalculatorFilter->ComputeMaximum();
+  float highestValue=imageCalculatorFilter->GetMaximum();
+  double doseScaling = highestValue/(pow(2,16)-1);
+  value.str("");
+  value<<doseScaling;
+  itk::EncapsulateMetaData<std::string>(*outputDict, "3004|000e", value.str());
+  DD(value.str());
+
+  // image data
+  typename OutputImageType::Pointer imageOutput = OutputImageType::New();
+  imageOutput->SetRegions(image->GetLargestPossibleRegion());
+  imageOutput->SetSpacing(image->GetSpacing());
+  imageOutput->SetOrigin(image->GetOrigin());
+  imageOutput->SetDirection(image->GetDirection());
+  imageOutput->Allocate();
+
+  typename itk::ImageRegionConstIterator<InputImageType> imageIterator(image,image->GetLargestPossibleRegion());
+  typename itk::ImageRegionIterator<OutputImageType> imageOutputIterator(imageOutput,imageOutput->GetLargestPossibleRegion());
+  while(!imageIterator.IsAtEnd()) {
+    // Set the current pixel to white
+    imageOutputIterator.Set((signed short int)(imageIterator.Get()/doseScaling));
+
+    ++imageIterator;
+    ++imageOutputIterator;
+  }
+
+  // Output directory and filenames
+  typename WriterSerieType::Pointer writerSerie = WriterSerieType::New();
+  outputArray.push_back(outputDict);
+  writerSerie->SetInput( imageOutput );
+  writerSerie->SetImageIO( gdcmIO );
+  typename ReaderSeriesType::FileNamesContainer fileNamesOutput;
+  fileNamesOutput.push_back(filename_out);
+  writerSerie->SetFileNames( fileNamesOutput );
+  writerSerie->SetMetaDataDictionaryArray(&outputArray);
+
+  // Write
+  try {
+    if (m_ArgsInfo.verbose_flag)
+      std::cout << writerSerie << std::endl;
+    writerSerie->Update();
+  } catch( itk::ExceptionObject & excp ) {
+    std::cerr << "Error: Exception thrown while writing the series!!" << std::endl;
+    std::cerr << excp << std::endl;
+  }
+
+  //Read sequence dicom tag with gdcm
+  gdcm::Reader readerTemplateGDCM;
+  readerTemplateGDCM.SetFileName( fileNames[0].c_str() );
+  readerTemplateGDCM.Read();
+  gdcm::File &fileTemplate = readerTemplateGDCM.GetFile();
+  gdcm::DataSet &dsTemplate = fileTemplate.GetDataSet();
+  const unsigned int ptr_len = 42;
+  char *ptrTemplate = new char[ptr_len];
+  memset(ptrTemplate,0,ptr_len);
+
+  const gdcm::DataElement &referenceRTPlanSq = dsTemplate.GetDataElement(gdcm::Tag(0x300c, 0x02));
+
+  //Create the Dose Grid Scaling data element (ITK 4.13 do not take into account - it works well with ITK 4.5.1)
+  gdcm::DataElement deDoseGridScaling( gdcm::Tag(0x3004,0x0e) );
+  deDoseGridScaling.SetVR( gdcm::VR::DS );
+  deDoseGridScaling.SetByteValue(NumberToString(doseScaling).c_str(), (uint32_t)strlen(NumberToString(doseScaling).c_str()));
+
+  //Copy/Write sequence dicom tag with gdcm
+  gdcm::Reader readerOutputGDCM;
+  readerOutputGDCM.SetFileName( fileNamesOutput[0].c_str() );
+  readerOutputGDCM.Read();
+  gdcm::File &file = readerOutputGDCM.GetFile();
+  gdcm::DataSet &dsOutput = file.GetDataSet();
+
+  dsOutput.Insert(referenceRTPlanSq);
+  dsOutput.Replace(deDoseGridScaling);
+  gdcm::Writer w;
+  w.SetFile( file );
+  w.SetFileName( fileNamesOutput[0].c_str() );
+  w.Write();
+
+//---------------------------------------------------------------------------------------
+//WRITE DICOM BIS
+// The previous way of writting DICOM-RT-DOSE works only for ITK
+// and resulting RT-DOSE files are not readable by commercial systems.
+// The nex step is to copy again the output file with another syntax, allowing to make RT-DOSE files readable by commercial systems.
+// see Jean-Pierre Roux comment below.
+
+/*gdcm::FileHelper *fh = new gdcm::FileHelper(args_info.OutputFile_arg);
+   void *imageData;
+   int dataSize;
+  
+   dataSize  = fh->GetImageDataRawSize();
+   imageData = fh->GetImageDataRaw();// somewhat important : Loads the Pixels in memory !
+  
+   fh->SetWriteModeToRaw();
+   fh->SetWriteTypeToDcmExplVR();
+
+   bool res = fh->Write(args_info.OutputFile_arg);
+
+   if(!res)
+      std::cout <<"Fail to write [" << args_info.OutputFile_arg << "]" <<std::endl;   
+   else std::cout<<"\n DICOM File re-written, using the FileHelper syntax, in order to be processed by commercial systems !"<<std::endl;
+
+delete fh; */
+/*  gdcm::Writer w;
+  w.SetFile(mDCMFile);
+  w.SetFileName(m_ArgsInfo.output_arg);
+  w.Write();*/
+//   fh->Delete();
+
+//---------------------------------------------------------------------------------------
+//---------------------------------------------------------------------------------------
+// Jean-Pierre Roux help for DICOM-RT-DOSE writting
+//---------------------------------------------------------------------------------------
+//---------------------------------------------------------------------------------------
+
+//     de      Jean-Pierre Roux <jpr@creatis.insa-lyon.fr>
+// répondre Ã         jpr@creatis.insa-lyon.fr
+// Ã   Loic Grevillot <loic.grevillot@gmail.com>
+// cc  jpr@creatis.insa-lyon.fr,
+// Joël Schaerer <joel.schaerer@gmail.com>,
+// David Sarrut <David.Sarrut@creatis.insa-lyon.fr>,
+// Joel Schaerer <joel.schaerer@creatis.insa-lyon.fr>
+// date        12 juillet 2010 12:23
+// objet       Re: DICOM RT DOSE
+//     
+// masquer les détails 12:23 (Il y a 21 heures)
+//     
+// Bonjour,
+// 
+// J'aurais Ã©crit Ã  peut prèt la même chose.
+// (Ci après un extrait de mon code -Example/ReWrite.cxx-)
+// 
+// L'utilisation d'un FileHelper (qui ne changera rien dans ce cas précis) est une mesure de précaution, car,  l'élément 7FE0|0010 peut Ãªtre compressé (ce qui n'est pas le cas pour tes images), que la manière de stocker les pixels ainsi compressés Ã©tait parfois un peu ... curieuse.
+// Je décompresse, et réécrit non compressé.
+// 
+// ============================
+//    gdcm::File *f = new gdcm::File();
+//    f->SetMaxSizeLoadEntry(0x7fffffff);
+//    f->SetFileName( fileName );
+//    bool res = f->Load(); 
+//    if ( !res )
+//    {
+//       delete f;
+//       return 0;
+//    }
+// 
+//    if (!f->IsReadable())
+//    {
+//        std::cerr << "Sorry, not a Readable DICOM / ACR File"  <<std::endl;
+//        delete f;
+//        return 0;
+//    }
+// 
+//    gdcm::FileHelper *fh = new gdcm::FileHelper(f);
+//    void *imageData;
+//    int dataSize;
+//   
+//    dataSize  = fh->GetImageDataRawSize();
+//    imageData = fh->GetImageDataRaw();// somewhat important : Loads the Pixels in memory !
+//   
+//    fh->SetWriteModeToRaw();
+//    fh->SetWriteTypeToDcmExplVR();
+//    res = fh->Write(outputFileName);
+//   
+//    if(!res)
+//       std::cout <<"Fail to write [" << outputFileName << "]" <<std::endl;   
+// 
+//    f->Delete();
+//    fh->Delete();
+
+//---------------------------------------------------------------------------------------
+//---------------------------------------------------------------------------------------
+// pour supprimer des tags:
+//---------------------------------------------------------------------------------------
+//---------------------------------------------------------------------------------------
+//     SOLUCE 1 QUI MARCHE POUR UN SQItem SIMPLE
+/*
+gdcm::DocEntry *a;
+//a= ((gdcm::SQItem*)mDCMFile)->GetDocEntry(0xfffe,0xe00d);
+a= ((gdcm::SQItem*)mDCMFile)->GetDocEntry(0x3004,0x000e);
+((gdcm::SQItem*)mDCMFile)->RemoveEntry(a);
+mDCMFile->Write (args_info.OutputFile_arg, type);
+*/
+
+//     SOLUCE 2 QUI MARCHE POUR UNE SeqEntry->SQItem 
+/*
+std::cout<<"\ntest correction fichier apres ecriture\n"<<std::endl;
+gdcm::SeqEntry *seqEntry = mDCMFile->GetSeqEntry(0x300c,0x0002);
+gdcm::SQItem* currentItem = seqEntry->GetFirstSQItem();
+gdcm::DocEntry *a;
+//a= currentItem->GetDocEntry(0x0008,0x1155);
+a= currentItem->GetDocEntry(0xfffe,0xe00d);
+currentItem->RemoveEntry(a);
+mDCMFile->Write (args_info.OutputFile_arg, type);
+*/
+
+//gdcm::DocEntry *a;
+//a=GetDocEntry(0x7fe0,0x0000);
+//((gdcm::SQItem*)mDCMFile)->RemoveEntry(a);
+
+//-----------------------------------------------------------------------------------
+  std::cout <<"\n## DICOM Image to RT DOSE application is ended..."<<std::endl;
+  std::cout <<"#########################################################\n" << std::endl;
+
+#else
+  std::cout << "Use GDCM2" << std::endl;
+#endif
+}
+//---------------------------------------------------------------------------
+
+
+//---------------------------------------------------------------------------
+void CopyDictionary (itk::MetaDataDictionary &fromDict, itk::MetaDataDictionary &toDict)
+{
+  typedef itk::MetaDataDictionary DictionaryType;
+
+  DictionaryType::ConstIterator itr = fromDict.Begin();
+  DictionaryType::ConstIterator end = fromDict.End();
+  typedef itk::MetaDataObject< std::string > MetaDataStringType;
+
+  while( itr != end )
+    {
+    itk::MetaDataObjectBase::Pointer  entry = itr->second;
+
+    MetaDataStringType::Pointer entryvalue =
+      dynamic_cast<MetaDataStringType *>( entry.GetPointer() ) ;
+    if( entryvalue )
+      {
+      std::string tagkey   = itr->first;
+      std::string tagvalue = entryvalue->GetMetaDataObjectValue();
+      itk::EncapsulateMetaData<std::string>(toDict, tagkey, tagvalue);
+      }
+    ++itr;
+    }
+}
+//---------------------------------------------------------------------------
+
+//---------------------------------------------------------------------------
+template <typename T> std::string NumberToString ( T Number )
+{
+   std::ostringstream ss;
+   ss << Number;
+   return ss.str();
+}
+//---------------------------------------------------------------------------
+
+
+}//end clitk
+
+#endif //#define clitkImage2DicomDoseGenericFilter_txx
index 7416d32b5c40fdf911258d5d742b7fb0933c9c20..acf432dfe61f01803324265667adaee283de8f20 100644 (file)
@@ -2,15 +2,15 @@
 package "clitk"
 version "Add a (binary) image inside a DICOM RT Structure Set (contours)"
 
-option "config"                 - "Config file"                     string     no
-option "verbose"         v "Verbose"                        flag       off
+option "config"    - "Config file"                                                    string  no
+option "verbose"   v "Verbose"                                                        flag off
 
-option "input"          i "Input image file (binary image) to be converted into contours"  string multiple yes
-option "rtstruct"       r "Input rt struct"                  string    yes
-option "dicom"          d "Input folder where the initial dicom ct is"                 string  yes
-option "output"                 o "Output DicomRT filename"          string    yes
+option "input"     i "Input image file (binary image) to be converted into contours"  string multiple yes
+option "rtstruct"  r "Input rt struct"                                                string  yes
+option "dicom"     d "Input folder where the initial dicom ct is"                     string  yes
+option "output"    o "Output DicomRT filename"                                        string  yes
 
-option "threshold"       t "Threshold for binary image"                         float   no default = "0.5"
-option "skip"           s "Do not write in output the structures that was in input"   flag off
-option "roitype"        - "Name of the type of roi added into the rt-struct"   string  no default = "ORGAN"
+option "threshold" t "Threshold for binary image"                                     float no default = "0.5"
+option "skip"      s "Do not write in output the structures that was in input"        flag off
+option "roitype"   - "Name of the type of roi added into the rt-struct"               string  no default = "ORGAN"
 
index 5d85f523e4b4320f699aecf08a1d9954b4daa353..05bb7f67f1f55db21f48a1fa042d6f06e14d9552 100644 (file)
@@ -13,7 +13,7 @@ option "input2"          j    "Input second image filename"     string   no
 option "output"    o   "Output image filename"           string   yes
 
 option "scalar"           s    "Scalar value"            double   no
-option "operation" t   "Type of operation : \n With another image : 0=add*, 1=multiply, 2=divide,\n 3=max, 4=min, 5=absdiff, 6=squareddiff,  7=difference*, 8=relativ diff\n; For 'scalar' : 0=add*, 1=multiply*, 2=inverse,\n 3=max, 4=min 5=absval 6=squareval\n 7=log 8=exp 9=sqrt 10=EPID 11=divide* 12=normalize (divide by max) 13=-ln(I/IO)**; \n* operations supported with vector fields as inputs. \n** for fluence image, if pixel value == 0, consider value=0.5"  int default="0" no 
+option "operation" t   "Type of operation : \n With another image : 0=add*, 1=multiply, 2=divide,\n 3=max, 4=min, 5=absdiff, 6=squareddiff,  7=difference*, 8=relativ diff\n; For 'scalar' : 0=add*, 1=multiply*, 2=inverse,\n 3=max, 4=min 5=absval 6=squareval\n 7=ln 8=exp 9=sqrt 10=EPID 11=divide* 12=normalize (divide by max) 13=-ln(I/IO)**; \n* operations supported with vector fields as inputs. \n** for fluence image, if pixel value == 0, consider value=0.5"   int default="0" no
 option "pixelValue" -  "Default value for NaN/Inf"     double   default="0.0"  no
 
 option "setFloatOutput" f "Set output to float pixel type" flag off
index 37fa000d3c3e402a1dac421696e070ed8f4f4e90..b2733472d66c54d5d3f41c9dc508522faec9cbb5 100644 (file)
@@ -109,7 +109,7 @@ void ImageArithmGenericFilter<args_info_type>::UpdateWithInputImageType()
   IteratorType it(input1, input1->GetLargestPossibleRegion());
 
   // typedef input2
-  typename ImageType::Pointer input2 = NULL;
+  typename ImageType::Pointer input2 = ITK_NULLPTR;
   IteratorType it2;
 
   // Special case for normalisation
@@ -163,6 +163,7 @@ void ImageArithmGenericFilter<args_info_type>::UpdateWithInputImageType()
       output->SetRegions(input1->GetLargestPossibleRegion());
       output->SetOrigin(input1->GetOrigin());
       output->SetSpacing(input1->GetSpacing());
+      output->SetDirection(input1->GetDirection());
       output->Allocate();
       // Set output iterator
       typedef itk::ImageRegionIterator<OutputImageType> IteratorOutputType;
@@ -218,7 +219,7 @@ void  ImageArithmGenericFilter<args_info_type>::ComputeImage(Iter1 it1, Iter2 it
     break;
   case 2: // Divide
     while (!ito.IsAtEnd()) {
-      if (it1.Get() != 0)
+      if (it2.Get() != 0)
         ito.Set(PixelTypeDownCast<double, PixelType>((double)it1.Get() / (double)it2.Get()));
       else ito.Set(mDefaultPixelValue);
       ++it1;
@@ -269,7 +270,7 @@ void  ImageArithmGenericFilter<args_info_type>::ComputeImage(Iter1 it1, Iter2 it
   case 8: // Relative Difference
     while (!ito.IsAtEnd()) {
       if (it1.Get() != 0) ito.Set(PixelTypeDownCast<double, PixelType>(((double)it1.Get()-(double)it2.Get()))/(double)it1.Get());
-      else ito.Set(0.0);
+      else ito.Set(mDefaultPixelValue);
       ++it1;
       ++it2;
       ++ito;
@@ -349,7 +350,7 @@ void clitk::ImageArithmGenericFilter<args_info_type>::ComputeImage(Iter1 it, Ite
       ++ito;
     }
     break;
-  case 7: // Log
+  case 7: // ln
     while (!it.IsAtEnd()) {
       if (it.Get() > 0)
         ito.Set(PixelTypeDownCast<double, PixelType>(log((double)it.Get())));
index 1e95a7bb53ca9e29408850e90cb4c5bd82ff877f..51a43a029a60fa239ebd0840465751af3198cd63 100644 (file)
@@ -148,6 +148,10 @@ void clitk::ImageConvertGenericFilter::UpdateWithInputImageType()
       // Read the attribute  Image Position (Patient)
       gdcm::Tag  DetectorInformationSequenceTag(0x0054,0x0022);
       const gdcm::DataElement & DIS = ds.GetDataElement(DetectorInformationSequenceTag);
+      if (!DIS.GetByteValue()) {
+        std::cout << "Error: could not find 0x0054,0x0022 tag. Abort." << std::endl;
+        exit(0);
+      }
       gdcm::SmartPointer<gdcm::SequenceOfItems> sqf = DIS.GetValueAsSQ();
       gdcm::Item & item = sqf->GetItem(1);
       gdcm::DataSet & ds_position = item.GetNestedDataSet();
index 1df5601732e76c5fd40683697039f841468edb33..7aecf99025de8963c95ffbedae28c342f999becd 100644 (file)
@@ -102,7 +102,7 @@ namespace clitk
         // Reading input
         typename InputImageType::Pointer input = this->template GetInput<InputImageType>(0);
 
-        typename MaskImageType::Pointer mask = NULL;
+        typename MaskImageType::Pointer mask = ITK_NULLPTR;
         if(mArgsInfo.mask_given) {
             mask = this->template GetInput<MaskImageType>(1);
         }
index 02f4acd3d07a01289abdae66d4c010a8a0a50fc6..96f7c7e65c4f0caea053c7e1053141c53717de1c 100644 (file)
@@ -95,7 +95,7 @@ ImageIntensityWindowingGenericFilter<args_info_type>::UpdateWithInputImageType()
   // Reading input
   typename OutputImageType::Pointer input = this->template GetInput<OutputImageType>(0);
 
-  typename MaskImageType::Pointer mask = NULL;
+  typename MaskImageType::Pointer mask = ITK_NULLPTR;
   if(mArgsInfo.mask_given) {
    mask = this->template GetInput<MaskImageType>(1);
   }
index ba36d18e0e0a77735b0aff4a32556276b91fbfb0..4ff1ef342f5855614946406c75c8fdb97a7cd5aa 100644 (file)
@@ -102,7 +102,7 @@ ImageLaplacianGenericFilter<args_info_type>::UpdateWithInputImageType()
     castFilter->SetInput(input);
     castFilter->Update();
 
-    typename MaskImageType::Pointer mask = NULL;
+    typename MaskImageType::Pointer mask = ITK_NULLPTR;
     if(mArgsInfo.mask_given) {
         mask = this->template GetInput<MaskImageType>(1);
     }
index 7e28e42c6a0f73c280cafcfc21f9ff9afc449298..60d451a7648d2029f89b9ec8b93e381355183561 100644 (file)
@@ -4,17 +4,18 @@ version "2.0"
 #This tool supports multiple images on the input, or even 4D, but all images must be of the same type and dimensions. 
 purpose "Compute statistics on an image, or on part of an image specified by a mask and label(s). The tool also supports multichannel images, which is useful, e.g., for vector fields. All channels are processed (separately) by default, but only one channel may be chosen."
 
-option "config"                -       "Config file"                     string        no
-option "verbose"       v       "Verbose"                         flag          off
+option "config"       - "Config file"                                                         string  no
+option "verbose"      v "Verbose"                                                             flag    off
 
-option "input"         i       "Input image filename"            string        no multiple
-option "channel"    c "Image channel to be used in statistics (-1 to process all channels)"  int no default="-1"
-option "mask"          m       "Mask image filename (uchar)"             string        no
-option "label"         l       "Label(s) in the mask image to consider"        int     no      multiple        default="1"
-option "histogram"     -       "Compute histogram, allows median calculation"  string  no
-option "dvhistogram" - "Compute dose volume histogram" string  no
-option "bins"          -       "Number of histogram bins"                      int     no      default="100"
-option "lower"         -       "Lower histogram bound" double  no default="-1000"      
-option "upper"         -       "Upper histogram bound" double  no default="1000"               
+option "input"        i "Input image filename"                                                string  no  multiple
+option "channel"      c "Image channel to be used in statistics (-1 to process all channels)" int     no  default="-1"
+option "mask"         m "Mask image filename (uchar)"                                         string  no
+option "label"        l "Label(s) in the mask image to consider"                              int     no  multiple     default="1"
+option "localize"     - "With verbose, write the index coordinates of the max and min"        flag    off
+option "histogram"    - "Compute histogram, allows median calculation"                        string  no
+option "dvhistogram"  - "Compute dose volume histogram"                                       string  no
+option "bins"         - "Number of histogram bins"                                            int     no  default="100"
+option "lower"        - "Lower histogram bound"                                               double  no  default="-1000"
+option "upper"        - "Upper histogram bound"                                               double  no  default="1000"
 
-option "allow_resize"          r       "Resize mask if different from input"                     flag          off
+option "allow_resize" r "Resize mask if different from input"                                 flag    off
index 8544d59b6a4027e2bea3fd89f0f621dcc9b17715..26da4e44d5e0a868029f2133327de6f715c90e2a 100644 (file)
@@ -61,6 +61,7 @@ namespace clitk
     {
       m_ArgsInfo=a;
       m_Verbose=m_ArgsInfo.verbose_flag;
+      m_Localize=m_ArgsInfo.localize_flag;
 
       if(m_ArgsInfo.input_given)
         m_InputFileName=m_ArgsInfo.input_arg[0];
@@ -98,7 +99,7 @@ namespace clitk
     // Data members
     //----------------------------------------
     args_info_clitkImageStatistics m_ArgsInfo;
-    bool m_Verbose;
+    bool m_Verbose, m_Localize;
     std::string m_InputFileName;
 
   };
index b3eff93ce1082304554c03f1a71589a41d2fa059..82a11ab5355a86134a3a6cc666ff9136b04dc423 100644 (file)
@@ -20,6 +20,7 @@
 
 #include "itkNthElementImageAdaptor.h"
 #include "itkJoinSeriesImageFilter.h"
+#include "itkImageRegionConstIterator.h"
 
 #include "clitkImageStatisticsGenericFilter.h"
 #include "clitkCropLikeImageFilter.h"
@@ -232,8 +233,21 @@ namespace clitk
 
         statisticsFilter->Update();
 
+        //find localization for max and min (the last pixel found)
+        typename InputImageType::IndexType minIndex, maxIndex;
+        if (m_Verbose && m_Localize) {
+          itk::ImageRegionConstIterator<InputImageAdaptorType> imageIterator(input_adaptor,input_adaptor->GetLargestPossibleRegion());
+          while(!imageIterator.IsAtEnd()) {
+            if (imageIterator.Get() == statisticsFilter->GetMinimum(label))
+              minIndex = imageIterator.GetIndex();
+            if (imageIterator.Get() == statisticsFilter->GetMaximum(label))
+              maxIndex = imageIterator.GetIndex();
+            ++imageIterator;
+          }
+        }
+
         // Output
-       if (m_Verbose) std::cout<<"N° of pixels: ";
+        if (m_Verbose) std::cout<<"N° of pixels: ";
         std::cout<<statisticsFilter->GetCount(label)<<std::endl;
         if (m_Verbose) std::cout<<"Mean: ";
         std::cout<<statisticsFilter->GetMean(label)<<std::endl;
@@ -243,8 +257,16 @@ namespace clitk
         std::cout<<statisticsFilter->GetVariance(label)<<std::endl;
         if (m_Verbose) std::cout<<"Min: ";
         std::cout<<statisticsFilter->GetMinimum(label)<<std::endl;
+        if (m_Verbose && m_Localize) {
+          std::cout<<"        in voxel of index: ";
+          std::cout<<minIndex<<std::endl;
+        }
         if (m_Verbose) std::cout<<"Max: ";
         std::cout<<statisticsFilter->GetMaximum(label)<<std::endl;
+        if (m_Verbose && m_Localize) {
+          std::cout<<"        in voxel of index: ";
+          std::cout<<maxIndex<<std::endl;
+        }
         if (m_Verbose) std::cout<<"Sum: ";
         std::cout<<statisticsFilter->GetSum(label)<<std::endl;
         if (m_Verbose) std::cout<<"Volume (cc): ";
index 7438814ece656e8d045fac26f700c471c630bcfc..8e8f2701c1880bfc2e931310c36076cb3b9100ff 100644 (file)
@@ -9,4 +9,4 @@ option "imagetypes"     -       "Display allowed image types"     flag          off
 
 option "input"         i       "Input image filename"            string        yes
 option "output"        o       "Output image filename"           string        yes
-option "dimension"     d       "Dimension along which to projetct"       int   yes
+option "dimension"     d       "Dimension along which to project"        int   yes
similarity index 80%
rename from tools/clitkResampleImage.cxx
rename to tools/clitkMaskOfIntegratedIntensity.cxx
index 64be16799a63a5c185e8acf65aa3488435f49dc0..93d53e199aa14c2bd8ef9c30178987467bb689ee 100644 (file)
 ===========================================================================**/
 
 // clitk
-#include "clitkResampleImage_ggo.h"
-#include "clitkIO.h"
-#include "clitkResampleImageGenericFilter.h"
+#include "clitkMaskOfIntegratedIntensity_ggo.h"
+#include "clitkMaskOfIntegratedIntensityGenericFilter.h"
 
 //--------------------------------------------------------------------
 int main(int argc, char * argv[])
 {
 
   // Init command line
-  GGO(clitkResampleImage, args_info);
+  GGO(clitkMaskOfIntegratedIntensity, args_info);
   CLITK_INIT;
 
   // Filter
-  typedef clitk::ResampleImageGenericFilter FilterType;
+  typedef clitk::MaskOfIntegratedIntensityGenericFilter<args_info_clitkMaskOfIntegratedIntensity> FilterType;
   FilterType::Pointer filter = FilterType::New();
 
   filter->SetArgsInfo(args_info);
-  CLITK_TRY_CATCH_EXIT(filter->Update());
+  filter->Update();
 
-  // this is the end my friend
   return EXIT_SUCCESS;
-
 }// end main
+
 //--------------------------------------------------------------------
diff --git a/tools/clitkMaskOfIntegratedIntensity.ggo b/tools/clitkMaskOfIntegratedIntensity.ggo
new file mode 100644 (file)
index 0000000..c60f92f
--- /dev/null
@@ -0,0 +1,12 @@
+#File clitkMaskOfIntegratedIntensity.ggo
+package "clitkMaskOfIntegratedIntensity"
+version "1.0"
+purpose "Compute a mask that represent X% of the total pixels values"
+
+option "config"                 -      "Config file"           string  optional
+option "verbose"        v  "Verbose"                     flag          off
+option "imagetypes"  -  "Display allowed image types"    flag          off
+
+option "input"         i       "Input image filename"            string  required
+option "output"   o "Output image filename"              string        required
+option "percentage"                p   "Percentage of total pixels values (in %)" double required
diff --git a/tools/clitkMaskOfIntegratedIntensityGenericFilter.h b/tools/clitkMaskOfIntegratedIntensityGenericFilter.h
new file mode 100644 (file)
index 0000000..3d64550
--- /dev/null
@@ -0,0 +1,78 @@
+/*=========================================================================
+  Program:   vv                     http://www.creatis.insa-lyon.fr/rio/vv
+
+  Authors belong to:
+  - University of LYON              http://www.universite-lyon.fr/
+  - Léon Bérard cancer center       http://www.centreleonberard.fr
+  - CREATIS CNRS laboratory         http://www.creatis.insa-lyon.fr
+
+  This software is distributed WITHOUT ANY WARRANTY; without even
+  the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+  PURPOSE.  See the copyright notices for more information.
+
+  It is distributed under dual licence
+
+  - BSD        See included LICENSE.txt file
+  - CeCILL-B   http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.html
+  ===========================================================================**/
+
+#ifndef CLITKMaskOfIntegratedIntensityGENERICFILTER_H
+#define CLITKMaskOfIntegratedIntensityGENERICFILTER_H
+
+#include "clitkIO.h"
+#include "clitkImageToImageGenericFilter.h"
+
+//--------------------------------------------------------------------
+namespace clitk
+{
+
+  template<class args_info_type>
+    class ITK_EXPORT MaskOfIntegratedIntensityGenericFilter:
+    public ImageToImageGenericFilter<MaskOfIntegratedIntensityGenericFilter<args_info_type> >
+    {
+
+    public:
+
+      //--------------------------------------------------------------------
+      MaskOfIntegratedIntensityGenericFilter();
+
+      //--------------------------------------------------------------------
+      typedef MaskOfIntegratedIntensityGenericFilter         Self;
+      typedef itk::SmartPointer<Self>                      Pointer;
+      typedef itk::SmartPointer<const Self>                ConstPointer;
+
+      //--------------------------------------------------------------------
+      // Method for creation through the object factory
+      // and Run-time type information (and related methods)
+      itkNewMacro(Self);
+      itkTypeMacro(MaskOfIntegratedIntensityGenericFilter, LightObject);
+
+      //--------------------------------------------------------------------
+      void SetArgsInfo(const args_info_type & a);
+
+      void SetPercentage(double p) { mPercentage = p; }
+      double GetPercentage() const { return mPercentage; }
+
+      //--------------------------------------------------------------------
+      // Main function called each time the filter is updated
+      template<class InputImageType>
+        void UpdateWithInputImageType();
+
+    protected:
+      template<unsigned int Dim> void InitializeImageType();
+      args_info_type mArgsInfo;
+      double mPercentage;
+
+    }; // end class
+  //--------------------------------------------------------------------
+
+//Implementation of the pair comparative function
+template <typename T> bool comparator ( const std::pair<T, size_t>& l, const std::pair<T, size_t>& r);
+
+} // end namespace clitk
+
+#ifndef ITK_MANUAL_INSTANTIATION
+#include "clitkMaskOfIntegratedIntensityGenericFilter.txx"
+#endif
+
+#endif // #define clitkMaskOfIntegratedIntensityGenericFilter_h
diff --git a/tools/clitkMaskOfIntegratedIntensityGenericFilter.txx b/tools/clitkMaskOfIntegratedIntensityGenericFilter.txx
new file mode 100644 (file)
index 0000000..f80dc99
--- /dev/null
@@ -0,0 +1,180 @@
+/*=========================================================================
+  Program:   vv                     http://www.creatis.insa-lyon.fr/rio/vv
+
+  Authors belong to:
+  - University of LYON              http://www.universite-lyon.fr/
+  - Léon Bérard cancer center       http://www.centreleonberard.fr
+  - CREATIS CNRS laboratory         http://www.creatis.insa-lyon.fr
+
+  This software is distributed WITHOUT ANY WARRANTY; without even
+  the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+  PURPOSE.  See the copyright notices for more information.
+
+  It is distributed under dual licence
+
+  - BSD        See included LICENSE.txt file
+  - CeCILL-B   http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.html
+  ===========================================================================**/
+
+#ifndef clitkMaskOfIntegratedIntensityGenericFilter_txx
+#define clitkMaskOfIntegratedIntensityGenericFilter_txx
+
+// itk include
+#include "itkIntensityWindowingImageFilter.h"
+#include "itkLabelStatisticsImageFilter.h"
+#include "itkMaskImageFilter.h"
+#include "itkMaskNegatedImageFilter.h"
+#include <clitkCommon.h>
+#include <numeric>
+
+namespace clitk
+{
+
+  //--------------------------------------------------------------------
+  template<class args_info_type>
+  MaskOfIntegratedIntensityGenericFilter<args_info_type>::MaskOfIntegratedIntensityGenericFilter():
+    ImageToImageGenericFilter<Self>("MaskOfIntegratedIntensity")
+  {
+    InitializeImageType<2>();
+    InitializeImageType<3>();
+  }
+  //--------------------------------------------------------------------
+
+
+  //--------------------------------------------------------------------
+  template<class args_info_type>
+  template<unsigned int Dim>
+  void MaskOfIntegratedIntensityGenericFilter<args_info_type>::InitializeImageType()
+  {
+    ADD_DEFAULT_IMAGE_TYPES(Dim);
+  }
+  //--------------------------------------------------------------------
+
+
+  //--------------------------------------------------------------------
+  template<class args_info_type>
+  void MaskOfIntegratedIntensityGenericFilter<args_info_type>::SetArgsInfo(const args_info_type & a)
+  {
+    mArgsInfo=a;
+    this->SetIOVerbose(mArgsInfo.verbose_flag);
+    if (mArgsInfo.imagetypes_flag) this->PrintAvailableImageTypes();
+
+    this->SetInputFilename(mArgsInfo.input_arg);
+    this->SetOutputFilename(mArgsInfo.output_arg);
+    this->SetPercentage(mArgsInfo.percentage_arg);
+
+  }
+  //--------------------------------------------------------------------
+
+
+  //--------------------------------------------------------------------
+  //  https://stackoverflow.com/questions/1577475/c-sorting-and-keeping-track-of-indexes
+  template <typename T>
+  std::vector<size_t> sort_indexes(const std::vector<T> &v) {
+
+    // initialize original index locations
+    std::vector<size_t> idx(v.size());
+    std::vector<std::pair<T, size_t> > compVector(v.size());
+    for (size_t i = 0; i < v.size(); ++i) {
+      compVector[i].first = v[i];
+      compVector[i].second = i;
+    }
+
+    // sort indexes based on comparing values in v
+    std::sort(compVector.begin(), compVector.end(), comparator<T>);
+    for (size_t i = 0; i < v.size(); ++i) {
+      idx[i] = compVector[i].second;
+    }
+
+    return idx;
+  }
+  //--------------------------------------------------------------------
+
+
+  //--------------------------------------------------------------------
+  // Update with the number of dimensions and the pixeltype
+  //--------------------------------------------------------------------
+  template<class args_info_type>
+  template<class InputImageType>
+  void
+  MaskOfIntegratedIntensityGenericFilter<args_info_type>::UpdateWithInputImageType()
+  {
+    // Main filter
+    typedef typename InputImageType::PixelType InputPixelType;
+    typedef itk::Image<unsigned char, InputImageType::ImageDimension> MaskImageType;
+
+    // Reading input
+    typename InputImageType::Pointer input = this->template GetInput<InputImageType>(0);
+
+    typename MaskImageType::Pointer mask;
+    mask = MaskImageType::New();
+    mask->SetRegions(input->GetLargestPossibleRegion());
+    mask->SetOrigin(input->GetOrigin());
+    mask->SetSpacing(input->GetSpacing());
+    mask->Allocate();
+    mask->FillBuffer(0);
+
+    // Get a vector of all values (will be easier to sort)
+    // And compute total sum of values
+    std::vector<double> values;
+    typedef itk::ImageRegionIterator<InputImageType> IteratorInputType;
+    IteratorInputType iter(input, input->GetLargestPossibleRegion());
+    iter.GoToBegin();
+    double total = 0.0;
+    while (!iter.IsAtEnd()) {
+      values.push_back(iter.Get());
+      total += iter.Get();
+      ++iter;
+    }
+
+    // Sort (reverse)
+    std::vector<size_t> indices = sort_indexes(values);
+
+    // Get max index of pixel to reach xx percent
+    double current = 0.0;
+    double max = GetPercentage()/100.0*total;
+    int i=0;
+    int n = input->GetLargestPossibleRegion().GetNumberOfPixels();
+    std::vector<int> should_keep(values.size());;
+    std::fill(should_keep.begin(), should_keep.end(), 0);
+    while (current<max && i<n) { // loop by decreasing pixel values
+      current += values[indices[i]];
+      should_keep[indices[i]] = 1.0;
+      ++i;
+    }
+    int nb = i;
+
+    // Set mask values
+    typedef itk::ImageRegionIterator<MaskImageType> IteratorMaskType;
+    IteratorMaskType itm(mask, mask->GetLargestPossibleRegion());
+    iter.GoToBegin();
+    itm.GoToBegin();
+    i = 0;
+    while (!iter.IsAtEnd()) {
+      if (should_keep[i]) itm.Set(1);
+      ++iter;
+      ++itm;
+      ++i;
+    }
+
+    // Verbose option
+    if (this->m_IOVerbose)
+      std::cout << "Sum of pixel values : " << total << std::endl
+                << "Percentage          : " << GetPercentage() << "%" << std::endl
+                << "Number of pixels    : " << nb << "/" << n << std::endl
+                << "Number of pixels    : " << nb/n*100.0 << "%" << std::endl;
+
+    // Write/Save results
+    this->template SetNextOutput<MaskImageType>(mask);
+  }
+  //--------------------------------------------------------------------
+
+//--------------------------------------------------------------------
+template <typename T>
+bool comparator ( const std::pair<T, size_t>& l, const std::pair<T, size_t>& r)
+   { return l.first > r.first; }
+//--------------------------------------------------------------------
+
+}//end clitk
+
+#endif //#define clitkMaskOfIntegratedIntensityGenericFilter_txx
index 022ae0bf070cd74255dcdfe7cb02e2adcb07c2f1..307c8cc326da6e21b84112e7efeb3ef4bf3a6f04 100644 (file)
@@ -55,6 +55,9 @@ int main(int argc, char *argv[]) {
         FATAL("Error, please provide at least two inputs files");
     }
 
+    // Set the tree maximum size to 1TB instead of 100GB
+    TTree::SetMaxTreeSize( 1000000000000LL );
+
     /* The following block does some bookkeeping necesary for files originating from a pet simulation.
     Seems fixing some timing info, for coincidences between files perhaps.
     It seems the files are later on reopened and merged, if the conditions were met.
index 99a43a464ad8157367caf9f5ed7e301b0736ac64..e6911afde54adce2a07e5a3656d18520ca235b43 100644 (file)
@@ -94,7 +94,7 @@ namespace clitk
         // Reading input
         typename InputImageType::Pointer input = this->template GetInput<InputImageType>(0);
 
-        typename MaskImageType::Pointer mask = NULL;
+        typename MaskImageType::Pointer mask = ITK_NULLPTR;
         if(mArgsInfo.mask_given) {
             mask = this->template GetInput<MaskImageType>(1);
         }
diff --git a/tools/clitkPartitionEnergyWindowDicom.cxx b/tools/clitkPartitionEnergyWindowDicom.cxx
new file mode 100644 (file)
index 0000000..c672bc2
--- /dev/null
@@ -0,0 +1,53 @@
+/*=========================================================================
+  Program:   vv                     http://www.creatis.insa-lyon.fr/rio/vv
+
+  Authors belong to:
+  - University of LYON              http://www.universite-lyon.fr/
+  - Léon Bérard cancer center       http://www.centreleonberard.fr
+  - CREATIS CNRS laboratory         http://www.creatis.insa-lyon.fr
+
+  This software is distributed WITHOUT ANY WARRANTY; without even
+  the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+  PURPOSE.  See the copyright notices for more information.
+
+  It is distributed under dual licence
+
+  - BSD        See included LICENSE.txt file
+  - CeCILL-B   http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.html
+===========================================================================**/
+
+/* =================================================
+ * @file   clitkPartitionEnergyWindowDicom.cxx
+ * @author Jef Vandemeulebroucke
+ * @date   4th of August
+ *
+ * @brief Write a volume into a dicom with the header of another dicom
+ *
+ ===================================================*/
+
+
+// clitk
+#include "clitkPartitionEnergyWindowDicom_ggo.h"
+#include "clitkIO.h"
+#include "clitkPartitionEnergyWindowDicomGenericFilter.h"
+#include "clitkCommon.h"
+
+//--------------------------------------------------------------------
+int main(int argc, char * argv[])
+{
+
+  // Init command line
+  GGO(clitkPartitionEnergyWindowDicom, args_info);
+  CLITK_INIT;
+
+  // Filter
+  typedef clitk::PartitionEnergyWindowDicomGenericFilter<args_info_clitkPartitionEnergyWindowDicom> FilterType;
+  FilterType::Pointer genericFilter = FilterType::New();
+
+  genericFilter->SetArgsInfo(args_info);
+  genericFilter->Update();
+
+  return EXIT_SUCCESS;
+}// end main
+
+//--------------------------------------------------------------------
diff --git a/tools/clitkPartitionEnergyWindowDicom.ggo b/tools/clitkPartitionEnergyWindowDicom.ggo
new file mode 100644 (file)
index 0000000..81bc1f4
--- /dev/null
@@ -0,0 +1,10 @@
+#File clitkPartitionEnergyWindowDicom.ggo
+package "clitkPartitionEnergyWindowDicom"
+version "1.0"
+purpose "From one Dicom with different energy windows (eg with SPECT), the tool create new dicoms for each energy window and copy the correct dicom tag"
+
+option "config"                -       "Config file"                     string        no
+option "verbose"       v       "Verbose"                         flag          off
+
+option "input"         i       "Input Dicom image filename"              string        yes
+option "output"        o       "Output dicom directory"          string        yes 
diff --git a/tools/clitkPartitionEnergyWindowDicomGenericFilter.cxx b/tools/clitkPartitionEnergyWindowDicomGenericFilter.cxx
new file mode 100644 (file)
index 0000000..0150703
--- /dev/null
@@ -0,0 +1,39 @@
+/*=========================================================================
+  Program:   vv                     http://www.creatis.insa-lyon.fr/rio/vv
+
+  Authors belong to:
+  - University of LYON              http://www.universite-lyon.fr/
+  - Léon Bérard cancer center       http://www.centreleonberard.fr
+  - CREATIS CNRS laboratory         http://www.creatis.insa-lyon.fr
+
+  This software is distributed WITHOUT ANY WARRANTY; without even
+  the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+  PURPOSE.  See the copyright notices for more information.
+
+  It is distributed under dual licence
+
+  - BSD        See included LICENSE.txt file
+  - CeCILL-B   http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.html
+===========================================================================**/
+#ifndef clitkPartitionEnergyWindowDicomGenericFilter_cxx
+#define clitkPartitionEnergyWindowDicomGenericFilter_cxx
+
+/* =================================================
+ * @file   clitkPartitionEnergyWindowDicomGenericFilter.cxx
+ * @author
+ * @date
+ *
+ * @brief
+ *
+ ===================================================*/
+
+#include "clitkPartitionEnergyWindowDicomGenericFilter.h"
+
+
+namespace clitk
+{
+
+
+} //end clitk
+
+#endif  //#define clitkPartitionEnergyWindowDicomGenericFilter_cxx
diff --git a/tools/clitkPartitionEnergyWindowDicomGenericFilter.h b/tools/clitkPartitionEnergyWindowDicomGenericFilter.h
new file mode 100644 (file)
index 0000000..e118521
--- /dev/null
@@ -0,0 +1,128 @@
+/*=========================================================================
+  Program:   vv                     http://www.creatis.insa-lyon.fr/rio/vv
+
+  Authors belong to: 
+  - University of LYON              http://www.universite-lyon.fr/
+  - Léon Bérard cancer center       http://www.centreleonberard.fr
+  - CREATIS CNRS laboratory         http://www.creatis.insa-lyon.fr
+
+  This software is distributed WITHOUT ANY WARRANTY; without even
+  the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+  PURPOSE.  See the copyright notices for more information.
+
+  It is distributed under dual licence
+
+  - BSD        See included LICENSE.txt file
+  - CeCILL-B   http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.html
+===========================================================================**/
+#ifndef clitkPartitionEnergyWindowDicomGenericFilter_h
+#define clitkPartitionEnergyWindowDicomGenericFilter_h
+
+/* =================================================
+ * @file   clitkPartitionEnergyWindowDicomGenericFilter.h
+ * @author 
+ * @date   
+ * 
+ * @brief 
+ * 
+ ===================================================*/
+
+
+// clitk include
+#include "clitkIO.h"
+#include "clitkImageCommon.h"
+#include "clitkPartitionEnergyWindowDicom_ggo.h"
+
+//itk include
+#include "itkLightObject.h"
+#include "itkGDCMImageIO.h"
+#include "itkMetaDataDictionary.h"
+#include "itkGDCMSeriesFileNames.h"
+#include "itkImageSeriesReader.h"
+#include "itkImageSeriesWriter.h"
+#include "itkImageFileReader.h"
+#include "itkImageFileWriter.h"
+#include <vector>
+#include <itksys/SystemTools.hxx>
+
+namespace clitk 
+{
+  template<class args_info_type>
+  class ITK_EXPORT PartitionEnergyWindowDicomGenericFilter : public itk::LightObject
+  {
+  public:
+    //----------------------------------------
+    // ITK
+    //----------------------------------------
+    typedef PartitionEnergyWindowDicomGenericFilter   Self;
+    typedef itk::LightObject                          Superclass;
+    typedef itk::SmartPointer<Self>                   Pointer;
+    typedef itk::SmartPointer<const Self>             ConstPointer;
+   
+    // Method for creation through the object factory
+    itkNewMacro(Self);  
+
+    // Run-time type information (and related methods)
+    itkTypeMacro( PartitionEnergyWindowDicomGenericFilter, LightObject );
+
+
+    //----------------------------------------
+    // Typedefs
+    //----------------------------------------
+
+
+    //----------------------------------------
+    // Set & Get
+    //----------------------------------------    
+    void SetArgsInfo(const args_info_type & a)
+    {
+      m_ArgsInfo=a;
+      m_Verbose=m_ArgsInfo.verbose_flag;
+      m_InputFileName=m_ArgsInfo.input_arg;
+    }
+    
+    
+    //----------------------------------------  
+    // Update
+    //----------------------------------------  
+    void Update();
+
+  protected:
+
+    //----------------------------------------  
+    // Constructor & Destructor
+    //----------------------------------------  
+    PartitionEnergyWindowDicomGenericFilter();
+    ~PartitionEnergyWindowDicomGenericFilter() {};
+
+    
+    //----------------------------------------  
+    // Templated members
+    //----------------------------------------  
+    template <unsigned int Dimension>  void UpdateWithDim(std::string PixelType);
+    template <unsigned int Dimension, class PixelType>  void UpdateWithDimAndPixelType();
+
+
+    //----------------------------------------  
+    // Data members
+    //----------------------------------------
+    args_info_type m_ArgsInfo;
+    bool m_Verbose;
+    std::string m_InputFileName;
+
+
+  };
+
+//Copy dicom dictionary
+void CopyDictionary (itk::MetaDataDictionary &fromDict, itk::MetaDataDictionary &toDict);
+
+//convert to std::string
+template <typename T> std::string NumberToString ( T Number );
+
+} // end namespace clitk
+
+#ifndef ITK_MANUAL_INSTANTIATION
+#include "clitkPartitionEnergyWindowDicomGenericFilter.txx"
+#endif
+
+#endif // #define clitkPartitionEnergyWindowDicomGenericFilter_h
diff --git a/tools/clitkPartitionEnergyWindowDicomGenericFilter.txx b/tools/clitkPartitionEnergyWindowDicomGenericFilter.txx
new file mode 100644 (file)
index 0000000..bf58925
--- /dev/null
@@ -0,0 +1,409 @@
+/*=========================================================================
+  Program:   vv                     http://www.creatis.insa-lyon.fr/rio/vv
+
+  Authors belong to:
+  - University of LYON              http://www.universite-lyon.fr/
+  - Léon Bérard cancer center       http://www.centreleonberard.fr
+  - CREATIS CNRS laboratory         http://www.creatis.insa-lyon.fr
+
+  This software is distributed WITHOUT ANY WARRANTY; without even
+  the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+  PURPOSE.  See the copyright notices for more information.
+
+  It is distributed under dual licence
+
+  - BSD        See included LICENSE.txt file
+  - CeCILL-B   http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.html
+===========================================================================**/
+#ifndef clitkPartitionEnergyWindowDicomGenericFilter_txx
+#define clitkPartitionEnergyWindowDicomGenericFilter_txx
+
+/* =================================================
+ * @file   clitkPartitionEnergyWindowDicomGenericFilter.txx
+ * @author
+ * @date
+ *
+ * @brief
+ *
+ ===================================================*/
+
+#include <sstream>
+// clitk
+#include "clitkResampleImageWithOptionsFilter.h"
+#if GDCM_MAJOR_VERSION >= 2
+#include "gdcmUIDGenerator.h"
+#include <gdcmImageHelper.h>
+#include <gdcmAttribute.h>
+#include <gdcmReader.h>
+#include <gdcmWriter.h>
+#include <gdcmDataElement.h>
+#include <gdcmTag.h>
+#else
+#include "gdcmFile.h"
+#include "gdcmUtil.h"
+#endif
+
+#include "itkImageRegionIterator.h"
+#include "itkMetaImageIO.h"
+#include "itkMetaDataDictionary.h"
+
+
+namespace clitk
+{
+
+
+//-----------------------------------------------------------
+// Constructor
+//-----------------------------------------------------------
+template<class args_info_type>
+PartitionEnergyWindowDicomGenericFilter<args_info_type>::PartitionEnergyWindowDicomGenericFilter()
+{
+  m_Verbose=false;
+  m_InputFileName="";
+}
+
+
+//-----------------------------------------------------------
+// Update
+//-----------------------------------------------------------
+template<class args_info_type>
+void PartitionEnergyWindowDicomGenericFilter<args_info_type>::Update()
+{
+  // Read the Dimension and PixelType
+  int Dimension;
+  std::string PixelType;
+  ReadImageDimensionAndPixelType(m_InputFileName, Dimension, PixelType);
+
+
+  // Call UpdateWithDim
+  if(Dimension==2) UpdateWithDim<2>(PixelType);
+  else if(Dimension==3) UpdateWithDim<3>(PixelType);
+  // else if (Dimension==4)UpdateWithDim<4>(PixelType);
+  else {
+    std::cout<<"Error, Only for 2 or 3  Dimensions!!!"<<std::endl ;
+    return;
+  }
+}
+
+//-------------------------------------------------------------------
+// Update with the number of dimensions
+//-------------------------------------------------------------------
+template<class args_info_type>
+template<unsigned int Dimension>
+void
+PartitionEnergyWindowDicomGenericFilter<args_info_type>::UpdateWithDim(std::string PixelType)
+{
+  if (m_Verbose) std::cout << "Image was detected to be "<<Dimension<<"D and "<< PixelType<<"..."<<std::endl;
+
+  if(PixelType == "short") {
+    if (m_Verbose) std::cout << "Launching filter in "<< Dimension <<"D and signed short..." << std::endl;
+    UpdateWithDimAndPixelType<Dimension, signed short>();
+  }
+  else if(PixelType == "unsigned_short"){
+    if (m_Verbose) std::cout  << "Launching filter in "<< Dimension <<"D and unsigned_short..." << std::endl;
+    UpdateWithDimAndPixelType<Dimension, unsigned short>();
+  }
+
+  else if (PixelType == "unsigned_char") {
+    if (m_Verbose) std::cout  << "Launching filter in "<< Dimension <<"D and unsigned_char..." << std::endl;
+    UpdateWithDimAndPixelType<Dimension, unsigned char>();
+  }
+
+  //     else if (PixelType == "char"){
+  //       if (m_Verbose) std::cout  << "Launching filter in "<< Dimension <<"D and signed_char..." << std::endl;
+  //       UpdateWithDimAndPixelType<Dimension, signed char>();
+  //     }
+  else if (PixelType == "double") {
+    if (m_Verbose) std::cout  << "Launching filter in "<< Dimension <<"D and double..." << std::endl;
+    UpdateWithDimAndPixelType<Dimension, double>();
+  }
+  else {
+    if (m_Verbose) std::cout  << "Launching filter in "<< Dimension <<"D and float..." << std::endl;
+    UpdateWithDimAndPixelType<Dimension, float>();
+  }
+}
+
+//-------------------------------------------------------------------
+// Update with the number of dimensions and the pixeltype read from
+// the dicom files. The MHD files may be resampled to match the
+// dicom spacing (and number of slices). Rounding errors in resampling
+// are handled by removing files when generating the output dicom
+// series.
+//-------------------------------------------------------------------
+template<class args_info_type>
+template <unsigned int Dimension, class  PixelType>
+void
+PartitionEnergyWindowDicomGenericFilter<args_info_type>::UpdateWithDimAndPixelType()
+{
+
+#if GDCM_MAJOR_VERSION == 2
+  // ImageTypes
+  typedef itk::Image<PixelType, Dimension> InputImageType;
+  typedef itk::Image<PixelType, Dimension> OutputImageType;
+  typedef itk::ImageSeriesReader< InputImageType >     ReaderType;
+  typedef itk::ImageSeriesWriter< InputImageType, InputImageType >     WriterType;
+  typedef itk::GDCMImageIO ImageIOType;
+  typedef typename InputImageType::RegionType RegionType;
+  typedef typename RegionType::SizeType SizeType;
+
+
+  // Read Dicom model file
+  typename ReaderType::Pointer reader = ReaderType::New();
+  ImageIOType::Pointer gdcmIO = ImageIOType::New();
+  gdcmIO->LoadPrivateTagsOn();
+  gdcmIO->KeepOriginalUIDOn();
+  reader->SetImageIO( gdcmIO );
+  typename ReaderType::FileNamesContainer fileNamesInput;
+  fileNamesInput.push_back(m_ArgsInfo.input_arg);
+  reader->SetFileNames( fileNamesInput );
+  try {
+    reader->Update();
+  } catch (itk::ExceptionObject &excp) {
+    std::cerr << "Error: Exception thrown while reading the DICOM model file !!" << std::endl;
+    std::cerr << excp << std::endl;
+  }
+  typename InputImageType::Pointer input = reader->GetOutput();
+
+  typename ReaderType::DictionaryRawPointer inputDict = (*(reader->GetMetaDataDictionaryArray()))[0];
+  typename ReaderType::DictionaryArrayType outputArray;
+  typename ReaderType::DictionaryRawPointer outputDict = new typename ReaderType::DictionaryType;
+  CopyDictionary (*inputDict, *outputDict);
+  itk::EncapsulateMetaData<std::string>(*outputDict, "0054|0011", NumberToString(1));
+  outputArray.push_back(outputDict);
+
+
+  //Read the number of slices and energy window, rotation
+  int nbSlice = input->GetLargestPossibleRegion().GetSize()[2];
+  gdcm::Reader hreader;
+  hreader.SetFileName(m_ArgsInfo.input_arg);
+  hreader.Read();
+  gdcm::DataSet& dsInput = hreader.GetFile().GetDataSet();
+  gdcm::Attribute<0x54,0x11> series_number_att;
+  series_number_att.SetFromDataSet(dsInput);
+  int nbEnergyWindow = series_number_att.GetValue();
+
+  int nbRotation;
+  if (dsInput.FindDataElement(gdcm::Tag(0x54, 0x52))) {
+    const gdcm::DataElement &seqRotation = dsInput.GetDataElement(gdcm::Tag(0x54, 0x52));
+    gdcm::SmartPointer<gdcm::SequenceOfItems> sqiRotation = seqRotation.GetValueAsSQ();
+    gdcm::Item &itemRotation = sqiRotation->GetItem(1);
+    const gdcm::DataElement &deRotation = itemRotation.GetDataElement(gdcm::Tag(0x54, 0x53));
+    unsigned short SamplesPerPixelRotation = *((unsigned short *) deRotation.GetByteValue()->GetPointer());
+    std::stringstream s_SamplesPerPixelRotation;
+    s_SamplesPerPixelRotation << SamplesPerPixelRotation;
+    std::string p_StrRotation = s_SamplesPerPixelRotation.str();
+    nbRotation = (int)atof(p_StrRotation.c_str());
+  } else
+    nbRotation = 1;
+
+  int nbSlicePerEnergy = nbSlice / nbEnergyWindow;
+  if (nbSlicePerEnergy*nbEnergyWindow != nbSlice)
+    std::cerr << "Error: The number of slices is not correct !!" << std::endl;
+
+  for (unsigned int energy = 0; energy < nbEnergyWindow; ++energy) {
+    
+    //Create the output
+    typename InputImageType::Pointer energyImage = InputImageType::New();
+    energyImage->SetRegions(input->GetLargestPossibleRegion());
+    typename InputImageType::IndexType startIndex;
+    startIndex[0] = 0;
+    startIndex[1] = 0;
+    startIndex[2] = 0;//energy*nbSlicePerEnergy;
+    typename InputImageType::SizeType regionSize;
+    regionSize[0] = input->GetLargestPossibleRegion().GetSize()[0];
+    regionSize[1] = input->GetLargestPossibleRegion().GetSize()[1];
+    regionSize[2] = nbSlicePerEnergy;
+    typename InputImageType::RegionType region;
+    region.SetSize(regionSize);
+    region.SetIndex(startIndex);
+    energyImage->SetRegions(region);
+    energyImage->Allocate();
+
+    //Create the iterator on the output
+    itk::ImageRegionIterator<InputImageType> imageOutputIterator(energyImage,energyImage->GetLargestPossibleRegion());
+
+    //Create the iterator on the region of the input
+    typename InputImageType::IndexType startIndexIterator;
+    startIndexIterator[0] = 0;
+    startIndexIterator[1] = 0;
+    startIndexIterator[2] = energy*nbSlicePerEnergy;
+    typename InputImageType::RegionType regionIterator;
+    regionIterator.SetSize(regionSize);
+    regionIterator.SetIndex(startIndexIterator);
+    itk::ImageRegionIterator<InputImageType> imageInputIterator(input,regionIterator);
+
+    //Copy the requested region
+    while(!imageInputIterator.IsAtEnd())
+    {
+      imageOutputIterator.Set(imageInputIterator.Get());
+
+      ++imageInputIterator;
+      ++imageOutputIterator;
+    }
+
+    // Output directory and filenames
+    typename WriterType::Pointer writer = WriterType::New();
+    writer->SetInput( energyImage );
+    writer->SetImageIO( gdcmIO );
+    typename ReaderType::FileNamesContainer fileNamesOutput;
+    std::string extension = NumberToString(energy) + ".dcm";
+    std::string filename_out = m_ArgsInfo.output_arg + extension;
+    fileNamesOutput.push_back(filename_out);
+    writer->SetFileNames( fileNamesOutput );
+    writer->SetMetaDataDictionaryArray(&outputArray);
+
+    // Write
+    try {
+      if (m_ArgsInfo.verbose_flag)
+        std::cout << writer << std::endl;
+      writer->Update();
+    } catch( itk::ExceptionObject & excp ) {
+      std::cerr << "Error: Exception thrown while writing the series!!" << std::endl;
+      std::cerr << excp << std::endl;
+    }
+
+    //Write sequence dicom tag with gdcm
+    gdcm::Reader reader;
+    reader.SetFileName( filename_out.c_str() );
+    reader.Read();
+    gdcm::File &fileOutput = reader.GetFile();
+    gdcm::DataSet &dsOutput = fileOutput.GetDataSet();
+    const unsigned int ptr_len = 42;
+    char *ptr = new char[ptr_len];
+    memset(ptr,0,ptr_len);
+    //Write rotation tag
+    // Create a Sequence
+    gdcm::SmartPointer<gdcm::SequenceOfItems> rotationSq = new gdcm::SequenceOfItems();
+    rotationSq->SetLengthToUndefined();
+    // Create a dataelement
+    gdcm::DataElement rotationDE( gdcm::Tag(0x54, 0x53) );
+    rotationDE.SetVR( gdcm::VR::US );
+    char essai = (char)nbRotation;
+    char *p_essai = &essai;
+    rotationDE.SetByteValue(p_essai, 1);
+    // Create an item
+    gdcm::Item rotationIt;
+    rotationIt.SetVLToUndefined();
+    gdcm::DataSet &rotationDS = rotationIt.GetNestedDataSet();
+    rotationDS.Insert(rotationDE);
+    rotationSq->AddItem(rotationIt);
+    // Insert sequence into data set
+    gdcm::DataElement rotationDEParent( gdcm::Tag(0x54, 0x52) );
+    rotationDEParent.SetVR(gdcm::VR::SQ);
+    rotationDEParent.SetValue(*rotationSq);
+    rotationDEParent.SetVLToUndefined();
+    //Write energy
+    gdcm::DataElement energyDEParent( gdcm::Tag(0x54, 0x12) );
+    energyDEParent.SetVR(gdcm::VR::SQ);
+    // Create a Sequence
+    gdcm::SmartPointer<gdcm::SequenceOfItems> energySq = new gdcm::SequenceOfItems();
+    energySq->SetLengthToUndefined();
+
+    //Read the caracteristics of this energy window : the name and the thresholds up and down
+    const gdcm::DataElement &seqWindow = dsInput.GetDataElement(gdcm::Tag(0x54, 0x12));
+    gdcm::SmartPointer<gdcm::SequenceOfItems> sqiWindow = seqWindow.GetValueAsSQ();
+    gdcm::Item &itemWindow = sqiWindow->GetItem(energy+1);
+    const gdcm::DataElement &deNameWindow = itemWindow.GetDataElement(gdcm::Tag(0x54, 0x18));
+    std::string nameWindow( deNameWindow.GetByteValue()->GetPointer(), deNameWindow.GetByteValue()->GetLength() );
+
+    const gdcm::DataElement &deThresholds = itemWindow.GetDataElement(gdcm::Tag(0x54, 0x13));
+    gdcm::SmartPointer<gdcm::SequenceOfItems> sqiThresholds = deThresholds.GetValueAsSQ();
+    for (unsigned int nbWindow=1; nbWindow <= sqiThresholds->GetNumberOfItems() ; ++nbWindow) {
+      gdcm::Item &itemThresholds = sqiThresholds->GetItem(nbWindow);
+      const gdcm::DataElement &deThresholdDown = itemThresholds.GetDataElement(gdcm::Tag(0x54, 0x14));
+      std::string p_StrThresholdDown( deThresholdDown.GetByteValue()->GetPointer(), deThresholdDown.GetByteValue()->GetLength() );
+      double thresholdDown = (double)atof(p_StrThresholdDown.c_str());
+      const gdcm::DataElement &deThresholdUp = itemThresholds.GetDataElement(gdcm::Tag(0x54, 0x15));
+      std::string p_StrThresholdUp( deThresholdUp.GetByteValue()->GetPointer(), deThresholdUp.GetByteValue()->GetLength() );
+      double thresholdUp = (double)atof(p_StrThresholdUp.c_str());
+
+      //Now write it !
+      gdcm::SmartPointer<gdcm::SequenceOfItems> energyThresholdSq = new gdcm::SequenceOfItems();
+      energyThresholdSq->SetLengthToUndefined();
+      // Create a dataelement
+      gdcm::DataElement energyThresholdDE( gdcm::Tag(0x54, 0x14) );
+      gdcm::DataElement energyUpholdDE( gdcm::Tag(0x54, 0x15) );
+      energyThresholdDE.SetVR( gdcm::VR::DS );
+      energyUpholdDE.SetVR( gdcm::VR::DS );
+      energyThresholdDE.SetByteValue(NumberToString(thresholdDown).c_str(), (uint32_t)strlen(NumberToString(thresholdDown).c_str()));
+      energyUpholdDE.SetByteValue(NumberToString(thresholdUp).c_str(), (uint32_t)strlen(NumberToString(thresholdUp).c_str()));
+      // Create an item
+      gdcm::Item energyThresholdIt;
+      energyThresholdIt.SetVLToUndefined();
+      gdcm::DataSet &energyThresholdDS = energyThresholdIt.GetNestedDataSet();
+      energyThresholdDS.Insert(energyThresholdDE);
+      energyThresholdDS.Insert(energyUpholdDE);
+      energyThresholdSq->AddItem(energyThresholdIt);
+      // Insert sequence into data set
+      gdcm::DataElement energyThresholdDEParent( gdcm::Tag(0x54, 0x13) );
+      energyThresholdDEParent.SetVR(gdcm::VR::SQ);
+      energyThresholdDEParent.SetValue(*energyThresholdSq);
+      energyThresholdDEParent.SetVLToUndefined();
+      // Create a dataelement
+      gdcm::DataElement energyNameDE( gdcm::Tag(0x54, 0x18) );
+      energyNameDE.SetVR( gdcm::VR::SH );
+      energyNameDE.SetByteValue(nameWindow.c_str(), (uint32_t)strlen(nameWindow.c_str()));
+      // Create an item
+      gdcm::Item energyIt;
+      energyIt.SetVLToUndefined();
+      gdcm::DataSet &energyDS = energyIt.GetNestedDataSet();
+      energyDS.Insert(energyNameDE);
+      energyDS.Insert(energyThresholdDEParent);
+      energySq->AddItem(energyIt);
+    }
+    // Insert sequence into data set
+    energyDEParent.SetValue(*energySq);
+    energyDEParent.SetVLToUndefined();
+    dsOutput.Insert(energyDEParent);
+    dsOutput.Insert(rotationDEParent);
+    gdcm::Writer w;
+    w.SetFile( fileOutput );
+    w.SetFileName( filename_out.c_str() );
+    w.Write();
+  }
+#else
+  std::cout << "Use GDCM2" << std::endl;
+#endif
+}
+//---------------------------------------------------------------------------
+
+
+//---------------------------------------------------------------------------
+void CopyDictionary (itk::MetaDataDictionary &fromDict, itk::MetaDataDictionary &toDict)
+{
+  typedef itk::MetaDataDictionary DictionaryType;
+
+  DictionaryType::ConstIterator itr = fromDict.Begin();
+  DictionaryType::ConstIterator end = fromDict.End();
+  typedef itk::MetaDataObject< std::string > MetaDataStringType;
+
+  while( itr != end )
+    {
+    itk::MetaDataObjectBase::Pointer  entry = itr->second;
+
+    MetaDataStringType::Pointer entryvalue =
+      dynamic_cast<MetaDataStringType *>( entry.GetPointer() ) ;
+    if( entryvalue )
+      {
+      std::string tagkey   = itr->first;
+      std::string tagvalue = entryvalue->GetMetaDataObjectValue();
+      itk::EncapsulateMetaData<std::string>(toDict, tagkey, tagvalue);
+      }
+    ++itr;
+    }
+}
+//---------------------------------------------------------------------------
+
+
+//---------------------------------------------------------------------------
+template <typename T> std::string NumberToString ( T Number )
+{
+   std::ostringstream ss;
+   ss << Number;
+   return ss.str();
+}
+//---------------------------------------------------------------------------
+
+}//end clitk
+
+#endif //#define clitkPartitionEnergyWindowDicomGenericFilter_txx
diff --git a/tools/clitkResampleImage.ggo b/tools/clitkResampleImage.ggo
deleted file mode 100644 (file)
index bee2e8e..0000000
+++ /dev/null
@@ -1,28 +0,0 @@
-#File clitkImageResample.ggo
-package "clitkResampleImage"
-version "2.0"
-purpose "Resample an image. You can specify the interpolation, you can apply a Gaussian filter before (automated if downsample)."
-
-section "Common"
-option "config"                -       "Config file"                        string     no
-option "verbose"       v       "Verbose"                            flag       off
-option "imagetypes"    -       "Verbose: allowed image types"       flag       off
-option "thread"        -       "Nb of thread to use (default=max)"  int        no
-
-section "Input & Output options"
-option "input"         i       "Input image filename"                  string  yes
-option "output"        o       "Output image filename"                 string  yes
-option "like"          l       "Resample like this image"              string  no
-option "size"          -       "Number of pixels of each coordonate"   int     no      multiple  default="0"
-option "spacing"       -       "Spacing in mm between pixels"          float   no      multiple  default="-1.0"
-option "default"       d       "Default pixel value"                   float   no      default = "0.0"
-option "time"           t       "Last Dimension Is Time -> do not resample it (auto on for 4D)" flag    off
-
-section "Interpolation"
-option "interp"                -       "Interpolation type: {nn, linear, bspline, blut}"  string  no default="nn"
-option "order"         b       "BSpline ordre (range 0-5)"                        int     no default="3"
-option "sampling"      s       "BLUT sampling value"                              int     no default="30"
-
-section "Gaussian filtering"
-option "gauss"         g       "Apply Gaussian before (sigma in mm)"             float  no     multiple  default="0.0"
-option "autogauss"     a       "Apply Gaussian with auto sigma when downsample"  flag   off    
diff --git a/tools/clitkResampleImageGenericFilter.cxx b/tools/clitkResampleImageGenericFilter.cxx
deleted file mode 100644 (file)
index b33e63e..0000000
+++ /dev/null
@@ -1,58 +0,0 @@
-/*=========================================================================
-  Program:   vv                     http://www.creatis.insa-lyon.fr/rio/vv
-
-  Authors belong to:
-  - University of LYON              http://www.universite-lyon.fr/
-  - Léon Bérard cancer center       http://www.centreleonberard.fr
-  - CREATIS CNRS laboratory         http://www.creatis.insa-lyon.fr
-
-  This software is distributed WITHOUT ANY WARRANTY; without even
-  the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
-  PURPOSE.  See the copyright notices for more information.
-
-  It is distributed under dual licence
-
-  - BSD        See included LICENSE.txt file
-  - CeCILL-B   http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.html
-  ===========================================================================**/
-
-// clitk
-#include "clitkResampleImageGenericFilter.h"
-#include "clitkResampleImageWithOptionsFilter.h"
-
-//--------------------------------------------------------------------
-clitk::ResampleImageGenericFilter::ResampleImageGenericFilter():
-  ImageToImageGenericFilter<Self>("Resample") 
-{
-  InitializeImageType<2>();
-  InitializeImageType<3>();
-  InitializeImageType<4>();
-}
-//--------------------------------------------------------------------
-
-
-//--------------------------------------------------------------------
-template<unsigned int Dim>
-void clitk::ResampleImageGenericFilter::InitializeImageType() 
-{      
-  ADD_DEFAULT_IMAGE_TYPES(Dim);
-  ADD_IMAGE_TYPE(Dim, short);
-}
-//--------------------------------------------------------------------
-  
-
-//--------------------------------------------------------------------
-void clitk::ResampleImageGenericFilter::SetArgsInfo(const ArgsInfoType & a) 
-{
-  mArgsInfo=a;
-  if (mArgsInfo.imagetypes_flag) this->PrintAvailableImageTypes();
-  SetIOVerbose(mArgsInfo.verbose_flag);
-  if (mArgsInfo.input_given) {
-    SetInputFilename(mArgsInfo.input_arg);
-  }
-  if (mArgsInfo.output_given) {
-    SetOutputFilename(mArgsInfo.output_arg);
-  }
-}
-//--------------------------------------------------------------------
-
diff --git a/tools/clitkResampleImageGenericFilter.h b/tools/clitkResampleImageGenericFilter.h
deleted file mode 100644 (file)
index d598dcf..0000000
+++ /dev/null
@@ -1,66 +0,0 @@
-/*=========================================================================
-  Program:   vv                     http://www.creatis.insa-lyon.fr/rio/vv
-
-  Authors belong to: 
-  - University of LYON              http://www.universite-lyon.fr/
-  - Léon Bérard cancer center       http://www.centreleonberard.fr
-  - CREATIS CNRS laboratory         http://www.creatis.insa-lyon.fr
-
-  This software is distributed WITHOUT ANY WARRANTY; without even
-  the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
-  PURPOSE.  See the copyright notices for more information.
-
-  It is distributed under dual licence
-
-  - BSD        See included LICENSE.txt file
-  - CeCILL-B   http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.html
-===========================================================================**/
-
-#ifndef CLITKIRESAMPLEIMAGEGENERICFILTER_H
-#define CLITKIRESAMPLEIMAGEGENERICFILTER_H
-
-// clitk include
-#include "clitkResampleImage_ggo.h"
-#include "clitkImageToImageGenericFilter.h"
-
-namespace clitk {
-  
-  //--------------------------------------------------------------------
-  class ITK_EXPORT ResampleImageGenericFilter: 
-    public ImageToImageGenericFilter<ResampleImageGenericFilter> {
-    
-  public: 
-    // Constructor
-    ResampleImageGenericFilter();
-
-    // Types
-    typedef ResampleImageGenericFilter    Self;
-    typedef itk::SmartPointer<Self>       Pointer;
-    typedef itk::SmartPointer<const Self> ConstPointer;
-    typedef args_info_clitkResampleImage  ArgsInfoType;
-
-    // New
-    itkNewMacro(Self);
-
-    // Args
-    void SetArgsInfo(const ArgsInfoType & a);
-    
-    // Main function
-    template<class InputImageType> void UpdateWithInputImageType();
-
-  protected:
-    ArgsInfoType mArgsInfo;
-    template<unsigned int Dim> void InitializeImageType();
-     
-  }; // end class ResampleImageGenericFilter
-  //--------------------------------------------------------------------
-    
-} // end namespace clitk
-//--------------------------------------------------------------------
-
-#ifndef ITK_MANUAL_INSTANTIATION
-#include "clitkResampleImageGenericFilter.txx"
-#endif
-
-#endif /* end #define CLITKIRESAMPLEIMAGEGENERICFILTER_H */
-
diff --git a/tools/clitkResampleImageGenericFilter.txx b/tools/clitkResampleImageGenericFilter.txx
deleted file mode 100644 (file)
index 7ecf842..0000000
+++ /dev/null
@@ -1,171 +0,0 @@
-/*=========================================================================
-  Program:   vv                     http://www.creatis.insa-lyon.fr/rio/vv
-
-  Authors belong to:
-  - University of LYON              http://www.universite-lyon.fr/
-  - Léon Bérard cancer center       http://www.centreleonberard.fr
-  - CREATIS CNRS laboratory         http://www.creatis.insa-lyon.fr
-
-  This software is distributed WITHOUT ANY WARRANTY; without even
-  the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
-  PURPOSE.  See the copyright notices for more information.
-
-  It is distributed under dual licence
-
-  - BSD        See included LICENSE.txt file
-  - CeCILL-B   http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.html
-  ===========================================================================**/
-
-#ifndef CLITKRESAMPLEIMAGEGENERICFILTER_TXX
-#define CLITKRESAMPLEIMAGEGENERICFILTER_TXX
-
-// clitk
-#include "clitkResampleImageWithOptionsFilter.h"
-
-//--------------------------------------------------------------------
-// Update with the number of dimensions and the pixeltype
-//--------------------------------------------------------------------
-template<class InputImageType>
-void
-clitk::ResampleImageGenericFilter::UpdateWithInputImageType()
-{
-
-  // Reading input
-  typename InputImageType::Pointer input = this->template GetInput<InputImageType>(0);
-
-  // Main filter
-  typedef typename InputImageType::PixelType PixelType;
-  typedef InputImageType OutputImageType; // to change to float is user ask it (?)
-
-  // Filter
-  typedef clitk::ResampleImageWithOptionsFilter<InputImageType, OutputImageType> ResampleImageFilterType;
-  typename ResampleImageFilterType::Pointer filter = ResampleImageFilterType::New();
-  filter->SetInput(input);
-
-  // Set Verbose
-  filter->SetVerboseOptions(mArgsInfo.verbose_flag);
-
-  // Set size / spacing
-  static const unsigned int dim = OutputImageType::ImageDimension;
-  typename OutputImageType::SpacingType spacing;
-  typename OutputImageType::SizeType size;
-  typename OutputImageType::PointType origin;
-  typename OutputImageType::DirectionType direction;
-
-  if (mArgsInfo.like_given) {
-    itk::ImageIOBase::Pointer header = clitk::readImageHeader(mArgsInfo.like_arg);
-    if (header) {
-      for(unsigned int i=0; i<dim; i++){
-        spacing[i] = header->GetSpacing(i);
-        size[i] = header->GetDimensions(i);
-        origin[i] = header->GetOrigin(i);
-      }
-      for(unsigned int i=0; i<dim; i++) {
-        for(unsigned int j=0;j<dim;j++) {
-            direction(i,j) = header->GetDirection(i)[j];
-        }
-      }
-      filter->SetOutputSpacing(spacing);
-      filter->SetOutputSize(size);
-      filter->SetOutputOrigin(origin);
-      filter->SetOutputDirection(direction);
-    }
-    else {
-      std::cerr << "*** Warning : I could not read '" << mArgsInfo.like_arg << "' ***" << std::endl;
-      exit(0);
-    }
-  }
-  else {
-    if (mArgsInfo.spacing_given == 1) {
-      filter->SetOutputIsoSpacing(mArgsInfo.spacing_arg[0]);
-    }
-    else if ((mArgsInfo.spacing_given != 0) && (mArgsInfo.size_given != 0)) {
-      std::cerr << "Error: use spacing or size, not both." << std::endl;
-      exit(0);
-    }
-    else if (mArgsInfo.spacing_given) {
-      if ((mArgsInfo.spacing_given != 0) && (mArgsInfo.spacing_given != dim)) {
-        std::cerr << "Error: spacing should have one or " << dim << " values." << std::endl;
-        exit(0);
-      }
-      for(unsigned int i=0; i<dim; i++)
-        spacing[i] = mArgsInfo.spacing_arg[i];
-      filter->SetOutputSpacing(spacing);
-    }
-    else if (mArgsInfo.size_given) {
-      if ((mArgsInfo.size_given != 0) && (mArgsInfo.size_given != dim)) {
-        std::cerr << "Error: size should have " << dim << " values." << std::endl;
-        exit(0);
-      }
-      for(unsigned int i=0; i<dim; i++)
-        size[i] = mArgsInfo.size_arg[i];
-      filter->SetOutputSize(size);
-    }
-    for(unsigned int i=0; i<dim; i++){
-      origin[i] = input->GetOrigin()[i];
-    }
-    for(unsigned int i=0; i<dim; i++) {
-      for(unsigned int j=0;j<dim;j++) {
-          direction(i,j) = input->GetDirection()[i][j];
-      }
-    }
-    filter->SetOutputOrigin(origin);
-    filter->SetOutputDirection(direction);
-  }
-
-  // Set temporal dimension
-  filter->SetLastDimensionIsTime(mArgsInfo.time_flag);
-
-  // Set Gauss
-  filter->SetGaussianFilteringEnabled(mArgsInfo.autogauss_flag);
-  if (mArgsInfo.gauss_given != 0) {
-    typename ResampleImageFilterType::GaussianSigmaType g;
-    for(unsigned int i=0; i<dim; i++) {
-      g[i] = mArgsInfo.gauss_arg[i];
-    }
-    filter->SetGaussianSigma(g);
-  }
-
-  // Set Interpolation
-  std::string interp = std::string(mArgsInfo.interp_arg);
-  if (interp == "nn") {
-    filter->SetInterpolationType(ResampleImageFilterType::NearestNeighbor);
-  } else {
-    if (interp == "linear") {
-      filter->SetInterpolationType(ResampleImageFilterType::Linear);
-    } else {
-      if (interp == "bspline") {
-        filter->SetInterpolationType(ResampleImageFilterType::BSpline);
-      } else {
-        if (interp == "blut") {
-          filter->SetInterpolationType(ResampleImageFilterType::B_LUT);
-        } else {
-          if (interp == "windowed sinc") {
-            filter->SetInterpolationType(ResampleImageFilterType::WSINC);
-          } else {
-            std::cerr << "Error. I do not know interpolation '" << mArgsInfo.interp_arg
-                      << "'. Choose among: nn, linear, bspline, blut, windowed sinc" << std::endl;
-            exit(0);
-          }
-        }
-      }
-    }
-  }
-
-  // Set default pixel value
-  filter->SetDefaultPixelValue(mArgsInfo.default_arg);
-
-  // Set thread
-  if (mArgsInfo.thread_given) {
-    filter->SetNumberOfThreads(mArgsInfo.thread_arg);
-  }
-
-  // Go !
-  filter->Update();
-  typename OutputImageType::Pointer outputImage = filter->GetOutput();
-  this->template SetNextOutput<OutputImageType>(outputImage);
-}
-//--------------------------------------------------------------------
-
-#endif /* end #define CLITKRESAMPLEIMAGEGENERICFILTER_TXX */
-
index a73d3080e359d06eb462b26b589d32974b789334..58732ab27af434a5f5e2a4b9996bc173347953b4 100644 (file)
@@ -54,7 +54,7 @@ clitk::SplitImageGenericFilter::PngConversion<ImageType>::Do(double window,
                                                              ImagePointer input)
 {
   static const unsigned int PixelDimension = itk::PixelTraits<typename ImageType::PixelType>::Dimension;
-  return this->Do(window, level, input, static_cast< PixelDimType<PixelDimension> *>(NULL) );
+  return this->Do(window, level, input, static_cast< PixelDimType<PixelDimension> *>(ITK_NULLPTR) );
 }
 //--------------------------------------------------------------------
 
@@ -68,7 +68,7 @@ clitk::SplitImageGenericFilter::PngConversion<ImageType>::Do(double window,
                                                              PixelDimType<Dim> *)
 {
   clitkExceptionMacro("Png conversion is not implemented for vector fields");
-  return NULL;
+  return ITK_NULLPTR;
 }
 //--------------------------------------------------------------------
 
index d2a867243a8317e4b0d3798b42e2aaff39c413c2..d809ad025c241894107df8675f459079856e3744 100644 (file)
@@ -1,7 +1,7 @@
 #File clitkUnsharpMask.ggo
 package "clitkUnsharpMask"
 version "1.0"
-purpose ""
+purpose "Image sharpening technique. It computes Image - blurred(Image). Here blurred(Image) is the output of a gaussian filtering with a sigma measured in world coordinates"
 
 option "config"                -       "Config file"                     string        no
 option "verbose"       v       "Verbose"                         flag          off
diff --git a/tools/clitkUpdateVRTagDicom.cxx b/tools/clitkUpdateVRTagDicom.cxx
new file mode 100644 (file)
index 0000000..20ea281
--- /dev/null
@@ -0,0 +1,53 @@
+/*=========================================================================
+  Program:   vv                     http://www.creatis.insa-lyon.fr/rio/vv
+
+  Authors belong to:
+  - University of LYON              http://www.universite-lyon.fr/
+  - Léon Bérard cancer center       http://www.centreleonberard.fr
+  - CREATIS CNRS laboratory         http://www.creatis.insa-lyon.fr
+
+  This software is distributed WITHOUT ANY WARRANTY; without even
+  the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+  PURPOSE.  See the copyright notices for more information.
+
+  It is distributed under dual licence
+
+  - BSD        See included LICENSE.txt file
+  - CeCILL-B   http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.html
+===========================================================================**/
+
+/* =================================================
+ * @file   clitkUpdateVRTagDicom.cxx
+ * @author Jef Vandemeulebroucke
+ * @date   4th of August
+ *
+ * @brief Write a volume into a dicom with the header of another dicom
+ *
+ ===================================================*/
+
+
+// clitk
+#include "clitkUpdateVRTagDicom_ggo.h"
+#include "clitkIO.h"
+#include "clitkUpdateVRTagDicomGenericFilter.h"
+#include "clitkCommon.h"
+
+//--------------------------------------------------------------------
+int main(int argc, char * argv[])
+{
+
+  // Init command line
+  GGO(clitkUpdateVRTagDicom, args_info);
+  CLITK_INIT;
+
+  // Filter
+  typedef clitk::UpdateVRTagDicomGenericFilter<args_info_clitkUpdateVRTagDicom> FilterType;
+  FilterType::Pointer genericFilter = FilterType::New();
+
+  genericFilter->SetArgsInfo(args_info);
+  genericFilter->Update();
+
+  return EXIT_SUCCESS;
+}// end main
+
+//--------------------------------------------------------------------
diff --git a/tools/clitkUpdateVRTagDicom.ggo b/tools/clitkUpdateVRTagDicom.ggo
new file mode 100644 (file)
index 0000000..7f518ca
--- /dev/null
@@ -0,0 +1,11 @@
+#File clitkUpdateVRTagDicom.ggo
+package "clitkUpdateVRTagDicom"
+version "1.0"
+purpose "Fix VR Dicom tag mistakes with gdcm"
+
+option "config"          - "Config file"                 string  no
+option "verbose"  v "Verbose"                     flag    off
+
+option "input"    i "Input Dicom image filename"  string  yes
+option "output"   o "Output dicom filename"       string  yes
+option "model"    m "Model dicom filename"        string  yes
diff --git a/tools/clitkUpdateVRTagDicomGenericFilter.cxx b/tools/clitkUpdateVRTagDicomGenericFilter.cxx
new file mode 100644 (file)
index 0000000..89b1a17
--- /dev/null
@@ -0,0 +1,39 @@
+/*=========================================================================
+  Program:   vv                     http://www.creatis.insa-lyon.fr/rio/vv
+
+  Authors belong to:
+  - University of LYON              http://www.universite-lyon.fr/
+  - Léon Bérard cancer center       http://www.centreleonberard.fr
+  - CREATIS CNRS laboratory         http://www.creatis.insa-lyon.fr
+
+  This software is distributed WITHOUT ANY WARRANTY; without even
+  the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+  PURPOSE.  See the copyright notices for more information.
+
+  It is distributed under dual licence
+
+  - BSD        See included LICENSE.txt file
+  - CeCILL-B   http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.html
+===========================================================================**/
+#ifndef clitkUpdateVRTagDicomGenericFilter_cxx
+#define clitkUpdateVRTagDicomGenericFilter_cxx
+
+/* =================================================
+ * @file   clitkUpdateVRTagDicomGenericFilter.cxx
+ * @author
+ * @date
+ *
+ * @brief
+ *
+ ===================================================*/
+
+#include "clitkUpdateVRTagDicomGenericFilter.h"
+
+
+namespace clitk
+{
+
+
+} //end clitk
+
+#endif  //#define clitkUpdateVRTagDicomGenericFilter_cxx
diff --git a/tools/clitkUpdateVRTagDicomGenericFilter.h b/tools/clitkUpdateVRTagDicomGenericFilter.h
new file mode 100644 (file)
index 0000000..2864055
--- /dev/null
@@ -0,0 +1,176 @@
+/*=========================================================================
+  Program:   vv                     http://www.creatis.insa-lyon.fr/rio/vv
+
+  Authors belong to: 
+  - University of LYON              http://www.universite-lyon.fr/
+  - Léon Bérard cancer center       http://www.centreleonberard.fr
+  - CREATIS CNRS laboratory         http://www.creatis.insa-lyon.fr
+
+  This software is distributed WITHOUT ANY WARRANTY; without even
+  the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+  PURPOSE.  See the copyright notices for more information.
+
+  It is distributed under dual licence
+
+  - BSD        See included LICENSE.txt file
+  - CeCILL-B   http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.html
+===========================================================================**/
+#ifndef clitkUpdateVRTagDicomGenericFilter_h
+#define clitkUpdateVRTagDicomGenericFilter_h
+
+/* =================================================
+ * @file   clitkUpdateVRTagDicomGenericFilter.h
+ * @author 
+ * @date   
+ * 
+ * @brief 
+ * 
+ ===================================================*/
+
+
+// clitk include
+#include "clitkIO.h"
+#include "clitkImageCommon.h"
+#include "clitkUpdateVRTagDicom_ggo.h"
+
+//itk include
+#include "itkLightObject.h"
+#include "itkGDCMImageIO.h"
+#include "itkMetaDataDictionary.h"
+#include "itkGDCMSeriesFileNames.h"
+#include "itkImageSeriesReader.h"
+#include "itkImageSeriesWriter.h"
+#include "itkImageFileReader.h"
+#include "itkImageFileWriter.h"
+#include <vector>
+#include <itksys/SystemTools.hxx>
+enum valrep_t {VR_AE,
+                                VR_AS,
+                                VR_AT,
+                                VR_CS,
+                                VR_DA,
+                                VR_DS,
+                                VR_DT,
+                                VR_FL,
+                                VR_FD,
+                                VR_IS,
+                                VR_LO,
+                                VR_LT,
+                                VR_OB,
+                                VR_OF,
+                                VR_OW,
+                                VR_PN,
+                                VR_SH,
+                                VR_SL,
+                                VR_SQ,
+                                VR_SS,
+                                VR_ST,
+                                VR_TM,
+                                VR_UI,
+                                VR_UL,
+                                VR_UN,
+                                VR_US,
+                                VR_UT};
+namespace clitk 
+{
+  template<class args_info_type>
+  class ITK_EXPORT UpdateVRTagDicomGenericFilter : public itk::LightObject
+  {
+  public:
+    //----------------------------------------
+    // ITK
+    //----------------------------------------
+    typedef UpdateVRTagDicomGenericFilter   Self;
+    typedef itk::LightObject                          Superclass;
+    typedef itk::SmartPointer<Self>                   Pointer;
+    typedef itk::SmartPointer<const Self>             ConstPointer;
+   
+    // Method for creation through the object factory
+    itkNewMacro(Self);
+
+    // Run-time type information (and related methods)
+    itkTypeMacro( UpdateVRTagDicomGenericFilter, LightObject );
+
+
+    //----------------------------------------
+    // Typedefs
+    //----------------------------------------
+
+
+    //----------------------------------------
+    // Set & Get
+    //----------------------------------------
+    void SetArgsInfo(const args_info_type & a)
+    {
+      m_ArgsInfo=a;
+      m_Verbose=m_ArgsInfo.verbose_flag;
+      m_InputFileName=m_ArgsInfo.input_arg;
+    }
+
+
+    //----------------------------------------
+    // Update
+    //----------------------------------------
+    void Update();
+
+  protected:
+
+    //----------------------------------------
+    // Constructor & Destructor
+    //----------------------------------------
+    UpdateVRTagDicomGenericFilter();
+    ~UpdateVRTagDicomGenericFilter() {};
+
+
+    //----------------------------------------
+    // Templated members
+    //----------------------------------------
+    template <unsigned int Dimension>  void UpdateWithDim(std::string PixelType);
+    template <unsigned int Dimension, class PixelType> void UpdateWithDimAndPixelType();
+    template <unsigned int Dimension, class PixelType> void ReadDataElement(std::ifstream& f, std::ifstream& model);
+    template <unsigned int Dimension, class PixelType> void ReadDicomObject(std::ifstream& f, std::ifstream& model);
+    template <unsigned int Dimension, class PixelType> void InitVRMap();
+    template <unsigned int Dimension, class PixelType> void UpdateVRMap();
+
+
+    //----------------------------------------
+    // Data members
+    //----------------------------------------
+    args_info_type m_ArgsInfo;
+    bool m_Verbose;
+    std::string m_InputFileName;
+    int counter;
+    uint16_t Group;
+    uint16_t Element;
+    std::string VR;
+    uint32_t Length;
+    uint32_t LengthModel;
+    char* Value;
+    char* ValueModel;
+    std::map<std::string, valrep_t> VRMap;
+    std::map<std::string, std::string> VRMapModel;
+
+    std::ofstream output;
+    std::ifstream f;
+    std::ifstream model;
+
+    unsigned long size;
+    unsigned long endSQ;
+
+  };
+
+//Copy dicom dictionary
+void CopyDictionary (itk::MetaDataDictionary &fromDict, itk::MetaDataDictionary &toDict);
+
+//convert to std::string
+template <typename T> std::string NumberToString ( T Number );
+template <typename T> std::string int_to_hex ( T Number );
+std::string string_to_hex( std::string &input );
+
+} // end namespace clitk
+
+#ifndef ITK_MANUAL_INSTANTIATION
+#include "clitkUpdateVRTagDicomGenericFilter.txx"
+#endif
+
+#endif // #define clitkUpdateVRTagDicomGenericFilter_h
diff --git a/tools/clitkUpdateVRTagDicomGenericFilter.txx b/tools/clitkUpdateVRTagDicomGenericFilter.txx
new file mode 100644 (file)
index 0000000..fd47485
--- /dev/null
@@ -0,0 +1,863 @@
+/*=========================================================================
+  Program:   vv                     http://www.creatis.insa-lyon.fr/rio/vv
+
+  Authors belong to:
+  - University of LYON              http://www.universite-lyon.fr/
+  - Léon Bérard cancer center       http://www.centreleonberard.fr
+  - CREATIS CNRS laboratory         http://www.creatis.insa-lyon.fr
+
+  This software is distributed WITHOUT ANY WARRANTY; without even
+  the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+  PURPOSE.  See the copyright notices for more information.
+
+  It is distributed under dual licence
+
+  - BSD        See included LICENSE.txt file
+  - CeCILL-B   http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.html
+===========================================================================**/
+#ifndef clitkUpdateVRTagDicomGenericFilter_txx
+#define clitkUpdateVRTagDicomGenericFilter_txx
+
+/* =================================================
+ * @file   clitkUpdateVRTagDicomGenericFilter.txx
+ * @author
+ * @date
+ *
+ * @brief
+ *
+ ===================================================*/
+
+#include <sstream>
+// clitk
+#include "clitkResampleImageWithOptionsFilter.h"
+#if GDCM_MAJOR_VERSION >= 2
+#include "gdcmUIDGenerator.h"
+#include <gdcmImageHelper.h>
+#include <gdcmAttribute.h>
+#include <gdcmReader.h>
+#include <gdcmWriter.h>
+#include <gdcmDataElement.h>
+#include <gdcmTag.h>
+#include <gdcmDict.h>
+#include <gdcmDicts.h>
+#include <gdcmGlobal.h>
+#else
+#include "gdcmFile.h"
+#include "gdcmUtil.h"
+#endif
+
+#include "itkImageRegionIterator.h"
+#include "itkMetaImageIO.h"
+#include "itkMetaDataDictionary.h"
+
+namespace clitk
+{
+
+
+//-----------------------------------------------------------
+// Constructor
+//-----------------------------------------------------------
+template<class args_info_type>
+UpdateVRTagDicomGenericFilter<args_info_type>::UpdateVRTagDicomGenericFilter()
+{
+  m_Verbose=false;
+  m_InputFileName="";
+}
+
+
+//-----------------------------------------------------------
+// Update
+//-----------------------------------------------------------
+template<class args_info_type>
+void UpdateVRTagDicomGenericFilter<args_info_type>::Update()
+{
+  // Read the Dimension and PixelType
+  int Dimension;
+  std::string PixelType;
+  ReadImageDimensionAndPixelType(m_InputFileName, Dimension, PixelType);
+
+
+  // Call UpdateWithDim
+  if(Dimension==2) UpdateWithDim<2>(PixelType);
+  else if(Dimension==3) UpdateWithDim<3>(PixelType);
+  // else if (Dimension==4)UpdateWithDim<4>(PixelType);
+  else {
+    std::cout<<"Error, Only for 2 or 3  Dimensions!!!"<<std::endl ;
+    return;
+  }
+}
+
+//-------------------------------------------------------------------
+// Update with the number of dimensions
+//-------------------------------------------------------------------
+template<class args_info_type>
+template<unsigned int Dimension>
+void
+UpdateVRTagDicomGenericFilter<args_info_type>::UpdateWithDim(std::string PixelType)
+{
+  if (m_Verbose) std::cout << "Image was detected to be "<<Dimension<<"D and "<< PixelType<<"..."<<std::endl;
+
+  if(PixelType == "short") {
+    if (m_Verbose) std::cout << "Launching filter in "<< Dimension <<"D and signed short..." << std::endl;
+    UpdateWithDimAndPixelType<Dimension, signed short>();
+  }
+  else if(PixelType == "unsigned_short"){
+    if (m_Verbose) std::cout  << "Launching filter in "<< Dimension <<"D and unsigned_short..." << std::endl;
+    UpdateWithDimAndPixelType<Dimension, unsigned short>();
+  }
+
+  else if (PixelType == "unsigned_char") {
+    if (m_Verbose) std::cout  << "Launching filter in "<< Dimension <<"D and unsigned_char..." << std::endl;
+    UpdateWithDimAndPixelType<Dimension, unsigned char>();
+  }
+
+  //     else if (PixelType == "char"){
+  //       if (m_Verbose) std::cout  << "Launching filter in "<< Dimension <<"D and signed_char..." << std::endl;
+  //       UpdateWithDimAndPixelType<Dimension, signed char>();
+  //     }
+  else if (PixelType == "double") {
+    if (m_Verbose) std::cout  << "Launching filter in "<< Dimension <<"D and double..." << std::endl;
+    UpdateWithDimAndPixelType<Dimension, double>();
+  }
+  else {
+    if (m_Verbose) std::cout  << "Launching filter in "<< Dimension <<"D and float..." << std::endl;
+    UpdateWithDimAndPixelType<Dimension, float>();
+  }
+}
+
+//-------------------------------------------------------------------
+// Update with the number of dimensions and the pixeltype read from
+// the dicom files. The MHD files may be resampled to match the
+// dicom spacing (and number of slices). Rounding errors in resampling
+// are handled by removing files when generating the output dicom
+// series.
+//-------------------------------------------------------------------
+template<class args_info_type>
+template <unsigned int Dimension, class  PixelType>
+void
+UpdateVRTagDicomGenericFilter<args_info_type>::UpdateWithDimAndPixelType()
+{
+  UpdateVRMap<Dimension,PixelType>();
+  InitVRMap<Dimension,PixelType>();
+  char temp[128];
+  f.open(m_ArgsInfo.input_arg,std::fstream::binary);
+  model.open(m_ArgsInfo.model_arg,std::fstream::binary);
+  output.open(m_ArgsInfo.output_arg,std::fstream::binary);
+
+  if(!f.is_open())
+  {
+    std::cout << "File " << m_ArgsInfo.input_arg << " not found." << std::endl;
+    return;
+  }
+  f.seekg (0);
+  model.seekg (0);
+  f.read(temp,128); // first 128 bytes in a DICOM file are unused
+  model.read(temp,128); // first 128 bytes in a DICOM file are unused
+  output.write(temp,128);
+  f.read(temp,4);
+  // verify that the first 4 bytes are DICM
+  if(std::memcmp(temp,"DICM",4))
+  {
+    std::cout << "Not a valid DICOM file!" << std::endl;
+    f.close();
+    return;
+  }
+  model.read(temp,4);
+  output.write(temp,4);
+  counter = 0;
+  size = 0;
+  endSQ = 0;
+
+  while(f.peek() != EOF) // process the entire file
+  {
+    this->ReadDataElement<Dimension,PixelType>(f, model);
+  }
+
+  f.close();
+  model.close();
+  output.close();
+}
+//---------------------------------------------------------------------------
+
+
+//---------------------------------------------------------------------------
+template<class args_info_type>
+template <unsigned int Dimension, class  PixelType>
+void
+UpdateVRTagDicomGenericFilter<args_info_type>::ReadDataElement(std::ifstream& f, std::ifstream& model)
+{
+
+  char temp[8] = {0,0,0,0,0,0,0,0};
+  unsigned char SQEndTag[] = {0xFE, 0xFF, 0xDD, 0xE0};
+
+  //Use to convert the output of clitkWriteDicomSeries to the a specific dicom format with the VR tag.
+  //Read the input file f and write it in the output.
+  //Except for the first 7 lines, we copy the model
+  //Except for the 8th line, we remove it.
+  //Except for the lines with an unknow VRTag (??)
+  //While copy, include the 2 letters of the VRTag
+  //For SQ, I had to count the number of byte with size and endSQ
+
+  /*while (f) {
+    uint32_t tempChar(0);
+    f.read((char*)&tempChar,1);
+    std::cout << tempChar << std::endl;
+  }
+  return;*/
+
+  f.read((char*)&Group,2);
+  if (counter <7) model.read((char*)&Group,2);
+  f.read((char*)&Element,2);
+  if (counter <7) model.read((char*)&Element,2);
+
+  if (counter <7) {
+    output.write((char*)&Group,2);
+    output.write((char*)&Element,2);
+    f.read(temp,2);
+    model.read(temp,2);
+    output.write(temp,2);
+    VR = temp;
+  }
+  else if (counter == 7) {
+    f.read(temp,2);
+    VR = temp;
+  }
+  else {
+    std::string VRtag = "(";
+    VRtag += int_to_hex(Group);
+    std::string tempVR = ",";
+    VRtag += tempVR;
+    VRtag += int_to_hex(Element);
+    tempVR = ")";
+    VRtag += tempVR;
+    VR = VRMapModel[VRtag];
+    if (VR == "??") {
+      f.read(temp,4);
+      return;
+    }
+    output.write((char*)&Group,2);
+    output.write((char*)&Element,2);
+    output.write((char*)&VR[0],1); // Ecrire les 2 lettre separemments en les convertissant en ASCII puis hexa
+    output.write((char*)&VR[1],1);
+  }
+  Length = 0;
+  LengthModel = 0;
+  switch(VRMap[VR])
+  {
+    case VR_OB:                // if VR is OB, OW, OF, SQ, UT, or UN, skip two bytes, then read 4 byte length
+    case VR_OF:
+    case VR_UT:
+    case VR_UN:
+      f.read(temp,2);
+      if (counter <7) model.read(temp,2);
+      output.write(temp,2);
+      f.read((char*)&Length,4);
+      if (counter <7) model.read((char*)&LengthModel,4);
+      if (counter <7) output.write((char*)&LengthModel,4); else output.write((char*)&Length,4);
+      size += Length + 12;
+      break;
+    case VR_SQ:
+      Value = NULL;
+      temp[0] = 0;
+      temp[1] = 0;
+      output.write(temp,2);
+      f.read((char*)&Length,4);
+      output.write((char*)&Length,4);
+      // this is where we'll be creating new Dicom Objects for SQ data
+      // for each Dicom Object in the sequence
+      if(Length==0xFFFFFFFF)
+      {
+        // check the tag before passing to the DicomObj creator
+        f.read(temp,4);
+        while(std::memcmp(temp,SQEndTag,4))
+        {
+          f.seekg(-4,std::ios::cur);
+          ++endSQ;
+          ReadDicomObject<Dimension,PixelType>(f, model);
+          --endSQ;
+          f.read(temp,4);
+        }
+        // skip the next four bytes and continue
+        output.write(temp,4);
+        f.read(temp,4);
+        output.write(temp,4);
+        size += 20;
+      }
+      else
+      {
+        while(size < Length)
+        {
+          ++endSQ;
+          ReadDicomObject<Dimension,PixelType>(f, model);
+          --endSQ;
+        }
+        size += Length + 12;
+      }
+      if (!endSQ)
+        size = 0;
+      return;
+    case VR_OW:
+      f.read(temp,1);
+      output.write(temp,1);
+      temp[0] = 0;
+      temp[1] = 0;
+      output.write(temp,2);
+      f.read((char*)&Length,4);
+      output.write((char*)&Length,4);
+      output.write(temp,2);
+      size += Length + 12;
+    default:
+      f.read((char*)&Length,2);
+      if (counter <7) model.read((char*)&LengthModel,2);
+      if (counter <7) output.write((char*)&LengthModel,2); else if (counter > 7) output.write((char*)&Length,2);
+      size += Length + 8;
+      break;
+  }
+  if (counter >=8) {
+    f.read(temp,2);
+  }
+  if(Length)
+  {
+    Value = new char[Length];
+    ValueModel = new char[LengthModel];
+    if(Value)
+    {
+      f.read(Value,Length);
+      if (counter <7) model.read(ValueModel,LengthModel);
+      if (counter <7) output.write(ValueModel,LengthModel); else if (counter > 7) output.write(Value,Length);
+    }
+    else
+      std::cout << "Error: unable to allocate memory for data element" << std::endl;
+  }
+  else
+    Value = NULL;
+
+  counter++;
+  if (!endSQ)
+    size = 0;
+
+  return;
+}
+//---------------------------------------------------------------------------
+
+
+
+//---------------------------------------------------------------------------
+template<class args_info_type>
+template <unsigned int Dimension, class  PixelType>
+void
+UpdateVRTagDicomGenericFilter<args_info_type>::ReadDicomObject(std::ifstream& f, std::ifstream& model)
+{
+  char temp[4];
+  const unsigned char DicomObjTag[4] = {0xFE,0xFF,0x00,0xE0}; //start of item
+  const unsigned char ItemEndTag[4] = {0xFE,0xFF,0x0D,0xE0}; //end of item
+
+
+  // read tags
+  f.read(temp,4);
+  output.write(temp,4);
+  if(std::memcmp(temp,DicomObjTag,4))
+    std::cout << "Nested Dicom Object Tag error\n";
+  // read ObjLength
+  f.read((char*)&Length,4);
+  output.write((char*)&Length,4);
+
+  if(Length == 0xFFFFFFFF)
+  {
+    // create data elements until an FFFE,E00D tag
+    f.read(temp,4);
+    while(std::memcmp(temp, ItemEndTag,4))
+    {
+      f.seekg(-4,std::ios::cur);
+      ++endSQ;
+      ReadDataElement<Dimension,PixelType>(f, model);
+      --endSQ;
+      f.read(temp,4);
+    }
+    output.write(temp,4);
+    f.read(temp,4);
+    output.write(temp,4);
+    size += 16;
+  }
+  else
+  {
+    while(size < Length)
+    {
+      ++endSQ;
+      ReadDataElement<Dimension,PixelType>(f, model);
+      --endSQ;
+    }
+    size += Length + 8;
+    if(size != Length)
+      std::cout << "DicomObj length error\n";
+  }
+}
+//---------------------------------------------------------------------------
+
+
+//---------------------------------------------------------------------------
+void CopyDictionary (itk::MetaDataDictionary &fromDict, itk::MetaDataDictionary &toDict)
+{
+  typedef itk::MetaDataDictionary DictionaryType;
+
+  DictionaryType::ConstIterator itr = fromDict.Begin();
+  DictionaryType::ConstIterator end = fromDict.End();
+  typedef itk::MetaDataObject< std::string > MetaDataStringType;
+
+  while( itr != end )
+    {
+    itk::MetaDataObjectBase::Pointer  entry = itr->second;
+
+    MetaDataStringType::Pointer entryvalue =
+      dynamic_cast<MetaDataStringType *>( entry.GetPointer() ) ;
+    if( entryvalue )
+      {
+      std::string tagkey   = itr->first;
+      std::string tagvalue = entryvalue->GetMetaDataObjectValue();
+      itk::EncapsulateMetaData<std::string>(toDict, tagkey, tagvalue);
+      }
+    ++itr;
+    }
+}
+//---------------------------------------------------------------------------
+
+
+//---------------------------------------------------------------------------
+template <typename T> std::string NumberToString ( T Number )
+{
+   std::ostringstream ss;
+   ss << Number;
+   return ss.str();
+}
+//---------------------------------------------------------------------------
+
+
+//---------------------------------------------------------------------------
+std::string string_to_hex( std::string &input )
+{
+    static const char* const lut = "0123456789ABCDEF";
+    size_t len = input.length();
+
+    std::string output;
+    output.reserve(2 * len);
+    for (size_t i = 0; i < len; ++i)
+    {
+        const unsigned char c = input[i];
+        output.push_back(lut[c >> 4]);
+        output.push_back(lut[c & 15]);
+    }
+    return output;
+}
+//---------------------------------------------------------------------------
+
+
+//---------------------------------------------------------------------------
+template< typename T > std::string int_to_hex( T i )
+{
+  std::stringstream stream;
+  stream << std::setfill ('0') << std::setw(sizeof(T)*2) 
+         << std::hex << i;
+  return stream.str();
+}
+//---------------------------------------------------------------------------
+
+
+
+//---------------------------------------------------------------------------
+template<class args_info_type>
+template <unsigned int Dimension, class  PixelType>
+void
+UpdateVRTagDicomGenericFilter<args_info_type>::InitVRMap()
+{
+  VRMap["AE"] = VR_AE;
+  VRMap["AS"] = VR_AS;
+  VRMap["AT"] = VR_AT;
+  VRMap["CS"] = VR_CS;
+  VRMap["DA"] = VR_DA;
+  VRMap["DS"] = VR_DS;
+  VRMap["DT"] = VR_DT;
+  VRMap["FL"] = VR_FL;
+  VRMap["FD"] = VR_FD;
+  VRMap["IS"] = VR_IS;
+  VRMap["LO"] = VR_LO;
+  VRMap["LT"] = VR_LT;
+  VRMap["OB"] = VR_OB;
+  VRMap["OF"] = VR_OF;
+  VRMap["OW"] = VR_OW;
+  VRMap["PN"] = VR_PN;
+  VRMap["SH"] = VR_SH;
+  VRMap["SL"] = VR_SL;
+  VRMap["SQ"] = VR_SQ;
+  VRMap["SS"] = VR_SS;
+  VRMap["ST"] = VR_ST;
+  VRMap["TM"] = VR_TM;
+  VRMap["UI"] = VR_UI;
+  VRMap["UL"] = VR_UL;
+  VRMap["UN"] = VR_UN;
+  VRMap["US"] = VR_US;
+  VRMap["UT"] = VR_UT;
+}
+//---------------------------------------------------------------------------
+
+
+//---------------------------------------------------------------------------
+template<class args_info_type>
+template <unsigned int Dimension, class  PixelType>
+void
+UpdateVRTagDicomGenericFilter<args_info_type>::UpdateVRMap()
+{
+  VRMapModel["(0002,0000)"] = "UL";
+  VRMapModel["(0002,0001)"] = "OB";
+  VRMapModel["(0002,0002)"] = "UI";
+  VRMapModel["(0002,0003)"] = "UI";
+  VRMapModel["(0002,0010)"] = "UI";
+  VRMapModel["(0002,0012)"] = "UI";
+  VRMapModel["(0002,0013)"] = "SH";
+  VRMapModel["(0008,0005)"] = "CS";
+  VRMapModel["(0008,0008)"] = "CS";
+  VRMapModel["(0008,0016)"] = "UI";
+  VRMapModel["(0008,0018)"] = "UI";
+  VRMapModel["(0008,0020)"] = "DA";
+  VRMapModel["(0008,0021)"] = "DA";
+  VRMapModel["(0008,0022)"] = "DA";
+  VRMapModel["(0008,0023)"] = "DA";
+  VRMapModel["(0008,002a)"] = "DT";
+  VRMapModel["(0008,0030)"] = "TM";
+  VRMapModel["(0008,0031)"] = "TM";
+  VRMapModel["(0008,0032)"] = "TM";
+  VRMapModel["(0008,0033)"] = "TM";
+  VRMapModel["(0008,0050)"] = "SH";
+  VRMapModel["(0008,0060)"] = "CS";
+  VRMapModel["(0008,0070)"] = "LO";
+  VRMapModel["(0008,0090)"] = "PN";
+  VRMapModel["(0008,1010)"] = "SH";
+  VRMapModel["(0008,1030)"] = "LO";
+  VRMapModel["(0008,1032)"] = "SQ";
+  VRMapModel["(0008,0100)"] = "SH";
+  VRMapModel["(0008,0102)"] = "SH";
+  VRMapModel["(0008,0103)"] = "SH";
+  VRMapModel["(0008,0104)"] = "LO";
+  VRMapModel["(0008,103e)"] = "LO";
+  VRMapModel["(0008,1090)"] = "LO";
+  VRMapModel["(0008,1111)"] = "SQ";
+  VRMapModel["(0008,1150)"] = "UI";
+  VRMapModel["(0009,0010)"] = "LO";
+  VRMapModel["(0009,1029)"] = "??";
+  VRMapModel["(0009,1030)"] = "??";
+  VRMapModel["(0009,104d)"] = "??";
+  VRMapModel["(0009,104e)"] = "??";
+  VRMapModel["(0009,104f)"] = "??";
+  VRMapModel["(0009,1050)"] = "??";
+  VRMapModel["(0009,1051)"] = "??";
+  VRMapModel["(0009,1052)"] = "??";
+  VRMapModel["(0009,1053)"] = "??";
+  VRMapModel["(0009,1054)"] = "??";
+  VRMapModel["(0009,1055)"] = "??";
+  VRMapModel["(0009,1056)"] = "??";
+  VRMapModel["(0009,1057)"] = "??";
+  VRMapModel["(0009,1058)"] = "??";
+  VRMapModel["(0009,1059)"] = "??";
+  VRMapModel["(0009,105a)"] = "??";
+  VRMapModel["(0009,105b)"] = "??";
+  VRMapModel["(0009,105c)"] = "??";
+  VRMapModel["(0009,105d)"] = "??";
+  VRMapModel["(0009,105e)"] = "??";
+  VRMapModel["(0009,1069)"] = "??";
+  VRMapModel["(0009,106a)"] = "??";
+  VRMapModel["(0009,106b)"] = "??";
+  VRMapModel["(0009,106c)"] = "??";
+  VRMapModel["(0009,106d)"] = "??";
+  VRMapModel["(0009,106e)"] = "??";
+  VRMapModel["(0009,106f)"] = "??";
+  VRMapModel["(0009,1070)"] = "??";
+  VRMapModel["(0009,1071)"] = "??";
+  VRMapModel["(0009,1087)"] = "??";
+  VRMapModel["(0009,1088)"] = "??";
+  VRMapModel["(0009,1090)"] = "??";
+  VRMapModel["(0009,1091)"] = "??";
+  VRMapModel["(0009,1092)"] = "??";
+  VRMapModel["(0009,1093)"] = "??";
+  VRMapModel["(0009,109a)"] = "??";
+  VRMapModel["(0009,10a3)"] = "??";
+  VRMapModel["(0009,10a4)"] = "??";
+  VRMapModel["(0009,10a9)"] = "??";
+  VRMapModel["(0009,10aa)"] = "??";
+  VRMapModel["(0009,10ab)"] = "??";
+  VRMapModel["(0009,10ac)"] = "??";
+  VRMapModel["(0009,10ad)"] = "??";
+  VRMapModel["(0009,10ae)"] = "??";
+  VRMapModel["(0009,10af)"] = "??";
+  VRMapModel["(0009,10b0)"] = "??";
+  VRMapModel["(0009,10b1)"] = "??";
+  VRMapModel["(0009,10b2)"] = "??";
+  VRMapModel["(0009,10ba)"] = "??";
+  VRMapModel["(0009,10bb)"] = "SQ";
+  VRMapModel["(0009,10bd)"] = "??";
+  VRMapModel["(0009,10c2)"] = "??";
+  VRMapModel["(0009,0010)"] = "LO";
+  VRMapModel["(0009,0010)"] = "LO";
+  VRMapModel["(0009,0010)"] = "LO";
+  VRMapModel["(0009,0010)"] = "LO";
+  VRMapModel["(0009,0010)"] = "LO";
+  VRMapModel["(0009,0010)"] = "LO";
+  VRMapModel["(0009,0010)"] = "LO";
+  VRMapModel["(0009,0010)"] = "LO";
+  VRMapModel["(0009,0010)"] = "LO";
+  VRMapModel["(0009,0010)"] = "LO";
+  VRMapModel["(0009,0010)"] = "LO";
+  VRMapModel["(0009,0010)"] = "LO";
+  VRMapModel["(0009,10c4)"] = "OB";
+  VRMapModel["(0010,0010)"] = "PN";
+  VRMapModel["(0010,0020)"] = "LO";
+  VRMapModel["(0010,0030)"] = "DA";
+  VRMapModel["(0010,0040)"] = "CS";
+  VRMapModel["(0010,1020)"] = "DS";
+  VRMapModel["(0010,1030)"] = "DS";
+  VRMapModel["(0018,0070)"] = "IS";
+  VRMapModel["(0018,0088)"] = "DS";
+  VRMapModel["(0018,0071)"] = "CS";
+  VRMapModel["(0018,1000)"] = "LO";
+  VRMapModel["(0018,1020)"] = "LO";
+  VRMapModel["(0018,1030)"] = "LO";
+  VRMapModel["(0018,1243)"] = "IS";
+  VRMapModel["(0018,5100)"] = "CS";
+  VRMapModel["(0020,000d)"] = "UI";
+  VRMapModel["(0020,000e)"] = "UI";
+  VRMapModel["(0020,0010)"] = "SH";
+  VRMapModel["(0020,0011)"] = "IS";
+  VRMapModel["(0020,0012)"] = "IS";
+  VRMapModel["(0020,0013)"] = "IS";
+  VRMapModel["(0020,4000)"] = "LT";
+  VRMapModel["(0028,0002)"] = "US";
+  VRMapModel["(0028,0004)"] = "CS";
+  VRMapModel["(0028,0008)"] = "IS";
+  VRMapModel["(0028,0009)"] = "AT";
+  VRMapModel["(0028,0010)"] = "US";
+  VRMapModel["(0028,0011)"] = "US";
+  VRMapModel["(0028,0030)"] = "DS";
+  VRMapModel["(0028,0051)"] = "CS";
+  VRMapModel["(0028,0100)"] = "US";
+  VRMapModel["(0028,0101)"] = "US";
+  VRMapModel["(0028,0102)"] = "US";
+  VRMapModel["(0028,0103)"] = "US";
+  VRMapModel["(0040,0244)"] = "DA";
+  VRMapModel["(0040,0245)"] = "TM";
+  VRMapModel["(0040,0253)"] = "SH";
+  VRMapModel["(0040,0254)"] = "LO";
+  VRMapModel["(0040,0260)"] = "SQ";
+  VRMapModel["(0008,0104)"] = "LO";
+  VRMapModel["(0040,0275)"] = "SQ";
+  VRMapModel["(0008,0050)"] = "SH";
+  VRMapModel["(0020,000d)"] = "UI";
+  VRMapModel["(0032,1060)"] = "LO";
+  VRMapModel["(0032,1064)"] = "SQ";
+  VRMapModel["(0008,0100)"] = "SH";
+  VRMapModel["(0008,0102)"] = "SH";
+  VRMapModel["(0008,0103)"] = "SH";
+  VRMapModel["(0008,0104)"] = "LO";
+  VRMapModel["(0040,0008)"] = "SQ";
+  VRMapModel["(0008,0104)"] = "LO";
+  VRMapModel["(0040,0009)"] = "SH";
+  VRMapModel["(0040,1001)"] = "SH";
+  VRMapModel["(0054,0010)"] = "US";
+  VRMapModel["(0054,0011)"] = "US";
+  VRMapModel["(0054,0012)"] = "SQ";
+  VRMapModel["(0054,0013)"] = "SQ";
+  VRMapModel["(0054,0014)"] = "DS";
+  VRMapModel["(0054,0015)"] = "DS";
+  VRMapModel["(0054,0014)"] = "DS";
+  VRMapModel["(0054,0015)"] = "DS";
+  VRMapModel["(0054,0018)"] = "SH";
+  VRMapModel["(0054,0013)"] = "SQ";
+  VRMapModel["(0054,0014)"] = "DS";
+  VRMapModel["(0054,0015)"] = "DS";
+  VRMapModel["(0054,0018)"] = "SH";
+  VRMapModel["(0054,0013)"] = "SQ";
+  VRMapModel["(0054,0014)"] = "DS";
+  VRMapModel["(0054,0015)"] = "DS";
+  VRMapModel["(0054,0018)"] = "SH";
+  VRMapModel["(0054,0016)"] = "SQ";
+  VRMapModel["(0018,0031)"] = "LO";
+  VRMapModel["(0018,1071)"] = "DS";
+  VRMapModel["(0018,1074)"] = "DS";
+  VRMapModel["(0054,0300)"] = "SQ";
+  VRMapModel["(0008,0100)"] = "SH";
+  VRMapModel["(0008,0102)"] = "SH";
+  VRMapModel["(0008,0104)"] = "LO";
+  VRMapModel["(0054,0302)"] = "SQ";
+  VRMapModel["(0008,0100)"] = "SH";
+  VRMapModel["(0008,0102)"] = "SH";
+  VRMapModel["(0008,0104)"] = "LO";
+  VRMapModel["(0054,0306)"] = "SQ";
+  VRMapModel["(0018,1045)"] = "IS";
+  VRMapModel["(0054,0017)"] = "IS";
+  VRMapModel["(0054,0308)"] = "US";
+  VRMapModel["(0018,1045)"] = "IS";
+  VRMapModel["(0054,0017)"] = "IS";
+  VRMapModel["(0054,0308)"] = "US";
+  VRMapModel["(0018,1045)"] = "IS";
+  VRMapModel["(0054,0017)"] = "IS";
+  VRMapModel["(0054,0308)"] = "US";
+  VRMapModel["(0018,1045)"] = "IS";
+  VRMapModel["(0054,0017)"] = "IS";
+  VRMapModel["(0054,0308)"] = "US";
+  VRMapModel["(0054,0020)"] = "US";
+  VRMapModel["(0054,0021)"] = "US";
+  VRMapModel["(0054,0022)"] = "SQ";
+  VRMapModel["(0009,0010)"] = "LO";
+  VRMapModel["(0009,1031)"] = "DS";
+  VRMapModel["(0009,1032)"] = "SH";
+  VRMapModel["(0009,1033)"] = "FD";
+  VRMapModel["(0009,1034)"] = "FD";
+  VRMapModel["(0009,107c)"] = "FD";
+  VRMapModel["(0009,107d)"] = "FD";
+  VRMapModel["(0009,107e)"] = "FD";
+  VRMapModel["(0009,107f)"] = "FD";
+  VRMapModel["(0009,1080)"] = "FD";
+  VRMapModel["(0009,1081)"] = "FD";
+  VRMapModel["(0009,1082)"] = "FD";
+  VRMapModel["(0009,1083)"] = "FD";
+  VRMapModel["(0009,1084)"] = "FD";
+  VRMapModel["(0009,1085)"] = "FD";
+  VRMapModel["(0009,1086)"] = "FD";
+  VRMapModel["(0009,10a5)"] = "FD";
+  VRMapModel["(0009,10a6)"] = "SL";
+  VRMapModel["(0009,10a7)"] = "SL";
+  VRMapModel["(0009,10a8)"] = "FD";
+  VRMapModel["(0018,1142)"] = "DS";
+  VRMapModel["(0018,1147)"] = "CS";
+  VRMapModel["(0018,1149)"] = "IS";
+  VRMapModel["(0018,1180)"] = "SH";
+  VRMapModel["(0018,1181)"] = "CS";
+  VRMapModel["(0018,1182)"] = "IS";
+  VRMapModel["(0020,0032)"] = "DS";
+  VRMapModel["(0020,0037)"] = "DS";
+  VRMapModel["(0028,0031)"] = "DS";
+  VRMapModel["(0054,0200)"] = "DS";
+  VRMapModel["(0009,0010)"] = "LO";
+  VRMapModel["(0009,1031)"] = "DS";
+  VRMapModel["(0009,1032)"] = "SH";
+  VRMapModel["(0009,1033)"] = "FD";
+  VRMapModel["(0009,1034)"] = "FD";
+  VRMapModel["(0009,107c)"] = "FD";
+  VRMapModel["(0009,107d)"] = "FD";
+  VRMapModel["(0009,107e)"] = "FD";
+  VRMapModel["(0009,107f)"] = "FD";
+  VRMapModel["(0009,1080)"] = "FD";
+  VRMapModel["(0009,1081)"] = "FD";
+  VRMapModel["(0009,1082)"] = "FD";
+  VRMapModel["(0009,1083)"] = "FD";
+  VRMapModel["(0009,1084)"] = "FD";
+  VRMapModel["(0009,1085)"] = "FD";
+  VRMapModel["(0009,1086)"] = "FD";
+  VRMapModel["(0009,10a5)"] = "FD";
+  VRMapModel["(0009,10a6)"] = "SL";
+  VRMapModel["(0009,10a7)"] = "SL";
+  VRMapModel["(0009,10a8)"] = "FD";
+  VRMapModel["(0018,1142)"] = "DS";
+  VRMapModel["(0018,1147)"] = "CS";
+  VRMapModel["(0018,1149)"] = "IS";
+  VRMapModel["(0018,1180)"] = "SH";
+  VRMapModel["(0018,1181)"] = "CS";
+  VRMapModel["(0018,1182)"] = "IS";
+  VRMapModel["(0020,0032)"] = "DS";
+  VRMapModel["(0020,0037)"] = "DS";
+  VRMapModel["(0028,0031)"] = "DS";
+  VRMapModel["(0054,0200)"] = "DS";
+  VRMapModel["(0009,0010)"] = "LO";
+  VRMapModel["(0009,1031)"] = "DS";
+  VRMapModel["(0009,1032)"] = "SH";
+  VRMapModel["(0009,1033)"] = "FD";
+  VRMapModel["(0009,1034)"] = "FD";
+  VRMapModel["(0009,107c)"] = "FD";
+  VRMapModel["(0009,107d)"] = "FD";
+  VRMapModel["(0009,107e)"] = "FD";
+  VRMapModel["(0009,107f)"] = "FD";
+  VRMapModel["(0009,1080)"] = "FD";
+  VRMapModel["(0009,1081)"] = "FD";
+  VRMapModel["(0009,1082)"] = "FD";
+  VRMapModel["(0009,1083)"] = "FD";
+  VRMapModel["(0009,1084)"] = "FD";
+  VRMapModel["(0009,1085)"] = "FD";
+  VRMapModel["(0009,1086)"] = "FD";
+  VRMapModel["(0009,10a5)"] = "FD";
+  VRMapModel["(0009,10a6)"] = "SL";
+  VRMapModel["(0009,10a7)"] = "SL";
+  VRMapModel["(0009,10a8)"] = "FD";
+  VRMapModel["(0018,1142)"] = "DS";
+  VRMapModel["(0018,1147)"] = "CS";
+  VRMapModel["(0018,1149)"] = "IS";
+  VRMapModel["(0018,1180)"] = "SH";
+  VRMapModel["(0018,1181)"] = "CS";
+  VRMapModel["(0018,1182)"] = "IS";
+  VRMapModel["(0020,0032)"] = "DS";
+  VRMapModel["(0020,0037)"] = "DS";
+  VRMapModel["(0028,0031)"] = "DS";
+  VRMapModel["(0054,0200)"] = "DS";
+  VRMapModel["(0009,0010)"] = "LO";
+  VRMapModel["(0009,1031)"] = "DS";
+  VRMapModel["(0009,1032)"] = "SH";
+  VRMapModel["(0009,1033)"] = "FD";
+  VRMapModel["(0009,1034)"] = "FD";
+  VRMapModel["(0009,107c)"] = "FD";
+  VRMapModel["(0009,107d)"] = "FD";
+  VRMapModel["(0009,107e)"] = "FD";
+  VRMapModel["(0009,107f)"] = "FD";
+  VRMapModel["(0009,1080)"] = "FD";
+  VRMapModel["(0009,1081)"] = "FD";
+  VRMapModel["(0009,1082)"] = "FD";
+  VRMapModel["(0009,1083)"] = "FD";
+  VRMapModel["(0009,1084)"] = "FD";
+  VRMapModel["(0009,1085)"] = "FD";
+  VRMapModel["(0009,1086)"] = "FD";
+  VRMapModel["(0009,10a5)"] = "FD";
+  VRMapModel["(0009,10a6)"] = "SL";
+  VRMapModel["(0009,10a7)"] = "SL";
+  VRMapModel["(0009,10a8)"] = "FD";
+  VRMapModel["(0018,1142)"] = "DS";
+  VRMapModel["(0018,1147)"] = "CS";
+  VRMapModel["(0018,1149)"] = "IS";
+  VRMapModel["(0018,1180)"] = "SH";
+  VRMapModel["(0018,1181)"] = "CS";
+  VRMapModel["(0018,1182)"] = "IS";
+  VRMapModel["(0020,0032)"] = "DS";
+  VRMapModel["(0020,0037)"] = "DS";
+  VRMapModel["(0028,0031)"] = "DS";
+  VRMapModel["(0054,0200)"] = "DS";
+  VRMapModel["(0054,0050)"] = "US";
+  VRMapModel["(0054,0051)"] = "US";
+  VRMapModel["(0054,0052)"] = "SQ";
+  VRMapModel["(0009,0010)"] = "LO";
+  VRMapModel["(0009,1024)"] = "DS";
+  VRMapModel["(0009,1025)"] = "DS";
+  VRMapModel["(0009,1026)"] = "DS";
+  VRMapModel["(0009,1027)"] = "DT";
+  VRMapModel["(0009,1028)"] = "DT";
+  VRMapModel["(0018,1140)"] = "CS";
+  VRMapModel["(0018,1143)"] = "DS";
+  VRMapModel["(0018,1144)"] = "DS";
+  VRMapModel["(0018,1242)"] = "IS";
+  VRMapModel["(0054,0053)"] = "US";
+  VRMapModel["(0054,0200)"] = "DS";
+  VRMapModel["(0054,0090)"] = "US";
+  VRMapModel["(0054,0202)"] = "CS";
+  VRMapModel["(0054,0400)"] = "SH";
+  VRMapModel["(0054,0410)"] = "SQ";
+  VRMapModel["(0008,0100)"] = "SH";
+  VRMapModel["(0008,0102)"] = "SH";
+  VRMapModel["(0008,0104)"] = "LO";
+  VRMapModel["(0054,0412)"] = "SQ";
+  VRMapModel["(0008,0100)"] = "SH";
+  VRMapModel["(0008,0102)"] = "SH";
+  VRMapModel["(0008,0104)"] = "LO";
+  VRMapModel["(0054,0414)"] = "SQ";
+  VRMapModel["(0008,0100)"] = "SH";
+  VRMapModel["(0008,0102)"] = "SH";
+  VRMapModel["(0008,0104)"] = "LO";
+  VRMapModel["(7fe0,0010)"] = "OW";
+}
+//---------------------------------------------------------------------------
+
+}//end clitk
+
+#endif //#define clitkUpdateVRTagDicomGenericFilter_txx
index 4175215526c030e19e5ba3c32d7bccd25ee79ce2..d287fec36e0b4047b64c7c5490b59536afcdb810 100644 (file)
@@ -92,17 +92,17 @@ clitk::VFResampleGenericFilter::ComputeImage(typename ImageType::Pointer inputIm
   static unsigned int dim = ImageType::ImageDimension;
   if (mOutputSize.size() != dim) {
     std::cerr << "Please set size with " << dim << " dimensions." << std::endl;
-    return NULL;
+    return ITK_NULLPTR;
   }
   if (mOutputSpacing.size() != dim) {
     std::cerr << "Please set spacing with " << dim << " dimensions." << std::endl;
-    return NULL;
+    return ITK_NULLPTR;
   }
   mOutputOrigin.resize(dim);
 
   if (mApplyGaussianFilterBefore && mSigma.size() != dim) {
     std::cerr << "Please set sigma with " << dim << " dimensions." << std::endl;
-    return NULL;
+    return ITK_NULLPTR;
   }
 
   // Some typedefs
index 5c7d65efe1d315c21482df0d487e2ca312697dd2..3e21fca4b1676b500d58382cc4aafa61b323e444 100644 (file)
@@ -13,7 +13,7 @@ option "input2"          j    "Input second image filename"     string   no
 option "output"    o   "Output image filename"           string   yes
 
 option "scalar"           s    "Scalar value"            double   no
-option "operation" t   "Type of operation : \n With another image : 0=add, 1=multiply (dotproduct), 7=difference, 9=crossproduct\n; For 'scalar' : 0=add, 1=multiply, 5=absval (magnitude), 6=squared magnitude, 11=divide, 12=normalize"      int default="0" no 
+option "operation" t   "Type of operation : \n With another image : 0=add, 1=multiply, 2=dotproduct, 7=difference, 9=crossproduct\n; For 'scalar' : 0=add, 1=multiply, 5=absval (magnitude), 6=squared magnitude, 11=divide, 12=normalize"     int default="0" no
 option "pixelValue" -  "Default value for NaN/Inf"     double   default="0.0"  no
 
 option "setFloatOutput" f "Set output to float pixel type" flag off
index 1f4cb721ea246b222c5ceb370cba283597918860..b4d61279a86602d3ca1337949a89a60b288ba01a 100644 (file)
@@ -79,13 +79,16 @@ void VectorArithmGenericFilter<args_info_type>::SetArgsInfo(const args_info_type
   if (mArgsInfo.input2_given) {
     mIsOperationUseASecondImage = true;
     this->AddInputFilename(mArgsInfo.input2_arg);
-    if (mArgsInfo.operation_arg == 1)
+    if (mArgsInfo.operation_arg == 2)
       mIsOutputScalar = true;
   } 
   else if (mArgsInfo.operation_arg == 5 || mArgsInfo.operation_arg == 6)
     mIsOutputScalar = true;
 
-  if (mArgsInfo.output_given) this->SetOutputFilename(mArgsInfo.output_arg);
+  if (mArgsInfo.output_given) {
+    this->SetOutputFilename(mArgsInfo.output_arg);
+    mOverwriteInputImage = false;
+  }
 
   // Check type of operation (with scalar or with other image)
   if ((mArgsInfo.input2_given) && (mArgsInfo.scalar_given)) {
@@ -115,7 +118,7 @@ void VectorArithmGenericFilter<args_info_type>::UpdateWithInputImageType()
   IteratorType it(input1, input1->GetLargestPossibleRegion());
 
   // typedef input2
-  typename ImageType::Pointer input2 = NULL;
+  typename ImageType::Pointer input2 = ITK_NULLPTR;
   IteratorType it2;
 
   // Special case for normalisation
@@ -217,16 +220,18 @@ void  VectorArithmGenericFilter<args_info_type>::ComputeImage(Iter1 it1, Iter2 i
       ++ito;
     }
     break;
-    /*
-  case 1: // Multiply
+
+  case 1: // term to term Multiply
     while (!ito.IsAtEnd()) {
-      ito.Set(it1.Get() * it2.Get()) );
+      typename Iter1::PixelType outputPixel(ito.Get());
+      outputPixel.SetVnlVector(element_product(it1.Get().GetVnlVector(),it2.Get().GetVnlVector()));
+      ito.Set(outputPixel);
       ++it1;
       ++it2;
       ++ito;
     }
     break;
-    */
+
     /*
   case 2: // Divide
     while (!ito.IsAtEnd()) {
@@ -457,7 +462,7 @@ void  VectorArithmGenericFilter<args_info_type>::ComputeScalarImage(Iter1 it1, I
   typedef typename Iter3::PixelType PixelType;
 
   switch (mTypeOfOperation) {
-  case 1: // Multiply
+  case 2: // Multiply
     while (!ito.IsAtEnd()) {
       ito.Set(it1.Get() * it2.Get());
       ++it1;
diff --git a/tools/clitkXml2DicomRTStruct.cxx b/tools/clitkXml2DicomRTStruct.cxx
new file mode 100644 (file)
index 0000000..96c160f
--- /dev/null
@@ -0,0 +1,43 @@
+/*=========================================================================
+  Program:         vv http://www.creatis.insa-lyon.fr/rio/vv
+  Main authors :   XX XX XX
+
+  Authors belongs to:
+  - University of LYON           http://www.universite-lyon.fr/
+  - Léon Bérard cancer center    http://www.centreleonberard.fr
+  - CREATIS CNRS laboratory      http://www.creatis.insa-lyon.fr
+
+  This software is distributed WITHOUT ANY WARRANTY; without even
+  the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+  PURPOSE.  See the copyright notices for more information.
+
+  It is distributed under dual licence
+  - BSD       http://www.opensource.org/licenses/bsd-license.php
+  - CeCILL-B  http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.html
+
+  =========================================================================*/
+
+// clitk
+#include "clitkXml2DicomRTStructFilter.h"
+#include "clitkXml2DicomRTStruct_ggo.h"
+
+//--------------------------------------------------------------------
+int main(int argc, char * argv[]) {
+
+  // Init command line
+  GGO(clitkXml2DicomRTStruct, args_info);
+
+  // Create a filter to convert Xml struct into dicomRTStruct and write to disk
+  typedef float PixelType;
+  clitk::Xml2DicomRTStructFilter<PixelType> filter;
+  filter.SetVerboseFlag(args_info.verbose_flag);
+  filter.SetInputFilename(args_info.input_arg);
+  filter.SetDicomFolder(args_info.dicom_arg);
+  filter.SetStructureSetFilename(args_info.rtstruct_arg);
+  filter.SetOutputFilename(args_info.output_arg);
+  filter.Update();
+
+  // This is the end my friend
+  return 0;
+}
+//--------------------------------------------------------------------
diff --git a/tools/clitkXml2DicomRTStruct.ggo b/tools/clitkXml2DicomRTStruct.ggo
new file mode 100644 (file)
index 0000000..ab49cce
--- /dev/null
@@ -0,0 +1,14 @@
+# file clitkXml2DicomRTStruct.ggo
+package "clitk"
+version "1.0"
+description "Convert a Structure from XML to DICOM RT Structure Set (contours)
+The structure of the xml has to be like this:
+"
+
+option "config"    - "Config file"                                                    string  no
+option "verbose"   v "Verbose"                                                        flag off
+
+option "input"     i "Input xml file with struct to be converted into DicomRTStruct"  string  yes
+option "rtstruct"  r "Input rt struct"                                                string  yes
+option "dicom"     d "Input folder where the initial dicom ct is"                     string  yes
+option "output"    o "Output DicomRT filename"                                        string  yes
index ee0fc2597572630ad1611bf70e6c80fe889fce11..ed6f89e8491f488480c819bbe38ee867281b0d8f 100755 (executable)
@@ -4,20 +4,26 @@ MAKE="make --jobs=$NUM_THREADS --keep-going"
 
 #Prepare cmake arguments following the ITK version
 if [ "$C11" == "true" ]; then
-  if [ "$ITK_VERSION" == "4.5" ]; then
-    itk_repo_str=" --branch v4.5.0 https://github.com/InsightSoftwareConsortium/ITK.git --depth 1"
-    cmake_arg_str=" -DCMAKE_CXX_FLAGS=-std=c++11 -DModule_ITKVtkGlue=ON -DVTK_DIR=$VTK_DIR -DModule_ITKReview=ON -DBUILD_SHARED_LIBS=ON -DBUILD_TESTING=OFF -DBUILD_EXAMPLES=OFF "
+  if [ "$ITK_VERSION" == "4.6" ]; then
+    itk_repo_str=" --branch v4.6.0 https://github.com/InsightSoftwareConsortium/ITK.git --depth 1"
+    cmake_arg_str=" -DCMAKE_CXX_FLAGS=-std=c++11 -DModule_ITKVtkGlue=ON -DVTK_DIR=$VTK_DIR -DBUILD_SHARED_LIBS=ON -DBUILD_TESTING=OFF -DBUILD_EXAMPLES=OFF "
   elif [ "$ITK_VERSION" == "4.9.1" ]; then
     itk_repo_str=" --branch v4.9.1 https://github.com/InsightSoftwareConsortium/ITK.git --depth 1"
-    cmake_arg_str=" -DCMAKE_CXX_FLAGS=-std=c++11 -DModule_ITKVtkGlue=ON -DVTK_DIR=$VTK_DIR -DModule_ITKReview=ON -DBUILD_SHARED_LIBS=ON -DBUILD_TESTING=OFF -DBUILD_EXAMPLES=OFF "
+    cmake_arg_str=" -DCMAKE_CXX_FLAGS=-std=c++11 -DModule_ITKVtkGlue=ON -DVTK_DIR=$VTK_DIR -DBUILD_SHARED_LIBS=ON -DBUILD_TESTING=OFF -DBUILD_EXAMPLES=OFF "
+  elif [ "$ITK_VERSION" == "4.13.0" ]; then
+    itk_repo_str=" --branch v4.13.0 https://github.com/InsightSoftwareConsortium/ITK.git --depth 1"
+    cmake_arg_str=" -DCMAKE_CXX_FLAGS=-std=c++11 -DModule_ITKVtkGlue=ON -DVTK_DIR=$VTK_DIR -DBUILD_SHARED_LIBS=ON -DBUILD_TESTING=OFF -DBUILD_EXAMPLES=OFF "
   fi
 else
-  if [ "$ITK_VERSION" == "4.5" ]; then
-    itk_repo_str=" --branch v4.5.0 https://github.com/InsightSoftwareConsortium/ITK.git --depth 1"
-    cmake_arg_str=" -DModule_ITKVtkGlue=ON -DVTK_DIR=$VTK_DIR -DModule_ITKReview=ON -DBUILD_SHARED_LIBS=ON -DBUILD_TESTING=OFF -DBUILD_EXAMPLES=OFF "
+  if [ "$ITK_VERSION" == "4.6" ]; then
+    itk_repo_str=" --branch v4.6.0 https://github.com/InsightSoftwareConsortium/ITK.git --depth 1"
+    cmake_arg_str=" -DModule_ITKVtkGlue=ON -DVTK_DIR=$VTK_DIR -DBUILD_SHARED_LIBS=ON -DBUILD_TESTING=OFF -DBUILD_EXAMPLES=OFF "
   elif [ "$ITK_VERSION" == "4.9.1" ]; then
     itk_repo_str=" --branch v4.9.1 https://github.com/InsightSoftwareConsortium/ITK.git --depth 1"
-    cmake_arg_str=" -DModule_ITKVtkGlue=ON -DVTK_DIR=$VTK_DIR -DModule_ITKReview=ON -DBUILD_SHARED_LIBS=ON -DBUILD_TESTING=OFF -DBUILD_EXAMPLES=OFF "
+    cmake_arg_str=" -DModule_ITKVtkGlue=ON -DVTK_DIR=$VTK_DIR -DBUILD_SHARED_LIBS=ON -DBUILD_TESTING=OFF -DBUILD_EXAMPLES=OFF "
+  elif [ "$ITK_VERSION" == "4.13.0" ]; then
+    itk_repo_str=" --branch v4.13.0 https://github.com/InsightSoftwareConsortium/ITK.git --depth 1"
+    cmake_arg_str=" -DModule_ITKVtkGlue=ON -DVTK_DIR=$VTK_DIR -DBUILD_SHARED_LIBS=ON -DBUILD_TESTING=OFF -DBUILD_EXAMPLES=OFF "
   fi
 fi
 
index 7b3ef0bb54c84814040528014b82b19d05f79dc6..7fffb00413c3586e2ad3b62ee0c40db49d3cbf62 100755 (executable)
@@ -4,20 +4,12 @@ set -ev
 #Install the correct Qt Version
 if [ "$QT_VERSION" == "4.8.7" ]; then
   if test $TRAVIS_OS_NAME == linux ; then sudo apt-get -y --force-yes install qt4-dev-tools; fi
-  
-  if test $TRAVIS_OS_NAME == osx ; then brew install cartr/qt4/qt@4; fi
-  if test $TRAVIS_OS_NAME == osx ; then brew install cartr/qt4/qt-webkit@2.3; fi
-  if test $TRAVIS_OS_NAME == osx ; then brew link qt@4 --force; fi
-  if test $TRAVIS_OS_NAME == osx ; then brew link qt-webkit@2.3 --force; fi
-  if test $TRAVIS_OS_NAME == osx ; then qmake -v; fi
 elif [ "$QT_VERSION" == "5.5.1" ]; then
   if test $TRAVIS_OS_NAME == linux ; then sudo add-apt-repository --yes ppa:beineri/opt-qt551-trusty; fi
   if test $TRAVIS_OS_NAME == linux ; then sudo apt-get update -qq; fi
   if test $TRAVIS_OS_NAME == linux ; then sudo apt-get -y --force-yes install qt55tools; fi
-  
-  if test $TRAVIS_OS_NAME == osx ; then brew install qt@5.5; fi
-  if test $TRAVIS_OS_NAME == osx ; then brew link qt@5.5 --force; fi
-  if test $TRAVIS_OS_NAME == osx ; then brew linkapps qt@5.5; fi
 fi
+if test $TRAVIS_OS_NAME == osx ; then brew install qt5; fi
+if test $TRAVIS_OS_NAME == osx ; then brew link qt5 --force; fi
 
 
index 54720764e6cd7a44b8b7c60e2914e47117feb319..1d8813354f8b47b2f870a9ae57bd37f4c7146faf 100755 (executable)
@@ -23,5 +23,7 @@ mkdir -p $BUILD_DIR
 cd $BUILD_DIR
 cmake $cmake_arg_str ..
 $MAKE
-cd -
+cd ..
+#build-wrapper-linux-x86-64 --out-dir bw-output make clean all
+sonar-scanner
 cd $cwd
index b388b2b81f4c8cad255c990374545c446305aea4..e4bf05e9a720d2bf1a1f4a818e8f24b03f977e94 100644 (file)
  #define CXIMAGE_SUPPORT_WINDOWS 0
 #endif
 
-#ifndef min
-#define min(a,b) (((a)<(b))?(a):(b))
+#ifndef __min
+#define __min(a,b) (((a)<(b))?(a):(b))
 #endif
-#ifndef max
-#define max(a,b) (((a)>(b))?(a):(b))
+#ifndef __max
+#define __max(a,b) (((a)>(b))?(a):(b))
 #endif
 
 #ifndef PI
index 8edfbfcd6d5521a681372eb67203a91b26f9d4a5..4f095feb2a42dac9774287f8c1c10822fa2a2002 100644 (file)
@@ -996,7 +996,7 @@ bool CxImage::CheckFormat(CxFile * hFile, DWORD imagetype)
 ////////////////////////////////////////////////////////////////////////////////
 bool CxImage::CheckFormat(BYTE * buffer, DWORD size, DWORD imagetype)
 {
-       if (buffer==NULL || size==NULL){
+       if (buffer==NULL || size==0){
                strcpy(info.szLastError,"invalid or empty buffer");
                return false;
        }
index e81d3c6c8e2487d85c9a310c10aec0dfc2927339..1b94d3b6e13586abcf688402e6f24f31c3e7b230 100644 (file)
@@ -460,7 +460,7 @@ bool CxImage::CreateFromArray(BYTE* pArray,DWORD dwWidth,DWORD dwHeight,DWORD dw
                                src+=4;
                        }
                } else {
-                       memcpy(dst,src,min(info.dwEffWidth,dwBytesperline));
+                       memcpy(dst,src,__min(info.dwEffWidth,dwBytesperline));
                }
        }
        return true;
@@ -500,7 +500,7 @@ bool CxImage::CreateFromMatrix(BYTE** ppMatrix,DWORD dwWidth,DWORD dwHeight,DWOR
                                        src+=4;
                                }
                        } else {
-                               memcpy(dst,src,min(info.dwEffWidth,dwBytesperline));
+                               memcpy(dst,src,__min(info.dwEffWidth,dwBytesperline));
                        }
                }
        }
index 740717c730107874c3cbdebaca305a358e185bb0..16fd3a51b718cd7c3e51edf91411a1ae91c17477 100644 (file)
@@ -478,7 +478,7 @@ bool CxImageGIF::Encode(CxFile * fp, CxImage ** pImages, int pagecount, bool bLo
        ghost.EncodeHeader(fp);
 
        if (m_loops!=1){
-               ghost.SetLoops(max(0,m_loops-1));
+               ghost.SetLoops(__max(0,m_loops-1));
                ghost.EncodeLoopExtension(fp);
        }
 
@@ -718,8 +718,8 @@ void CxImageGIF::Putword(int w, CxFile *fp )
 ////////////////////////////////////////////////////////////////////////////////
 void CxImageGIF::compressNONE( int init_bits, CxFile* outfile)
 {
-       register long c;
-       register long ent;
+       long c;
+       long ent;
 
        // g_init_bits - initial number of bits
        // g_outfile   - pointer to output file
@@ -766,12 +766,12 @@ void CxImageGIF::compressNONE( int init_bits, CxFile* outfile)
 
 void CxImageGIF::compressLZW( int init_bits, CxFile* outfile)
 {
-       register long fcode;
-       register long c;
-       register long ent;
-       register long hshift;
-       register long disp;
-       register long i;
+       long fcode;
+       long c;
+       long ent;
+       long hshift;
+       long disp;
+       long i;
 
        // g_init_bits - initial number of bits
        // g_outfile   - pointer to output file
@@ -891,10 +891,10 @@ void CxImageGIF::output( code_int  code)
 void CxImageGIF::cl_hash(long hsize)
 
 {
-       register long *htab_p = htab+hsize;
+       long *htab_p = htab+hsize;
 
-       register long i;
-       register long m1 = -1L;
+       long i;
+       long m1 = -1L;
 
        i = hsize - 16;
 
@@ -1061,9 +1061,9 @@ short CxImageGIF::get_next_code(CxFile* file)
  */
 short CxImageGIF::decoder(CxFile* file, CImageIterator* iter, short linewidth, int &bad_code_count)
 {
-       register BYTE *sp, *bufptr;
+       BYTE *sp, *bufptr;
        BYTE *buf;
-       register short code, fc, oc, bufcnt;
+       short code, fc, oc, bufcnt;
        short c, size, ret;
 
        /* Initialize for decoding a new image... */
@@ -1340,10 +1340,10 @@ void CxImageGIF::GetComment(char* sz_comment_out)
 ////////////////////////////////////////////////////////////////////////////////
 void CxImageGIF::GifMix(CxImage & imgsrc2, struct_image & imgdesc)
 {
-       long ymin = max(0,(long)(GetHeight()-imgdesc.t - imgdesc.h));
+       long ymin = __max(0,(long)(GetHeight()-imgdesc.t - imgdesc.h));
        long ymax = GetHeight()-imgdesc.t;
        long xmin = imgdesc.l;
-       long xmax = min(GetWidth(), (DWORD)(imgdesc.l + imgdesc.w));
+       long xmax = __min(GetWidth(), (DWORD)(imgdesc.l + imgdesc.w));
 
        long ibg2= imgsrc2.GetTransIndex();
     BYTE i2;
index 989d76cf71e3d1014cb7b95d2e46706a2995c0dd..b09d54968138d9e747e68e47a95ae67d7ed9ec93 100644 (file)
@@ -26,8 +26,8 @@ void CxImage::OverflowCoordinates(long &x, long &y, OverflowMethod const ofMetho
   switch (ofMethod) {
     case OM_REPEAT:
       //clip coordinates
-      x=max(x,0); x=min(x, head.biWidth-1);
-      y=max(y,0); y=min(y, head.biHeight-1);
+      x=__max(x,0); x=__min(x, head.biWidth-1);
+      y=__max(y,0); y=__min(y, head.biHeight-1);
       break;
     case OM_WRAP:
       //wrap coordinates
@@ -59,8 +59,8 @@ void CxImage::OverflowCoordinates(float &x, float &y, OverflowMethod const ofMet
   switch (ofMethod) {
     case OM_REPEAT:
       //clip coordinates
-      x=max(x,0); x=min(x, head.biWidth-1);
-      y=max(y,0); y=min(y, head.biHeight-1);
+      x=__max(x,0); x=__min(x, head.biWidth-1);
+      y=__max(y,0); y=__min(y, head.biHeight-1);
       break;
     case OM_WRAP:
       //wrap coordinates
@@ -397,6 +397,8 @@ RGBQUAD CxImage::GetPixelColorInterpolated(
             kernely[i]=KernelPower((float)(yi+i-1-y));
           }//for i
           break;
+        default:
+          i=0;
       }//switch
       rr=gg=bb=aa=0;
       if (((xi+2)<head.biWidth) && xi>=1 && ((yi+2)<head.biHeight) && (yi>=1) && !IsIndexed()) {
@@ -689,7 +691,7 @@ float CxImage::KernelBSpline(const float x)
 
        return (0.16666666666666666667f * (a - (4.0f * b) + (6.0f * c) - (4.0f * d)));
 
-       /* equivalent <Vladimír Kloucek>
+       /* equivalent <Vladimir Kloucek>
        if (x < -2.0)
                return(0.0f);
        if (x < -1.0)
@@ -722,7 +724,7 @@ float CxImage::KernelLinear(const float t)
 //  if (-1<=t && t<0) return 1+t;
 //  return 0;
        
-       //<Vladimír Kloucek>
+       //<Vladimir Kloucek>
        if (t < -1.0f)
                return 0.0f;
        if (t < 0.0f)
@@ -853,7 +855,7 @@ float CxImage::KernelBessel_J1(const float x)
 {
        double p, q;
        
-       register long i;
+       long i;
        
        static const double
        Pone[] =
@@ -895,7 +897,7 @@ float CxImage::KernelBessel_P1(const float x)
 {
        double p, q;
        
-       register long i;
+       long i;
        
        static const double
        Pone[] =
@@ -931,7 +933,7 @@ float CxImage::KernelBessel_Q1(const float x)
 {
        double p, q;
        
-       register long i;
+       long i;
        
        static const double
        Pone[] =
index 9788919b8f6d63ee9247fd7f1686ea250e1ca82e..cd5c26667c5944998bbb1e1d3591ff3d5ac1f131 100644 (file)
@@ -140,7 +140,7 @@ inline void CImageIterator::SetY(int y)
 inline void CImageIterator::SetRow(BYTE *buf, int n)
 {
        if (n<0) n = (int)ima->GetEffWidth();
-       else n = min(n,(int)ima->GetEffWidth());
+       else n = __min(n,(int)ima->GetEffWidth());
 
        if ((IterImage!=NULL)&&(buf!=NULL)&&(n>0)) memcpy(IterImage,buf,n);
 }
@@ -148,7 +148,7 @@ inline void CImageIterator::SetRow(BYTE *buf, int n)
 inline void CImageIterator::GetRow(BYTE *buf, int n)
 {
        if ((IterImage!=NULL)&&(buf!=NULL)&&(n>0))
-               memcpy(buf,IterImage,min(n,(int)ima->GetEffWidth()));
+               memcpy(buf,IterImage,__min(n,(int)ima->GetEffWidth()));
 }
 /////////////////////////////////////////////////////////////////////
 inline BYTE* CImageIterator::GetRow()
index b3bd3da39a5b7b0c50043d9725246328b2103aba..192776c4acc2d98e4bb5aa1c1fca9cb5caffd7c4 100644 (file)
@@ -398,8 +398,8 @@ void CxImage::RGBtoBGR(BYTE *buffer, int length)
 {
        if (buffer && (head.biClrUsed==0)){
                BYTE temp;
-               length = min(length,(int)info.dwEffWidth);
-               length = min(length,(int)(3*head.biWidth));
+               length = __min(length,(int)info.dwEffWidth);
+               length = __min(length,(int)(3*head.biWidth));
                for (int i=0;i<length;i+=3){
                        temp = buffer[i]; buffer[i] = buffer[i+2]; buffer[i+2] = temp;
                }
@@ -444,7 +444,7 @@ void CxImage::SetPalette(DWORD n, BYTE *r, BYTE *g, BYTE *b)
        if (!g) g = r;
        if (!b) b = g;
        RGBQUAD* ppal=GetPalette();
-       DWORD m=min(n,head.biClrUsed);
+       DWORD m=__min(n,head.biClrUsed);
        for (DWORD i=0; i<m;i++){
                ppal[i].rgbRed=r[i];
                ppal[i].rgbGreen=g[i];
@@ -457,7 +457,7 @@ void CxImage::SetPalette(rgb_color *rgb,DWORD nColors)
 {
        if ((!rgb)||(pDib==NULL)||(head.biClrUsed==0)) return;
        RGBQUAD* ppal=GetPalette();
-       DWORD m=min(nColors,head.biClrUsed);
+       DWORD m=__min(nColors,head.biClrUsed);
        for (DWORD i=0; i<m;i++){
                ppal[i].rgbRed=rgb[i].r;
                ppal[i].rgbGreen=rgb[i].g;
@@ -469,7 +469,7 @@ void CxImage::SetPalette(rgb_color *rgb,DWORD nColors)
 void CxImage::SetPalette(RGBQUAD* pPal,DWORD nColors)
 {
        if ((pPal==NULL)||(pDib==NULL)||(head.biClrUsed==0)) return;
-       memcpy(GetPalette(),pPal,min(GetPaletteSize(),nColors*sizeof(RGBQUAD)));
+       memcpy(GetPalette(),pPal,__min(GetPaletteSize(),nColors*sizeof(RGBQUAD)));
        info.last_c_isvalid = false;
 }
 ////////////////////////////////////////////////////////////////////////////////
@@ -654,10 +654,10 @@ void CxImage::SetClrImportant(DWORD ncolors)
 
        switch(head.biBitCount){
        case 1:
-               head.biClrImportant = min(ncolors,2);
+               head.biClrImportant = __min(ncolors,2);
                break;
        case 4:
-               head.biClrImportant = min(ncolors,16);
+               head.biClrImportant = __min(ncolors,16);
                break;
        case 8:
                head.biClrImportant = ncolors;
index 3a7c9a1845be59b8e90f7a23c651ad48e5134060..3e7d20298342be5971b3b88e634180808fd1a959 100644 (file)
@@ -113,15 +113,15 @@ bool CxImage::SelectionAddRect(RECT r, BYTE level)
        if (r.left<r.right) {r2.left=r.left; r2.right=r.right; } else {r2.left=r.right ; r2.right=r.left; }
        if (r.bottom<r.top) {r2.bottom=r.bottom; r2.top=r.top; } else {r2.bottom=r.top ; r2.top=r.bottom; }
 
-       if (info.rSelectionBox.top <= r2.top) info.rSelectionBox.top = max(0L,min(head.biHeight,r2.top+1));
-       if (info.rSelectionBox.left > r2.left) info.rSelectionBox.left = max(0L,min(head.biWidth,r2.left));
-       if (info.rSelectionBox.right <= r2.right) info.rSelectionBox.right = max(0L,min(head.biWidth,r2.right+1));
-       if (info.rSelectionBox.bottom > r2.bottom) info.rSelectionBox.bottom = max(0L,min(head.biHeight,r2.bottom));
+       if (info.rSelectionBox.top <= r2.top) info.rSelectionBox.top = __max(0L,__min(head.biHeight,r2.top+1));
+       if (info.rSelectionBox.left > r2.left) info.rSelectionBox.left = __max(0L,__min(head.biWidth,r2.left));
+       if (info.rSelectionBox.right <= r2.right) info.rSelectionBox.right = __max(0L,__min(head.biWidth,r2.right+1));
+       if (info.rSelectionBox.bottom > r2.bottom) info.rSelectionBox.bottom = __max(0L,__min(head.biHeight,r2.bottom));
 
-       long ymin = max(0L,min(head.biHeight,r2.bottom));
-       long ymax = max(0L,min(head.biHeight,r2.top+1));
-       long xmin = max(0L,min(head.biWidth,r2.left));
-       long xmax = max(0L,min(head.biWidth,r2.right+1));
+       long ymin = __max(0L,__min(head.biHeight,r2.bottom));
+       long ymax = __max(0L,__min(head.biHeight,r2.top+1));
+       long xmin = __max(0L,__min(head.biWidth,r2.left));
+       long xmax = __max(0L,__min(head.biWidth,r2.right+1));
 
        for (long y=ymin; y<ymax; y++)
                memset(pSelection + xmin + y * head.biWidth, level, xmax-xmin);
@@ -137,25 +137,25 @@ bool CxImage::SelectionAddEllipse(RECT r, BYTE level)
        if (pSelection==NULL) SelectionCreate();
        if (pSelection==NULL) return false;
 
-       long xradius = abs(r.right - r.left)/2;
-       long yradius = abs(r.top - r.bottom)/2;
+       long xradius = labs(r.right - r.left)/2;
+       long yradius = labs(r.top - r.bottom)/2;
        if (xradius==0 || yradius==0) return false;
 
        long xcenter = (r.right + r.left)/2;
        long ycenter = (r.top + r.bottom)/2;
 
-       if (info.rSelectionBox.left > (xcenter - xradius)) info.rSelectionBox.left = max(0L,min(head.biWidth,(xcenter - xradius)));
-       if (info.rSelectionBox.right <= (xcenter + xradius)) info.rSelectionBox.right = max(0L,min(head.biWidth,(xcenter + xradius + 1)));
-       if (info.rSelectionBox.bottom > (ycenter - yradius)) info.rSelectionBox.bottom = max(0L,min(head.biHeight,(ycenter - yradius)));
-       if (info.rSelectionBox.top <= (ycenter + yradius)) info.rSelectionBox.top = max(0L,min(head.biHeight,(ycenter + yradius + 1)));
+       if (info.rSelectionBox.left > (xcenter - xradius)) info.rSelectionBox.left = __max(0L,__min(head.biWidth,(xcenter - xradius)));
+       if (info.rSelectionBox.right <= (xcenter + xradius)) info.rSelectionBox.right = __max(0L,__min(head.biWidth,(xcenter + xradius + 1)));
+       if (info.rSelectionBox.bottom > (ycenter - yradius)) info.rSelectionBox.bottom = __max(0L,__min(head.biHeight,(ycenter - yradius)));
+       if (info.rSelectionBox.top <= (ycenter + yradius)) info.rSelectionBox.top = __max(0L,__min(head.biHeight,(ycenter + yradius + 1)));
 
-       long xmin = max(0L,min(head.biWidth,xcenter - xradius));
-       long xmax = max(0L,min(head.biWidth,xcenter + xradius + 1));
-       long ymin = max(0L,min(head.biHeight,ycenter - yradius));
-       long ymax = max(0L,min(head.biHeight,ycenter + yradius + 1));
+       long xmin = __max(0L,__min(head.biWidth,xcenter - xradius));
+       long xmax = __max(0L,__min(head.biWidth,xcenter + xradius + 1));
+       long ymin = __max(0L,__min(head.biHeight,ycenter - yradius));
+       long ymax = __max(0L,__min(head.biHeight,ycenter + yradius + 1));
 
        long y,yo;
-       for (y=ymin; y<min(ycenter,ymax); y++){
+       for (y=ymin; y<__min(ycenter,ymax); y++){
                for (long x=xmin; x<xmax; x++){
                        yo = (long)(ycenter - yradius * sqrt(1-pow((float)(x - xcenter)/(float)xradius,2)));
                        if (yo<y) pSelection[x + y * head.biWidth] = level;
@@ -268,10 +268,10 @@ bool CxImage::SelectionAddPolygon(POINT *points, long npoints, BYTE level)
                RECT r2;
                if (current->x < next->x) {r2.left=current->x; r2.right=next->x; } else {r2.left=next->x ; r2.right=current->x; }
                if (current->y < next->y) {r2.bottom=current->y; r2.top=next->y; } else {r2.bottom=next->y ; r2.top=current->y; }
-               if (localbox.top < r2.top) localbox.top = max(0L,min(head.biHeight-1,r2.top+1));
-               if (localbox.left > r2.left) localbox.left = max(0L,min(head.biWidth-1,r2.left-1));
-               if (localbox.right < r2.right) localbox.right = max(0L,min(head.biWidth-1,r2.right+1));
-               if (localbox.bottom > r2.bottom) localbox.bottom = max(0L,min(head.biHeight-1,r2.bottom-1));
+               if (localbox.top < r2.top) localbox.top = __max(0L,__min(head.biHeight-1,r2.top+1));
+               if (localbox.left > r2.left) localbox.left = __max(0L,__min(head.biWidth-1,r2.left-1));
+               if (localbox.right < r2.right) localbox.right = __max(0L,__min(head.biWidth-1,r2.right+1));
+               if (localbox.bottom > r2.bottom) localbox.bottom = __max(0L,__min(head.biHeight-1,r2.bottom-1));
 
                i++;
        }
@@ -385,10 +385,10 @@ bool CxImage::SelectionAddPolygon(POINT *points, long npoints, BYTE level)
                for (x=localbox.left; x<=localbox.right; x++)
                        if (plocal[x + yoffset]!=1) pSelection[x + yoffset]=level;
        }
-       if (info.rSelectionBox.top <= localbox.top) info.rSelectionBox.top = min(head.biHeight,localbox.top + 1);
-       if (info.rSelectionBox.left > localbox.left) info.rSelectionBox.left = min(head.biWidth,localbox.left);
-       if (info.rSelectionBox.right <= localbox.right) info.rSelectionBox.right = min(head.biWidth,localbox.right + 1);
-       if (info.rSelectionBox.bottom > localbox.bottom) info.rSelectionBox.bottom = min(head.biHeight,localbox.bottom);
+       if (info.rSelectionBox.top <= localbox.top) info.rSelectionBox.top = __min(head.biHeight,localbox.top + 1);
+       if (info.rSelectionBox.left > localbox.left) info.rSelectionBox.left = __min(head.biWidth,localbox.left);
+       if (info.rSelectionBox.right <= localbox.right) info.rSelectionBox.right = __min(head.biWidth,localbox.right + 1);
+       if (info.rSelectionBox.bottom > localbox.bottom) info.rSelectionBox.bottom = __min(head.biHeight,localbox.bottom);
 
        free(plocal);
        free(pix);
index 37533e2280c0e6c0f4227d4a9ee467e0179b6ee2..8d8cf13d0da839ed8498cc2eaaefd7fbc0335546 100644 (file)
@@ -64,10 +64,10 @@ CxRect2 CxRect2::CrossSection(CxRect2 const &r2) const
  */
 {
   CxRect2 cs;
-  cs.botLeft.x=max(botLeft.x, r2.botLeft.x);
-  cs.botLeft.y=max(botLeft.y, r2.botLeft.y);
-  cs.topRight.x=min(topRight.x, r2.topRight.x);
-  cs.topRight.y=min(topRight.y, r2.topRight.y);
+  cs.botLeft.x=__max(botLeft.x, r2.botLeft.x);
+  cs.botLeft.y=__max(botLeft.y, r2.botLeft.y);
+  cs.topRight.x=__min(topRight.x, r2.topRight.x);
+  cs.topRight.y=__min(topRight.y, r2.topRight.y);
   if (cs.botLeft.x<=cs.topRight.x && cs.botLeft.y<=cs.topRight.y) {
     return cs;
   } else {
index d3aac0182ae0e0532080d1c4c72e4abcf7c110e7..dd635cf1185368b1a81311688bd02b540c21916f 100644 (file)
@@ -282,12 +282,12 @@ bool CxImage::RotateLeft(CxImage* iDst)
                        for (ys = 0; ys < newHeight; ys+=RBLOCK) {
                                if (head.biBitCount==24) {
                                        //RGB24 optimized pixel access:
-                                       for (x = xs; x < min(newWidth, xs+RBLOCK); x++){    //do rotation
+                                       for (x = xs; x < __min(newWidth, xs+RBLOCK); x++){    //do rotation
                                                info.nProgress = (long)(100*x/newWidth);
                                                x2=newWidth-x-1;
                                                dstPtr = (BYTE*) imgDest.BlindGetPixelPointer(x,ys);
                                                srcPtr = (BYTE*) BlindGetPixelPointer(ys, x2);
-                                               for (y = ys; y < min(newHeight, ys+RBLOCK); y++){
+                                               for (y = ys; y < __min(newHeight, ys+RBLOCK); y++){
                                                        //imgDest.SetPixelColor(x, y, GetPixelColor(y, x2));
                                                        *(dstPtr) = *(srcPtr);
                                                        *(dstPtr+1) = *(srcPtr+1);
@@ -298,19 +298,19 @@ bool CxImage::RotateLeft(CxImage* iDst)
                                        }//for x
                                } else {
                                        //anything else than 24bpp (and 1bpp): palette
-                                       for (x = xs; x < min(newWidth, xs+RBLOCK); x++){
+                                       for (x = xs; x < __min(newWidth, xs+RBLOCK); x++){
                                                info.nProgress = (long)(100*x/newWidth); //<Anatoly Ivasyuk>
                                                x2=newWidth-x-1;
-                                               for (y = ys; y < min(newHeight, ys+RBLOCK); y++){
+                                               for (y = ys; y < __min(newHeight, ys+RBLOCK); y++){
                                                        imgDest.SetPixelIndex(x, y, BlindGetPixelIndex(y, x2));
                                                }//for y
                                        }//for x
                                }//if (version selection)
 #if CXIMAGE_SUPPORT_ALPHA
                                if (AlphaIsValid()) {
-                                       for (x = xs; x < min(newWidth, xs+RBLOCK); x++){
+                                       for (x = xs; x < __min(newWidth, xs+RBLOCK); x++){
                                                x2=newWidth-x-1;
-                                               for (y = ys; y < min(newHeight, ys+RBLOCK); y++){
+                                               for (y = ys; y < __min(newHeight, ys+RBLOCK); y++){
                                                        imgDest.AlphaSet(x,y,BlindAlphaGet(y, x2));
                                                }//for y
                                        }//for x
@@ -323,9 +323,9 @@ bool CxImage::RotateLeft(CxImage* iDst)
                                        imgDest.info.rSelectionBox.right = newWidth-info.rSelectionBox.bottom;
                                        imgDest.info.rSelectionBox.bottom = info.rSelectionBox.left;
                                        imgDest.info.rSelectionBox.top = info.rSelectionBox.right;
-                                       for (x = xs; x < min(newWidth, xs+RBLOCK); x++){
+                                       for (x = xs; x < __min(newWidth, xs+RBLOCK); x++){
                                                x2=newWidth-x-1;
-                                               for (y = ys; y < min(newHeight, ys+RBLOCK); y++){
+                                               for (y = ys; y < __min(newHeight, ys+RBLOCK); y++){
                                                        imgDest.SelectionSet(x,y,BlindSelectionGet(y, x2));
                                                }//for y
                                        }//for x
@@ -427,12 +427,12 @@ bool CxImage::RotateRight(CxImage* iDst)
                        for (ys = 0; ys < newHeight; ys+=RBLOCK) {
                                if (head.biBitCount==24) {
                                        //RGB24 optimized pixel access:
-                                       for (y = ys; y < min(newHeight, ys+RBLOCK); y++){
+                                       for (y = ys; y < __min(newHeight, ys+RBLOCK); y++){
                                                info.nProgress = (long)(100*y/newHeight); //<Anatoly Ivasyuk>
                                                y2=newHeight-y-1;
                                                dstPtr = (BYTE*) imgDest.BlindGetPixelPointer(xs,y);
                                                srcPtr = (BYTE*) BlindGetPixelPointer(y2, xs);
-                                               for (x = xs; x < min(newWidth, xs+RBLOCK); x++){
+                                               for (x = xs; x < __min(newWidth, xs+RBLOCK); x++){
                                                        //imgDest.SetPixelColor(x, y, GetPixelColor(y2, x));
                                                        *(dstPtr) = *(srcPtr);
                                                        *(dstPtr+1) = *(srcPtr+1);
@@ -443,19 +443,19 @@ bool CxImage::RotateRight(CxImage* iDst)
                                        }//for y
                                } else {
                                        //anything else than BW & RGB24: palette
-                                       for (y = ys; y < min(newHeight, ys+RBLOCK); y++){
+                                       for (y = ys; y < __min(newHeight, ys+RBLOCK); y++){
                                                info.nProgress = (long)(100*y/newHeight); //<Anatoly Ivasyuk>
                                                y2=newHeight-y-1;
-                                               for (x = xs; x < min(newWidth, xs+RBLOCK); x++){
+                                               for (x = xs; x < __min(newWidth, xs+RBLOCK); x++){
                                                        imgDest.SetPixelIndex(x, y, BlindGetPixelIndex(y2, x));
                                                }//for x
                                        }//for y
                                }//if
 #if CXIMAGE_SUPPORT_ALPHA
                                if (AlphaIsValid()){
-                                       for (y = ys; y < min(newHeight, ys+RBLOCK); y++){
+                                       for (y = ys; y < __min(newHeight, ys+RBLOCK); y++){
                                                y2=newHeight-y-1;
-                                               for (x = xs; x < min(newWidth, xs+RBLOCK); x++){
+                                               for (x = xs; x < __min(newWidth, xs+RBLOCK); x++){
                                                        imgDest.AlphaSet(x,y,BlindAlphaGet(y2, x));
                                                }//for x
                                        }//for y
@@ -468,9 +468,9 @@ bool CxImage::RotateRight(CxImage* iDst)
                                        imgDest.info.rSelectionBox.right = info.rSelectionBox.top;
                                        imgDest.info.rSelectionBox.bottom = newHeight-info.rSelectionBox.right;
                                        imgDest.info.rSelectionBox.top = newHeight-info.rSelectionBox.left;
-                                       for (y = ys; y < min(newHeight, ys+RBLOCK); y++){
+                                       for (y = ys; y < __min(newHeight, ys+RBLOCK); y++){
                                                y2=newHeight-y-1;
-                                               for (x = xs; x < min(newWidth, xs+RBLOCK); x++){
+                                               for (x = xs; x < __min(newWidth, xs+RBLOCK); x++){
                                                        imgDest.SelectionSet(x,y,BlindSelectionGet(y2, x));
                                                }//for x
                                        }//for y
@@ -588,10 +588,10 @@ bool CxImage::Rotate(float angle, CxImage* iDst)
        newP4.x = (float)(p4.x*cos_angle - p4.y*sin_angle);
        newP4.y = (float)(p4.x*sin_angle + p4.y*cos_angle);
 
-       leftTop.x = min(min(newP1.x,newP2.x),min(newP3.x,newP4.x));
-       leftTop.y = min(min(newP1.y,newP2.y),min(newP3.y,newP4.y));
-       rightBottom.x = max(max(newP1.x,newP2.x),max(newP3.x,newP4.x));
-       rightBottom.y = max(max(newP1.y,newP2.y),max(newP3.y,newP4.y));
+       leftTop.x = __min(__min(newP1.x,newP2.x),__min(newP3.x,newP4.x));
+       leftTop.y = __min(__min(newP1.y,newP2.y),__min(newP3.y,newP4.y));
+       rightBottom.x = __max(__max(newP1.x,newP2.x),__max(newP3.x,newP4.x));
+       rightBottom.y = __max(__max(newP1.y,newP2.y),__max(newP3.y,newP4.y));
        leftBottom.x = leftTop.x;
        leftBottom.y = rightBottom.y;
        rightTop.x = rightBottom.x;
@@ -720,10 +720,10 @@ bool CxImage::Rotate2(float angle,
        }//if
 
        //(read new dimensions from location of corners)
-       float minx = (float) min(min(newp[0].x,newp[1].x),min(newp[2].x,newp[3].x));
-       float miny = (float) min(min(newp[0].y,newp[1].y),min(newp[2].y,newp[3].y));
-       float maxx = (float) max(max(newp[0].x,newp[1].x),max(newp[2].x,newp[3].x));
-       float maxy = (float) max(max(newp[0].y,newp[1].y),max(newp[2].y,newp[3].y));
+       float minx = (float) __min(__min(newp[0].x,newp[1].x),__min(newp[2].x,newp[3].x));
+       float miny = (float) __min(__min(newp[0].y,newp[1].y),__min(newp[2].y,newp[3].y));
+       float maxx = (float) __max(__max(newp[0].x,newp[1].x),__max(newp[2].x,newp[3].x));
+       float maxy = (float) __max(__max(newp[0].y,newp[1].y),__max(newp[2].y,newp[3].y));
        int newWidth = (int) floor(maxx-minx+0.5f);
        int newHeight= (int) floor(maxy-miny+0.5f);
        float ssx=((maxx+minx)- ((float) newWidth-1))/2.0f;   //start for x
@@ -983,12 +983,12 @@ bool CxImage::Resample(long newx, long newy, int mode, CxImage* iDst)
                                if (info.nEscape) break;
                                fY = y * yScale;
                                ifY = (int)fY;
-                               ifY1 = min(ymax, ifY+1);
+                               ifY1 = __min(ymax, ifY+1);
                                dy = fY - ifY;
                                for(long x=0; x<newx; x++){
                                        fX = x * xScale;
                                        ifX = (int)fX;
-                                       ifX1 = min(xmax, ifX+1);
+                                       ifX1 = __min(xmax, ifX+1);
                                        dx = fX - ifX;
                                        // Interpolate using the four nearest pixels in the source
                                        if (head.biClrUsed){
@@ -1308,9 +1308,9 @@ bool CxImage::DecreaseBpp(DWORD nbit, bool errordiffusion, RGBQUAD* ppal, DWORD
                                eb=(long)c.rgbBlue - (long)ce.rgbBlue;
 
                                c = GetPixelColor(x+1,y);
-                               c.rgbRed = (BYTE)min(255L,max(0L,(long)c.rgbRed + ((er*7)/16)));
-                               c.rgbGreen = (BYTE)min(255L,max(0L,(long)c.rgbGreen + ((eg*7)/16)));
-                               c.rgbBlue = (BYTE)min(255L,max(0L,(long)c.rgbBlue + ((eb*7)/16)));
+                               c.rgbRed = (BYTE)__min(255L,__max(0L,(long)c.rgbRed + ((er*7)/16)));
+                               c.rgbGreen = (BYTE)__min(255L,__max(0L,(long)c.rgbGreen + ((eg*7)/16)));
+                               c.rgbBlue = (BYTE)__min(255L,__max(0L,(long)c.rgbBlue + ((eb*7)/16)));
                                SetPixelColor(x+1,y,c);
                                int coeff=1;
                                for(int i=-1; i<2; i++){
@@ -1323,9 +1323,9 @@ bool CxImage::DecreaseBpp(DWORD nbit, bool errordiffusion, RGBQUAD* ppal, DWORD
                                                coeff=1; break;
                                        }
                                        c = GetPixelColor(x+i,y+1);
-                                       c.rgbRed = (BYTE)min(255L,max(0L,(long)c.rgbRed + ((er * coeff)/16)));
-                                       c.rgbGreen = (BYTE)min(255L,max(0L,(long)c.rgbGreen + ((eg * coeff)/16)));
-                                       c.rgbBlue = (BYTE)min(255L,max(0L,(long)c.rgbBlue + ((eb * coeff)/16)));
+                                       c.rgbRed = (BYTE)__min(255L,__max(0L,(long)c.rgbRed + ((er * coeff)/16)));
+                                       c.rgbGreen = (BYTE)__min(255L,__max(0L,(long)c.rgbGreen + ((eg * coeff)/16)));
+                                       c.rgbBlue = (BYTE)__min(255L,__max(0L,(long)c.rgbBlue + ((eb * coeff)/16)));
                                        SetPixelColor(x+i,y+1,c);
                                }
                        }
@@ -1546,10 +1546,10 @@ bool CxImage::Dither(long method)
                                }
 
                                nlevel = GetPixelIndex(x + 1, y) + (error * 8) / TotalCoeffSum;
-                               level = (BYTE)min(255, max(0, (int)nlevel));
+                               level = (BYTE)__min(255, __max(0, (int)nlevel));
                                SetPixelIndex(x + 1, y, level);
                                nlevel = GetPixelIndex(x + 2, y) + (error * 4) / TotalCoeffSum;
-                               level = (BYTE)min(255, max(0, (int)nlevel));
+                               level = (BYTE)__min(255, __max(0, (int)nlevel));
                                SetPixelIndex(x + 2, y, level);
                                int i;
                                for (i = -2; i < 3; i++) {
@@ -1571,7 +1571,7 @@ bool CxImage::Dither(long method)
                                                break;
                                        }
                                        nlevel = GetPixelIndex(x + i, y + 1) + (error * coeff) / TotalCoeffSum;
-                                       level = (BYTE)min(255, max(0, (int)nlevel));
+                                       level = (BYTE)__min(255, __max(0, (int)nlevel));
                                        SetPixelIndex(x + i, y + 1, level);
                                }
                        }
@@ -1600,10 +1600,10 @@ bool CxImage::Dither(long method)
                                }
 
                                nlevel = GetPixelIndex(x + 1, y) + (error * 8) / TotalCoeffSum;
-                               level = (BYTE)min(255, max(0, (int)nlevel));
+                               level = (BYTE)__min(255, __max(0, (int)nlevel));
                                SetPixelIndex(x + 1, y, level);
                                nlevel = GetPixelIndex(x + 2, y) + (error * 4) / TotalCoeffSum;
-                               level = (BYTE)min(255, max(0, (int)nlevel));
+                               level = (BYTE)__min(255, __max(0, (int)nlevel));
                                SetPixelIndex(x + 2, y, level);
                                int i;
                                for (i = -2; i < 3; i++) {
@@ -1625,7 +1625,7 @@ bool CxImage::Dither(long method)
                                                break;
                                        }
                                        nlevel = GetPixelIndex(x + i, y + 1) + (error * coeff) / TotalCoeffSum;
-                                       level = (BYTE)min(255, max(0, (int)nlevel));
+                                       level = (BYTE)__min(255, __max(0, (int)nlevel));
                                        SetPixelIndex(x + i, y + 1, level);
                                }
                                for (i = -2; i < 3; i++) {
@@ -1647,7 +1647,7 @@ bool CxImage::Dither(long method)
                                                break;
                                        }
                                        nlevel = GetPixelIndex(x + i, y + 2) + (error * coeff) / TotalCoeffSum;
-                                       level = (BYTE)min(255, max(0, (int)nlevel));
+                                       level = (BYTE)__min(255, __max(0, (int)nlevel));
                                        SetPixelIndex(x + i, y + 2, level);
                                }
                        }
@@ -1676,10 +1676,10 @@ bool CxImage::Dither(long method)
                                }
 
                                nlevel = GetPixelIndex(x + 1, y) + (error * 7) / TotalCoeffSum;
-                               level = (BYTE)min(255, max(0, (int)nlevel));
+                               level = (BYTE)__min(255, __max(0, (int)nlevel));
                                SetPixelIndex(x + 1, y, level);
                                nlevel = GetPixelIndex(x + 2, y) + (error * 5) / TotalCoeffSum;
-                               level = (BYTE)min(255, max(0, (int)nlevel));
+                               level = (BYTE)__min(255, __max(0, (int)nlevel));
                                SetPixelIndex(x + 2, y, level);
                                int i;
                                for (i = -2; i < 3; i++) {
@@ -1701,7 +1701,7 @@ bool CxImage::Dither(long method)
                                                break;
                                        }
                                        nlevel = GetPixelIndex(x + i, y + 1) + (error * coeff) / TotalCoeffSum;
-                                       level = (BYTE)min(255, max(0, (int)nlevel));
+                                       level = (BYTE)__min(255, __max(0, (int)nlevel));
                                        SetPixelIndex(x + i, y + 1, level);
                                }
                                for (i = -2; i < 3; i++) {
@@ -1723,7 +1723,7 @@ bool CxImage::Dither(long method)
                                                break;
                                        }
                                        nlevel = GetPixelIndex(x + i, y + 2) + (error * coeff) / TotalCoeffSum;
-                                       level = (BYTE)min(255, max(0, (int)nlevel));
+                                       level = (BYTE)__min(255, __max(0, (int)nlevel));
                                        SetPixelIndex(x + i, y + 2, level);
                                }
                        }
@@ -1752,10 +1752,10 @@ bool CxImage::Dither(long method)
                                }
 
                                nlevel = GetPixelIndex(x + 1, y) + (error * 5) / TotalCoeffSum;
-                               level = (BYTE)min(255, max(0, (int)nlevel));
+                               level = (BYTE)__min(255, __max(0, (int)nlevel));
                                SetPixelIndex(x + 1, y, level);
                                nlevel = GetPixelIndex(x + 2, y) + (error * 3) / TotalCoeffSum;
-                               level = (BYTE)min(255, max(0, (int)nlevel));
+                               level = (BYTE)__min(255, __max(0, (int)nlevel));
                                SetPixelIndex(x + 2, y, level);
                                int i;
                                for (i = -2; i < 3; i++) {
@@ -1777,7 +1777,7 @@ bool CxImage::Dither(long method)
                                                break;
                                        }
                                        nlevel = GetPixelIndex(x + i, y + 1) + (error * coeff) / TotalCoeffSum;
-                                       level = (BYTE)min(255, max(0, (int)nlevel));
+                                       level = (BYTE)__min(255, __max(0, (int)nlevel));
                                        SetPixelIndex(x + i, y + 1, level);
                                }
                                for (i = -1; i < 2; i++) {
@@ -1793,7 +1793,7 @@ bool CxImage::Dither(long method)
                                                break;
                                        }
                                        nlevel = GetPixelIndex(x + i, y + 2) + (error * coeff) / TotalCoeffSum;
-                                       level = (BYTE)min(255, max(0, (int)nlevel));
+                                       level = (BYTE)__min(255, __max(0, (int)nlevel));
                                        SetPixelIndex(x + i, y + 2, level);
                                }
                        }
@@ -1825,76 +1825,76 @@ bool CxImage::Dither(long method)
                                int tmp_index_y = y;
                                int tmp_coeff = 32;
                                nlevel = GetPixelIndex(tmp_index_x, tmp_index_y) + (error * tmp_coeff) / TotalCoeffSum;
-                               level = (BYTE)min(255, max(0, (int)nlevel));
+                               level = (BYTE)__min(255, __max(0, (int)nlevel));
                                SetPixelIndex(tmp_index_x, tmp_index_y, level);
 
                                tmp_index_x = x - 3;
                                tmp_index_y = y + 1;
                                tmp_coeff = 12;
                                nlevel = GetPixelIndex(tmp_index_x, tmp_index_y) + (error * tmp_coeff) / TotalCoeffSum;
-                               level = (BYTE)min(255, max(0, (int)nlevel));
+                               level = (BYTE)__min(255, __max(0, (int)nlevel));
                                SetPixelIndex(tmp_index_x, tmp_index_y, level);
 
                                tmp_index_x = x - 1;
                                tmp_coeff = 26;
                                nlevel = GetPixelIndex(tmp_index_x, tmp_index_y) + (error * tmp_coeff) / TotalCoeffSum;
-                               level = (BYTE)min(255, max(0, (int)nlevel));
+                               level = (BYTE)__min(255, __max(0, (int)nlevel));
                                SetPixelIndex(tmp_index_x, tmp_index_y, level);
 
                                tmp_index_x = x + 1;
                                tmp_coeff = 30;
                                nlevel = GetPixelIndex(tmp_index_x, tmp_index_y) + (error * tmp_coeff) / TotalCoeffSum;
-                               level = (BYTE)min(255, max(0, (int)nlevel));
+                               level = (BYTE)__min(255, __max(0, (int)nlevel));
                                SetPixelIndex(tmp_index_x, tmp_index_y, level);
 
                                tmp_index_x = x + 3;
                                tmp_coeff = 16;
                                nlevel = GetPixelIndex(tmp_index_x, tmp_index_y) + (error * tmp_coeff) / TotalCoeffSum;
-                               level = (BYTE)min(255, max(0, (int)nlevel));
+                               level = (BYTE)__min(255, __max(0, (int)nlevel));
                                SetPixelIndex(tmp_index_x, tmp_index_y, level);
 
                                tmp_index_x = x - 2;
                                tmp_index_y = y + 2;
                                tmp_coeff = 12;
                                nlevel = GetPixelIndex(tmp_index_x, tmp_index_y) + (error * tmp_coeff) / TotalCoeffSum;
-                               level = (BYTE)min(255, max(0, (int)nlevel));
+                               level = (BYTE)__min(255, __max(0, (int)nlevel));
                                SetPixelIndex(tmp_index_x, tmp_index_y, level);
 
                                tmp_index_x = x;
                                tmp_coeff = 26;
                                nlevel = GetPixelIndex(tmp_index_x, tmp_index_y) + (error * tmp_coeff) / TotalCoeffSum;
-                               level = (BYTE)min(255, max(0, (int)nlevel));
+                               level = (BYTE)__min(255, __max(0, (int)nlevel));
                                SetPixelIndex(tmp_index_x, tmp_index_y, level);
 
                                tmp_index_x = x + 2;
                                tmp_coeff = 12;
                                nlevel = GetPixelIndex(tmp_index_x, tmp_index_y) + (error * tmp_coeff) / TotalCoeffSum;
-                               level = (BYTE)min(255, max(0, (int)nlevel));
+                               level = (BYTE)__min(255, __max(0, (int)nlevel));
                                SetPixelIndex(tmp_index_x, tmp_index_y, level);
 
                                tmp_index_x = x - 3;
                                tmp_index_y = y + 3;
                                tmp_coeff = 5;
                                nlevel = GetPixelIndex(tmp_index_x, tmp_index_y) + (error * tmp_coeff) / TotalCoeffSum;
-                               level = (BYTE)min(255, max(0, (int)nlevel));
+                               level = (BYTE)__min(255, __max(0, (int)nlevel));
                                SetPixelIndex(tmp_index_x, tmp_index_y, level);
 
                                tmp_index_x = x - 1;
                                tmp_coeff = 12;
                                nlevel = GetPixelIndex(tmp_index_x, tmp_index_y) + (error * tmp_coeff) / TotalCoeffSum;
-                               level = (BYTE)min(255, max(0, (int)nlevel));
+                               level = (BYTE)__min(255, __max(0, (int)nlevel));
                                SetPixelIndex(tmp_index_x, tmp_index_y, level);
 
                                tmp_index_x = x + 1;
                                tmp_coeff = 12;
                                nlevel = GetPixelIndex(tmp_index_x, tmp_index_y) + (error * tmp_coeff) / TotalCoeffSum;
-                               level = (BYTE)min(255, max(0, (int)nlevel));
+                               level = (BYTE)__min(255, __max(0, (int)nlevel));
                                SetPixelIndex(tmp_index_x, tmp_index_y, level);
 
                                tmp_index_x = x + 3;
                                tmp_coeff = 5;
                                nlevel = GetPixelIndex(tmp_index_x, tmp_index_y) + (error * tmp_coeff) / TotalCoeffSum;
-                               level = (BYTE)min(255, max(0, (int)nlevel));
+                               level = (BYTE)__min(255, __max(0, (int)nlevel));
                                SetPixelIndex(tmp_index_x, tmp_index_y, level);
                        }
                }
@@ -1921,7 +1921,7 @@ bool CxImage::Dither(long method)
                        Bmatrix[i] = (BYTE)(dither);
                }
 
-               int scale = max(0,(8-2*order));
+               int scale = __max(0,(8-2*order));
                int level;
                for (long y=0;y<head.biHeight;y++){
                        info.nProgress = (long)(100*y/head.biHeight);
@@ -1961,7 +1961,7 @@ bool CxImage::Dither(long method)
                                }
 
                                nlevel = GetPixelIndex(x+1,y) + (error * 7)/16;
-                               level = (BYTE)min(255,max(0,(int)nlevel));
+                               level = (BYTE)__min(255,__max(0,(int)nlevel));
                                SetPixelIndex(x+1,y,level);
                                for(int i=-1; i<2; i++){
                                        switch(i){
@@ -1973,7 +1973,7 @@ bool CxImage::Dither(long method)
                                                coeff=1; break;
                                        }
                                        nlevel = GetPixelIndex(x+i,y+1) + (error * coeff)/16;
-                                       level = (BYTE)min(255,max(0,(int)nlevel));
+                                       level = (BYTE)__min(255,__max(0,(int)nlevel));
                                        SetPixelIndex(x+i,y+1,level);
                                }
                        }
@@ -2011,7 +2011,7 @@ bool CxImage::CropRotatedRectangle( long topx, long topy, long width, long heigh
        if ( fabs(angle)<0.0002 )
                return Crop( topx, topy, topx+width, topy+height, iDst);
 
-       startx = min(topx, topx - (long)(sin_angle*(double)height));
+       startx = __min(topx, topx - (long)(sin_angle*(double)height));
        endx   = topx + (long)(cos_angle*(double)width);
        endy   = topy + (long)(cos_angle*(double)height + sin_angle*(double)width);
        // check: corners of the rectangle must be inside
@@ -2059,10 +2059,10 @@ bool CxImage::Crop(long left, long top, long right, long bottom, CxImage* iDst)
 {
        if (!pDib) return false;
 
-       long startx = max(0L,min(left,head.biWidth));
-       long endx = max(0L,min(right,head.biWidth));
-       long starty = head.biHeight - max(0L,min(top,head.biHeight));
-       long endy = head.biHeight - max(0L,min(bottom,head.biHeight));
+       long startx = __max(0L,__min(left,head.biWidth));
+       long endx = __max(0L,__min(right,head.biWidth));
+       long starty = head.biHeight - __max(0L,__min(top,head.biHeight));
+       long endy = head.biHeight - __max(0L,__min(bottom,head.biHeight));
 
        if (startx==endx || starty==endy) return false;
 
diff --git a/utilities/pugixml/pugiconfig.hpp b/utilities/pugixml/pugiconfig.hpp
new file mode 100644 (file)
index 0000000..f739e06
--- /dev/null
@@ -0,0 +1,74 @@
+/**
+ * pugixml parser - version 1.9
+ * --------------------------------------------------------
+ * Copyright (C) 2006-2018, by Arseny Kapoulkine (arseny.kapoulkine@gmail.com)
+ * Report bugs and download new versions at http://pugixml.org/
+ *
+ * This library is distributed under the MIT License. See notice at the end
+ * of this file.
+ *
+ * This work is based on the pugxml parser, which is:
+ * Copyright (C) 2003, by Kristen Wegner (kristen@tima.net)
+ */
+
+#ifndef HEADER_PUGICONFIG_HPP
+#define HEADER_PUGICONFIG_HPP
+
+// Uncomment this to enable wchar_t mode
+// #define PUGIXML_WCHAR_MODE
+
+// Uncomment this to enable compact mode
+// #define PUGIXML_COMPACT
+
+// Uncomment this to disable XPath
+// #define PUGIXML_NO_XPATH
+
+// Uncomment this to disable STL
+// #define PUGIXML_NO_STL
+
+// Uncomment this to disable exceptions
+// #define PUGIXML_NO_EXCEPTIONS
+
+// Set this to control attributes for public classes/functions, i.e.:
+// #define PUGIXML_API __declspec(dllexport) // to export all public symbols from DLL
+// #define PUGIXML_CLASS __declspec(dllimport) // to import all classes from DLL
+// #define PUGIXML_FUNCTION __fastcall // to set calling conventions to all public functions to fastcall
+// In absence of PUGIXML_CLASS/PUGIXML_FUNCTION definitions PUGIXML_API is used instead
+
+// Tune these constants to adjust memory-related behavior
+// #define PUGIXML_MEMORY_PAGE_SIZE 32768
+// #define PUGIXML_MEMORY_OUTPUT_STACK 10240
+// #define PUGIXML_MEMORY_XPATH_PAGE_SIZE 4096
+
+// Uncomment this to switch to header-only version
+// #define PUGIXML_HEADER_ONLY
+
+// Uncomment this to enable long long support
+// #define PUGIXML_HAS_LONG_LONG
+
+#endif
+
+/**
+ * Copyright (c) 2006-2018 Arseny Kapoulkine
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
diff --git a/utilities/pugixml/pugixml.cpp b/utilities/pugixml/pugixml.cpp
new file mode 100644 (file)
index 0000000..b811ba7
--- /dev/null
@@ -0,0 +1,12796 @@
+/**
+ * pugixml parser - version 1.9
+ * --------------------------------------------------------
+ * Copyright (C) 2006-2018, by Arseny Kapoulkine (arseny.kapoulkine@gmail.com)
+ * Report bugs and download new versions at http://pugixml.org/
+ *
+ * This library is distributed under the MIT License. See notice at the end
+ * of this file.
+ *
+ * This work is based on the pugxml parser, which is:
+ * Copyright (C) 2003, by Kristen Wegner (kristen@tima.net)
+ */
+
+#ifndef SOURCE_PUGIXML_CPP
+#define SOURCE_PUGIXML_CPP
+
+#include "pugixml.hpp"
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <assert.h>
+#include <limits.h>
+
+#ifdef PUGIXML_WCHAR_MODE
+#      include <wchar.h>
+#endif
+
+#ifndef PUGIXML_NO_XPATH
+#      include <math.h>
+#      include <float.h>
+#endif
+
+#ifndef PUGIXML_NO_STL
+#      include <istream>
+#      include <ostream>
+#      include <string>
+#endif
+
+// For placement new
+#include <new>
+
+#ifdef _MSC_VER
+#      pragma warning(push)
+#      pragma warning(disable: 4127) // conditional expression is constant
+#      pragma warning(disable: 4324) // structure was padded due to __declspec(align())
+#      pragma warning(disable: 4702) // unreachable code
+#      pragma warning(disable: 4996) // this function or variable may be unsafe
+#endif
+
+#if defined(_MSC_VER) && defined(__c2__)
+#      pragma clang diagnostic push
+#      pragma clang diagnostic ignored "-Wdeprecated" // this function or variable may be unsafe
+#endif
+
+#ifdef __INTEL_COMPILER
+#      pragma warning(disable: 177) // function was declared but never referenced
+#      pragma warning(disable: 279) // controlling expression is constant
+#      pragma warning(disable: 1478 1786) // function was declared "deprecated"
+#      pragma warning(disable: 1684) // conversion from pointer to same-sized integral type
+#endif
+
+#if defined(__BORLANDC__) && defined(PUGIXML_HEADER_ONLY)
+#      pragma warn -8080 // symbol is declared but never used; disabling this inside push/pop bracket does not make the warning go away
+#endif
+
+#ifdef __BORLANDC__
+#      pragma option push
+#      pragma warn -8008 // condition is always false
+#      pragma warn -8066 // unreachable code
+#endif
+
+#ifdef __SNC__
+// Using diag_push/diag_pop does not disable the warnings inside templates due to a compiler bug
+#      pragma diag_suppress=178 // function was declared but never referenced
+#      pragma diag_suppress=237 // controlling expression is constant
+#endif
+
+#ifdef __TI_COMPILER_VERSION__
+#      pragma diag_suppress 179 // function was declared but never referenced
+#endif
+
+// Inlining controls
+#if defined(_MSC_VER) && _MSC_VER >= 1300
+#      define PUGI__NO_INLINE __declspec(noinline)
+#elif defined(__GNUC__)
+#      define PUGI__NO_INLINE __attribute__((noinline))
+#else
+#      define PUGI__NO_INLINE
+#endif
+
+// Branch weight controls
+#if defined(__GNUC__) && !defined(__c2__)
+#      define PUGI__UNLIKELY(cond) __builtin_expect(cond, 0)
+#else
+#      define PUGI__UNLIKELY(cond) (cond)
+#endif
+
+// Simple static assertion
+#define PUGI__STATIC_ASSERT(cond) { static const char condition_failed[(cond) ? 1 : -1] = {0}; (void)condition_failed[0]; }
+
+// Digital Mars C++ bug workaround for passing char loaded from memory via stack
+#ifdef __DMC__
+#      define PUGI__DMC_VOLATILE volatile
+#else
+#      define PUGI__DMC_VOLATILE
+#endif
+
+// Integer sanitizer workaround; we only apply this for clang since gcc8 has no_sanitize but not unsigned-integer-overflow and produces "attribute directive ignored" warnings
+#if defined(__clang__) && defined(__has_attribute)
+#      if __has_attribute(no_sanitize)
+#              define PUGI__UNSIGNED_OVERFLOW __attribute__((no_sanitize("unsigned-integer-overflow")))
+#      else
+#              define PUGI__UNSIGNED_OVERFLOW
+#      endif
+#else
+#      define PUGI__UNSIGNED_OVERFLOW
+#endif
+
+// Borland C++ bug workaround for not defining ::memcpy depending on header include order (can't always use std::memcpy because some compilers don't have it at all)
+#if defined(__BORLANDC__) && !defined(__MEM_H_USING_LIST)
+using std::memcpy;
+using std::memmove;
+using std::memset;
+#endif
+
+// Some MinGW/GCC versions have headers that erroneously omit LLONG_MIN/LLONG_MAX/ULLONG_MAX definitions from limits.h in some configurations
+#if defined(PUGIXML_HAS_LONG_LONG) && defined(__GNUC__) && !defined(LLONG_MAX) && !defined(LLONG_MIN) && !defined(ULLONG_MAX)
+#      define LLONG_MIN (-LLONG_MAX - 1LL)
+#      define LLONG_MAX __LONG_LONG_MAX__
+#      define ULLONG_MAX (LLONG_MAX * 2ULL + 1ULL)
+#endif
+
+// In some environments MSVC is a compiler but the CRT lacks certain MSVC-specific features
+#if defined(_MSC_VER) && !defined(__S3E__)
+#      define PUGI__MSVC_CRT_VERSION _MSC_VER
+#endif
+
+// Not all platforms have snprintf; we define a wrapper that uses snprintf if possible. This only works with buffers with a known size.
+#if __cplusplus >= 201103
+#      define PUGI__SNPRINTF(buf, ...) snprintf(buf, sizeof(buf), __VA_ARGS__)
+#elif defined(PUGI__MSVC_CRT_VERSION) && PUGI__MSVC_CRT_VERSION >= 1400
+#      define PUGI__SNPRINTF(buf, ...) _snprintf_s(buf, _countof(buf), _TRUNCATE, __VA_ARGS__)
+#else
+#      define PUGI__SNPRINTF sprintf
+#endif
+
+// We put implementation details into an anonymous namespace in source mode, but have to keep it in non-anonymous namespace in header-only mode to prevent binary bloat.
+#ifdef PUGIXML_HEADER_ONLY
+#      define PUGI__NS_BEGIN namespace pugi { namespace impl {
+#      define PUGI__NS_END } }
+#      define PUGI__FN inline
+#      define PUGI__FN_NO_INLINE inline
+#else
+#      if defined(_MSC_VER) && _MSC_VER < 1300 // MSVC6 seems to have an amusing bug with anonymous namespaces inside namespaces
+#              define PUGI__NS_BEGIN namespace pugi { namespace impl {
+#              define PUGI__NS_END } }
+#      else
+#              define PUGI__NS_BEGIN namespace pugi { namespace impl { namespace {
+#              define PUGI__NS_END } } }
+#      endif
+#      define PUGI__FN
+#      define PUGI__FN_NO_INLINE PUGI__NO_INLINE
+#endif
+
+// uintptr_t
+#if (defined(_MSC_VER) && _MSC_VER < 1600) || (defined(__BORLANDC__) && __BORLANDC__ < 0x561)
+namespace pugi
+{
+#      ifndef _UINTPTR_T_DEFINED
+       typedef size_t uintptr_t;
+#      endif
+
+       typedef unsigned __int8 uint8_t;
+       typedef unsigned __int16 uint16_t;
+       typedef unsigned __int32 uint32_t;
+}
+#else
+#      include <stdint.h>
+#endif
+
+// Memory allocation
+PUGI__NS_BEGIN
+       PUGI__FN void* default_allocate(size_t size)
+       {
+               return malloc(size);
+       }
+
+       PUGI__FN void default_deallocate(void* ptr)
+       {
+               free(ptr);
+       }
+
+       template <typename T>
+       struct xml_memory_management_function_storage
+       {
+               static allocation_function allocate;
+               static deallocation_function deallocate;
+       };
+
+       // Global allocation functions are stored in class statics so that in header mode linker deduplicates them
+       // Without a template<> we'll get multiple definitions of the same static
+       template <typename T> allocation_function xml_memory_management_function_storage<T>::allocate = default_allocate;
+       template <typename T> deallocation_function xml_memory_management_function_storage<T>::deallocate = default_deallocate;
+
+       typedef xml_memory_management_function_storage<int> xml_memory;
+PUGI__NS_END
+
+// String utilities
+PUGI__NS_BEGIN
+       // Get string length
+       PUGI__FN size_t strlength(const char_t* s)
+       {
+               assert(s);
+
+       #ifdef PUGIXML_WCHAR_MODE
+               return wcslen(s);
+       #else
+               return strlen(s);
+       #endif
+       }
+
+       // Compare two strings
+       PUGI__FN bool strequal(const char_t* src, const char_t* dst)
+       {
+               assert(src && dst);
+
+       #ifdef PUGIXML_WCHAR_MODE
+               return wcscmp(src, dst) == 0;
+       #else
+               return strcmp(src, dst) == 0;
+       #endif
+       }
+
+       // Compare lhs with [rhs_begin, rhs_end)
+       PUGI__FN bool strequalrange(const char_t* lhs, const char_t* rhs, size_t count)
+       {
+               for (size_t i = 0; i < count; ++i)
+                       if (lhs[i] != rhs[i])
+                               return false;
+
+               return lhs[count] == 0;
+       }
+
+       // Get length of wide string, even if CRT lacks wide character support
+       PUGI__FN size_t strlength_wide(const wchar_t* s)
+       {
+               assert(s);
+
+       #ifdef PUGIXML_WCHAR_MODE
+               return wcslen(s);
+       #else
+               const wchar_t* end = s;
+               while (*end) end++;
+               return static_cast<size_t>(end - s);
+       #endif
+       }
+PUGI__NS_END
+
+// auto_ptr-like object for exception recovery
+PUGI__NS_BEGIN
+       template <typename T> struct auto_deleter
+       {
+               typedef void (*D)(T*);
+
+               T* data;
+               D deleter;
+
+               auto_deleter(T* data_, D deleter_): data(data_), deleter(deleter_)
+               {
+               }
+
+               ~auto_deleter()
+               {
+                       if (data) deleter(data);
+               }
+
+               T* release()
+               {
+                       T* result = data;
+                       data = 0;
+                       return result;
+               }
+       };
+PUGI__NS_END
+
+#ifdef PUGIXML_COMPACT
+PUGI__NS_BEGIN
+       class compact_hash_table
+       {
+       public:
+               compact_hash_table(): _items(0), _capacity(0), _count(0)
+               {
+               }
+
+               void clear()
+               {
+                       if (_items)
+                       {
+                               xml_memory::deallocate(_items);
+                               _items = 0;
+                               _capacity = 0;
+                               _count = 0;
+                       }
+               }
+
+               void* find(const void* key)
+               {
+                       if (_capacity == 0) return 0;
+
+                       item_t* item = get_item(key);
+                       assert(item);
+                       assert(item->key == key || (item->key == 0 && item->value == 0));
+
+                       return item->value;
+               }
+
+               void insert(const void* key, void* value)
+               {
+                       assert(_capacity != 0 && _count < _capacity - _capacity / 4);
+
+                       item_t* item = get_item(key);
+                       assert(item);
+
+                       if (item->key == 0)
+                       {
+                               _count++;
+                               item->key = key;
+                       }
+
+                       item->value = value;
+               }
+
+               bool reserve(size_t extra = 16)
+               {
+                       if (_count + extra >= _capacity - _capacity / 4)
+                               return rehash(_count + extra);
+
+                       return true;
+               }
+
+       private:
+               struct item_t
+               {
+                       const void* key;
+                       void* value;
+               };
+
+               item_t* _items;
+               size_t _capacity;
+
+               size_t _count;
+
+               bool rehash(size_t count);
+
+               item_t* get_item(const void* key)
+               {
+                       assert(key);
+                       assert(_capacity > 0);
+
+                       size_t hashmod = _capacity - 1;
+                       size_t bucket = hash(key) & hashmod;
+
+                       for (size_t probe = 0; probe <= hashmod; ++probe)
+                       {
+                               item_t& probe_item = _items[bucket];
+
+                               if (probe_item.key == key || probe_item.key == 0)
+                                       return &probe_item;
+
+                               // hash collision, quadratic probing
+                               bucket = (bucket + probe + 1) & hashmod;
+                       }
+
+                       assert(false && "Hash table is full"); // unreachable
+                       return 0;
+               }
+
+               static PUGI__UNSIGNED_OVERFLOW unsigned int hash(const void* key)
+               {
+                       unsigned int h = static_cast<unsigned int>(reinterpret_cast<uintptr_t>(key));
+
+                       // MurmurHash3 32-bit finalizer
+                       h ^= h >> 16;
+                       h *= 0x85ebca6bu;
+                       h ^= h >> 13;
+                       h *= 0xc2b2ae35u;
+                       h ^= h >> 16;
+
+                       return h;
+               }
+       };
+
+       PUGI__FN_NO_INLINE bool compact_hash_table::rehash(size_t count)
+       {
+               size_t capacity = 32;
+               while (count >= capacity - capacity / 4)
+                       capacity *= 2;
+
+               compact_hash_table rt;
+               rt._capacity = capacity;
+               rt._items = static_cast<item_t*>(xml_memory::allocate(sizeof(item_t) * capacity));
+
+               if (!rt._items)
+                       return false;
+
+               memset(rt._items, 0, sizeof(item_t) * capacity);
+
+               for (size_t i = 0; i < _capacity; ++i)
+                       if (_items[i].key)
+                               rt.insert(_items[i].key, _items[i].value);
+
+               if (_items)
+                       xml_memory::deallocate(_items);
+
+               _capacity = capacity;
+               _items = rt._items;
+
+               assert(_count == rt._count);
+
+               return true;
+       }
+
+PUGI__NS_END
+#endif
+
+PUGI__NS_BEGIN
+#ifdef PUGIXML_COMPACT
+       static const uintptr_t xml_memory_block_alignment = 4;
+#else
+       static const uintptr_t xml_memory_block_alignment = sizeof(void*);
+#endif
+
+       // extra metadata bits
+       static const uintptr_t xml_memory_page_contents_shared_mask = 64;
+       static const uintptr_t xml_memory_page_name_allocated_mask = 32;
+       static const uintptr_t xml_memory_page_value_allocated_mask = 16;
+       static const uintptr_t xml_memory_page_type_mask = 15;
+
+       // combined masks for string uniqueness
+       static const uintptr_t xml_memory_page_name_allocated_or_shared_mask = xml_memory_page_name_allocated_mask | xml_memory_page_contents_shared_mask;
+       static const uintptr_t xml_memory_page_value_allocated_or_shared_mask = xml_memory_page_value_allocated_mask | xml_memory_page_contents_shared_mask;
+
+#ifdef PUGIXML_COMPACT
+       #define PUGI__GETHEADER_IMPL(object, page, flags) // unused
+       #define PUGI__GETPAGE_IMPL(header) (header).get_page()
+#else
+       #define PUGI__GETHEADER_IMPL(object, page, flags) (((reinterpret_cast<char*>(object) - reinterpret_cast<char*>(page)) << 8) | (flags))
+       // this macro casts pointers through void* to avoid 'cast increases required alignment of target type' warnings
+       #define PUGI__GETPAGE_IMPL(header) static_cast<impl::xml_memory_page*>(const_cast<void*>(static_cast<const void*>(reinterpret_cast<const char*>(&header) - (header >> 8))))
+#endif
+
+       #define PUGI__GETPAGE(n) PUGI__GETPAGE_IMPL((n)->header)
+       #define PUGI__NODETYPE(n) static_cast<xml_node_type>((n)->header & impl::xml_memory_page_type_mask)
+
+       struct xml_allocator;
+
+       struct xml_memory_page
+       {
+               static xml_memory_page* construct(void* memory)
+               {
+                       xml_memory_page* result = static_cast<xml_memory_page*>(memory);
+
+                       result->allocator = 0;
+                       result->prev = 0;
+                       result->next = 0;
+                       result->busy_size = 0;
+                       result->freed_size = 0;
+
+               #ifdef PUGIXML_COMPACT
+                       result->compact_string_base = 0;
+                       result->compact_shared_parent = 0;
+                       result->compact_page_marker = 0;
+               #endif
+
+                       return result;
+               }
+
+               xml_allocator* allocator;
+
+               xml_memory_page* prev;
+               xml_memory_page* next;
+
+               size_t busy_size;
+               size_t freed_size;
+
+       #ifdef PUGIXML_COMPACT
+               char_t* compact_string_base;
+               void* compact_shared_parent;
+               uint32_t* compact_page_marker;
+       #endif
+       };
+
+       static const size_t xml_memory_page_size =
+       #ifdef PUGIXML_MEMORY_PAGE_SIZE
+               (PUGIXML_MEMORY_PAGE_SIZE)
+       #else
+               32768
+       #endif
+               - sizeof(xml_memory_page);
+
+       struct xml_memory_string_header
+       {
+               uint16_t page_offset; // offset from page->data
+               uint16_t full_size; // 0 if string occupies whole page
+       };
+
+       struct xml_allocator
+       {
+               xml_allocator(xml_memory_page* root): _root(root), _busy_size(root->busy_size)
+               {
+               #ifdef PUGIXML_COMPACT
+                       _hash = 0;
+               #endif
+               }
+
+               xml_memory_page* allocate_page(size_t data_size)
+               {
+                       size_t size = sizeof(xml_memory_page) + data_size;
+
+                       // allocate block with some alignment, leaving memory for worst-case padding
+                       void* memory = xml_memory::allocate(size);
+                       if (!memory) return 0;
+
+                       // prepare page structure
+                       xml_memory_page* page = xml_memory_page::construct(memory);
+                       assert(page);
+
+                       page->allocator = _root->allocator;
+
+                       return page;
+               }
+
+               static void deallocate_page(xml_memory_page* page)
+               {
+                       xml_memory::deallocate(page);
+               }
+
+               void* allocate_memory_oob(size_t size, xml_memory_page*& out_page);
+
+               void* allocate_memory(size_t size, xml_memory_page*& out_page)
+               {
+                       if (PUGI__UNLIKELY(_busy_size + size > xml_memory_page_size))
+                               return allocate_memory_oob(size, out_page);
+
+                       void* buf = reinterpret_cast<char*>(_root) + sizeof(xml_memory_page) + _busy_size;
+
+                       _busy_size += size;
+
+                       out_page = _root;
+
+                       return buf;
+               }
+
+       #ifdef PUGIXML_COMPACT
+               void* allocate_object(size_t size, xml_memory_page*& out_page)
+               {
+                       void* result = allocate_memory(size + sizeof(uint32_t), out_page);
+                       if (!result) return 0;
+
+                       // adjust for marker
+                       ptrdiff_t offset = static_cast<char*>(result) - reinterpret_cast<char*>(out_page->compact_page_marker);
+
+                       if (PUGI__UNLIKELY(static_cast<uintptr_t>(offset) >= 256 * xml_memory_block_alignment))
+                       {
+                               // insert new marker
+                               uint32_t* marker = static_cast<uint32_t*>(result);
+
+                               *marker = static_cast<uint32_t>(reinterpret_cast<char*>(marker) - reinterpret_cast<char*>(out_page));
+                               out_page->compact_page_marker = marker;
+
+                               // since we don't reuse the page space until we reallocate it, we can just pretend that we freed the marker block
+                               // this will make sure deallocate_memory correctly tracks the size
+                               out_page->freed_size += sizeof(uint32_t);
+
+                               return marker + 1;
+                       }
+                       else
+                       {
+                               // roll back uint32_t part
+                               _busy_size -= sizeof(uint32_t);
+
+                               return result;
+                       }
+               }
+       #else
+               void* allocate_object(size_t size, xml_memory_page*& out_page)
+               {
+                       return allocate_memory(size, out_page);
+               }
+       #endif
+
+               void deallocate_memory(void* ptr, size_t size, xml_memory_page* page)
+               {
+                       if (page == _root) page->busy_size = _busy_size;
+
+                       assert(ptr >= reinterpret_cast<char*>(page) + sizeof(xml_memory_page) && ptr < reinterpret_cast<char*>(page) + sizeof(xml_memory_page) + page->busy_size);
+                       (void)!ptr;
+
+                       page->freed_size += size;
+                       assert(page->freed_size <= page->busy_size);
+
+                       if (page->freed_size == page->busy_size)
+                       {
+                               if (page->next == 0)
+                               {
+                                       assert(_root == page);
+
+                                       // top page freed, just reset sizes
+                                       page->busy_size = 0;
+                                       page->freed_size = 0;
+
+                               #ifdef PUGIXML_COMPACT
+                                       // reset compact state to maximize efficiency
+                                       page->compact_string_base = 0;
+                                       page->compact_shared_parent = 0;
+                                       page->compact_page_marker = 0;
+                               #endif
+
+                                       _busy_size = 0;
+                               }
+                               else
+                               {
+                                       assert(_root != page);
+                                       assert(page->prev);
+
+                                       // remove from the list
+                                       page->prev->next = page->next;
+                                       page->next->prev = page->prev;
+
+                                       // deallocate
+                                       deallocate_page(page);
+                               }
+                       }
+               }
+
+               char_t* allocate_string(size_t length)
+               {
+                       static const size_t max_encoded_offset = (1 << 16) * xml_memory_block_alignment;
+
+                       PUGI__STATIC_ASSERT(xml_memory_page_size <= max_encoded_offset);
+
+                       // allocate memory for string and header block
+                       size_t size = sizeof(xml_memory_string_header) + length * sizeof(char_t);
+
+                       // round size up to block alignment boundary
+                       size_t full_size = (size + (xml_memory_block_alignment - 1)) & ~(xml_memory_block_alignment - 1);
+
+                       xml_memory_page* page;
+                       xml_memory_string_header* header = static_cast<xml_memory_string_header*>(allocate_memory(full_size, page));
+
+                       if (!header) return 0;
+
+                       // setup header
+                       ptrdiff_t page_offset = reinterpret_cast<char*>(header) - reinterpret_cast<char*>(page) - sizeof(xml_memory_page);
+
+                       assert(page_offset % xml_memory_block_alignment == 0);
+                       assert(page_offset >= 0 && static_cast<size_t>(page_offset) < max_encoded_offset);
+                       header->page_offset = static_cast<uint16_t>(static_cast<size_t>(page_offset) / xml_memory_block_alignment);
+
+                       // full_size == 0 for large strings that occupy the whole page
+                       assert(full_size % xml_memory_block_alignment == 0);
+                       assert(full_size < max_encoded_offset || (page->busy_size == full_size && page_offset == 0));
+                       header->full_size = static_cast<uint16_t>(full_size < max_encoded_offset ? full_size / xml_memory_block_alignment : 0);
+
+                       // round-trip through void* to avoid 'cast increases required alignment of target type' warning
+                       // header is guaranteed a pointer-sized alignment, which should be enough for char_t
+                       return static_cast<char_t*>(static_cast<void*>(header + 1));
+               }
+
+               void deallocate_string(char_t* string)
+               {
+                       // this function casts pointers through void* to avoid 'cast increases required alignment of target type' warnings
+                       // we're guaranteed the proper (pointer-sized) alignment on the input string if it was allocated via allocate_string
+
+                       // get header
+                       xml_memory_string_header* header = static_cast<xml_memory_string_header*>(static_cast<void*>(string)) - 1;
+                       assert(header);
+
+                       // deallocate
+                       size_t page_offset = sizeof(xml_memory_page) + header->page_offset * xml_memory_block_alignment;
+                       xml_memory_page* page = reinterpret_cast<xml_memory_page*>(static_cast<void*>(reinterpret_cast<char*>(header) - page_offset));
+
+                       // if full_size == 0 then this string occupies the whole page
+                       size_t full_size = header->full_size == 0 ? page->busy_size : header->full_size * xml_memory_block_alignment;
+
+                       deallocate_memory(header, full_size, page);
+               }
+
+               bool reserve()
+               {
+               #ifdef PUGIXML_COMPACT
+                       return _hash->reserve();
+               #else
+                       return true;
+               #endif
+               }
+
+               xml_memory_page* _root;
+               size_t _busy_size;
+
+       #ifdef PUGIXML_COMPACT
+               compact_hash_table* _hash;
+       #endif
+       };
+
+       PUGI__FN_NO_INLINE void* xml_allocator::allocate_memory_oob(size_t size, xml_memory_page*& out_page)
+       {
+               const size_t large_allocation_threshold = xml_memory_page_size / 4;
+
+               xml_memory_page* page = allocate_page(size <= large_allocation_threshold ? xml_memory_page_size : size);
+               out_page = page;
+
+               if (!page) return 0;
+
+               if (size <= large_allocation_threshold)
+               {
+                       _root->busy_size = _busy_size;
+
+                       // insert page at the end of linked list
+                       page->prev = _root;
+                       _root->next = page;
+                       _root = page;
+
+                       _busy_size = size;
+               }
+               else
+               {
+                       // insert page before the end of linked list, so that it is deleted as soon as possible
+                       // the last page is not deleted even if it's empty (see deallocate_memory)
+                       assert(_root->prev);
+
+                       page->prev = _root->prev;
+                       page->next = _root;
+
+                       _root->prev->next = page;
+                       _root->prev = page;
+
+                       page->busy_size = size;
+               }
+
+               return reinterpret_cast<char*>(page) + sizeof(xml_memory_page);
+       }
+PUGI__NS_END
+
+#ifdef PUGIXML_COMPACT
+PUGI__NS_BEGIN
+       static const uintptr_t compact_alignment_log2 = 2;
+       static const uintptr_t compact_alignment = 1 << compact_alignment_log2;
+
+       class compact_header
+       {
+       public:
+               compact_header(xml_memory_page* page, unsigned int flags)
+               {
+                       PUGI__STATIC_ASSERT(xml_memory_block_alignment == compact_alignment);
+
+                       ptrdiff_t offset = (reinterpret_cast<char*>(this) - reinterpret_cast<char*>(page->compact_page_marker));
+                       assert(offset % compact_alignment == 0 && static_cast<uintptr_t>(offset) < 256 * compact_alignment);
+
+                       _page = static_cast<unsigned char>(offset >> compact_alignment_log2);
+                       _flags = static_cast<unsigned char>(flags);
+               }
+
+               void operator&=(uintptr_t mod)
+               {
+                       _flags &= static_cast<unsigned char>(mod);
+               }
+
+               void operator|=(uintptr_t mod)
+               {
+                       _flags |= static_cast<unsigned char>(mod);
+               }
+
+               uintptr_t operator&(uintptr_t mod) const
+               {
+                       return _flags & mod;
+               }
+
+               xml_memory_page* get_page() const
+               {
+                       // round-trip through void* to silence 'cast increases required alignment of target type' warnings
+                       const char* page_marker = reinterpret_cast<const char*>(this) - (_page << compact_alignment_log2);
+                       const char* page = page_marker - *reinterpret_cast<const uint32_t*>(static_cast<const void*>(page_marker));
+
+                       return const_cast<xml_memory_page*>(reinterpret_cast<const xml_memory_page*>(static_cast<const void*>(page)));
+               }
+
+       private:
+               unsigned char _page;
+               unsigned char _flags;
+       };
+
+       PUGI__FN xml_memory_page* compact_get_page(const void* object, int header_offset)
+       {
+               const compact_header* header = reinterpret_cast<const compact_header*>(static_cast<const char*>(object) - header_offset);
+
+               return header->get_page();
+       }
+
+       template <int header_offset, typename T> PUGI__FN_NO_INLINE T* compact_get_value(const void* object)
+       {
+               return static_cast<T*>(compact_get_page(object, header_offset)->allocator->_hash->find(object));
+       }
+
+       template <int header_offset, typename T> PUGI__FN_NO_INLINE void compact_set_value(const void* object, T* value)
+       {
+               compact_get_page(object, header_offset)->allocator->_hash->insert(object, value);
+       }
+
+       template <typename T, int header_offset, int start = -126> class compact_pointer
+       {
+       public:
+               compact_pointer(): _data(0)
+               {
+               }
+
+               void operator=(const compact_pointer& rhs)
+               {
+                       *this = rhs + 0;
+               }
+
+               void operator=(T* value)
+               {
+                       if (value)
+                       {
+                               // value is guaranteed to be compact-aligned; 'this' is not
+                               // our decoding is based on 'this' aligned to compact alignment downwards (see operator T*)
+                               // so for negative offsets (e.g. -3) we need to adjust the diff by compact_alignment - 1 to
+                               // compensate for arithmetic shift rounding for negative values
+                               ptrdiff_t diff = reinterpret_cast<char*>(value) - reinterpret_cast<char*>(this);
+                               ptrdiff_t offset = ((diff + int(compact_alignment - 1)) >> compact_alignment_log2) - start;
+
+                               if (static_cast<uintptr_t>(offset) <= 253)
+                                       _data = static_cast<unsigned char>(offset + 1);
+                               else
+                               {
+                                       compact_set_value<header_offset>(this, value);
+
+                                       _data = 255;
+                               }
+                       }
+                       else
+                               _data = 0;
+               }
+
+               operator T*() const
+               {
+                       if (_data)
+                       {
+                               if (_data < 255)
+                               {
+                                       uintptr_t base = reinterpret_cast<uintptr_t>(this) & ~(compact_alignment - 1);
+
+                                       return reinterpret_cast<T*>(base + (_data - 1 + start) * compact_alignment);
+                               }
+                               else
+                                       return compact_get_value<header_offset, T>(this);
+                       }
+                       else
+                               return 0;
+               }
+
+               T* operator->() const
+               {
+                       return *this;
+               }
+
+       private:
+               unsigned char _data;
+       };
+
+       template <typename T, int header_offset> class compact_pointer_parent
+       {
+       public:
+               compact_pointer_parent(): _data(0)
+               {
+               }
+
+               void operator=(const compact_pointer_parent& rhs)
+               {
+                       *this = rhs + 0;
+               }
+
+               void operator=(T* value)
+               {
+                       if (value)
+                       {
+                               // value is guaranteed to be compact-aligned; 'this' is not
+                               // our decoding is based on 'this' aligned to compact alignment downwards (see operator T*)
+                               // so for negative offsets (e.g. -3) we need to adjust the diff by compact_alignment - 1 to
+                               // compensate for arithmetic shift behavior for negative values
+                               ptrdiff_t diff = reinterpret_cast<char*>(value) - reinterpret_cast<char*>(this);
+                               ptrdiff_t offset = ((diff + int(compact_alignment - 1)) >> compact_alignment_log2) + 65533;
+
+                               if (static_cast<uintptr_t>(offset) <= 65533)
+                               {
+                                       _data = static_cast<unsigned short>(offset + 1);
+                               }
+                               else
+                               {
+                                       xml_memory_page* page = compact_get_page(this, header_offset);
+
+                                       if (PUGI__UNLIKELY(page->compact_shared_parent == 0))
+                                               page->compact_shared_parent = value;
+
+                                       if (page->compact_shared_parent == value)
+                                       {
+                                               _data = 65534;
+                                       }
+                                       else
+                                       {
+                                               compact_set_value<header_offset>(this, value);
+
+                                               _data = 65535;
+                                       }
+                               }
+                       }
+                       else
+                       {
+                               _data = 0;
+                       }
+               }
+
+               operator T*() const
+               {
+                       if (_data)
+                       {
+                               if (_data < 65534)
+                               {
+                                       uintptr_t base = reinterpret_cast<uintptr_t>(this) & ~(compact_alignment - 1);
+
+                                       return reinterpret_cast<T*>(base + (_data - 1 - 65533) * compact_alignment);
+                               }
+                               else if (_data == 65534)
+                                       return static_cast<T*>(compact_get_page(this, header_offset)->compact_shared_parent);
+                               else
+                                       return compact_get_value<header_offset, T>(this);
+                       }
+                       else
+                               return 0;
+               }
+
+               T* operator->() const
+               {
+                       return *this;
+               }
+
+       private:
+               uint16_t _data;
+       };
+
+       template <int header_offset, int base_offset> class compact_string
+       {
+       public:
+               compact_string(): _data(0)
+               {
+               }
+
+               void operator=(const compact_string& rhs)
+               {
+                       *this = rhs + 0;
+               }
+
+               void operator=(char_t* value)
+               {
+                       if (value)
+                       {
+                               xml_memory_page* page = compact_get_page(this, header_offset);
+
+                               if (PUGI__UNLIKELY(page->compact_string_base == 0))
+                                       page->compact_string_base = value;
+
+                               ptrdiff_t offset = value - page->compact_string_base;
+
+                               if (static_cast<uintptr_t>(offset) < (65535 << 7))
+                               {
+                                       // round-trip through void* to silence 'cast increases required alignment of target type' warnings
+                                       uint16_t* base = reinterpret_cast<uint16_t*>(static_cast<void*>(reinterpret_cast<char*>(this) - base_offset));
+
+                                       if (*base == 0)
+                                       {
+                                               *base = static_cast<uint16_t>((offset >> 7) + 1);
+                                               _data = static_cast<unsigned char>((offset & 127) + 1);
+                                       }
+                                       else
+                                       {
+                                               ptrdiff_t remainder = offset - ((*base - 1) << 7);
+
+                                               if (static_cast<uintptr_t>(remainder) <= 253)
+                                               {
+                                                       _data = static_cast<unsigned char>(remainder + 1);
+                                               }
+                                               else
+                                               {
+                                                       compact_set_value<header_offset>(this, value);
+
+                                                       _data = 255;
+                                               }
+                                       }
+                               }
+                               else
+                               {
+                                       compact_set_value<header_offset>(this, value);
+
+                                       _data = 255;
+                               }
+                       }
+                       else
+                       {
+                               _data = 0;
+                       }
+               }
+
+               operator char_t*() const
+               {
+                       if (_data)
+                       {
+                               if (_data < 255)
+                               {
+                                       xml_memory_page* page = compact_get_page(this, header_offset);
+
+                                       // round-trip through void* to silence 'cast increases required alignment of target type' warnings
+                                       const uint16_t* base = reinterpret_cast<const uint16_t*>(static_cast<const void*>(reinterpret_cast<const char*>(this) - base_offset));
+                                       assert(*base);
+
+                                       ptrdiff_t offset = ((*base - 1) << 7) + (_data - 1);
+
+                                       return page->compact_string_base + offset;
+                               }
+                               else
+                               {
+                                       return compact_get_value<header_offset, char_t>(this);
+                               }
+                       }
+                       else
+                               return 0;
+               }
+
+       private:
+               unsigned char _data;
+       };
+PUGI__NS_END
+#endif
+
+#ifdef PUGIXML_COMPACT
+namespace pugi
+{
+       struct xml_attribute_struct
+       {
+               xml_attribute_struct(impl::xml_memory_page* page): header(page, 0), namevalue_base(0)
+               {
+                       PUGI__STATIC_ASSERT(sizeof(xml_attribute_struct) == 8);
+               }
+
+               impl::compact_header header;
+
+               uint16_t namevalue_base;
+
+               impl::compact_string<4, 2> name;
+               impl::compact_string<5, 3> value;
+
+               impl::compact_pointer<xml_attribute_struct, 6> prev_attribute_c;
+               impl::compact_pointer<xml_attribute_struct, 7, 0> next_attribute;
+       };
+
+       struct xml_node_struct
+       {
+               xml_node_struct(impl::xml_memory_page* page, xml_node_type type): header(page, type), namevalue_base(0)
+               {
+                       PUGI__STATIC_ASSERT(sizeof(xml_node_struct) == 12);
+               }
+
+               impl::compact_header header;
+
+               uint16_t namevalue_base;
+
+               impl::compact_string<4, 2> name;
+               impl::compact_string<5, 3> value;
+
+               impl::compact_pointer_parent<xml_node_struct, 6> parent;
+
+               impl::compact_pointer<xml_node_struct, 8, 0> first_child;
+
+               impl::compact_pointer<xml_node_struct,  9>    prev_sibling_c;
+               impl::compact_pointer<xml_node_struct, 10, 0> next_sibling;
+
+               impl::compact_pointer<xml_attribute_struct, 11, 0> first_attribute;
+       };
+}
+#else
+namespace pugi
+{
+       struct xml_attribute_struct
+       {
+               xml_attribute_struct(impl::xml_memory_page* page): name(0), value(0), prev_attribute_c(0), next_attribute(0)
+               {
+                       header = PUGI__GETHEADER_IMPL(this, page, 0);
+               }
+
+               uintptr_t header;
+
+               char_t* name;
+               char_t* value;
+
+               xml_attribute_struct* prev_attribute_c;
+               xml_attribute_struct* next_attribute;
+       };
+
+       struct xml_node_struct
+       {
+               xml_node_struct(impl::xml_memory_page* page, xml_node_type type): name(0), value(0), parent(0), first_child(0), prev_sibling_c(0), next_sibling(0), first_attribute(0)
+               {
+                       header = PUGI__GETHEADER_IMPL(this, page, type);
+               }
+
+               uintptr_t header;
+
+               char_t* name;
+               char_t* value;
+
+               xml_node_struct* parent;
+
+               xml_node_struct* first_child;
+
+               xml_node_struct* prev_sibling_c;
+               xml_node_struct* next_sibling;
+
+               xml_attribute_struct* first_attribute;
+       };
+}
+#endif
+
+PUGI__NS_BEGIN
+       struct xml_extra_buffer
+       {
+               char_t* buffer;
+               xml_extra_buffer* next;
+       };
+
+       struct xml_document_struct: public xml_node_struct, public xml_allocator
+       {
+               xml_document_struct(xml_memory_page* page): xml_node_struct(page, node_document), xml_allocator(page), buffer(0), extra_buffers(0)
+               {
+               }
+
+               const char_t* buffer;
+
+               xml_extra_buffer* extra_buffers;
+
+       #ifdef PUGIXML_COMPACT
+               compact_hash_table hash;
+       #endif
+       };
+
+       template <typename Object> inline xml_allocator& get_allocator(const Object* object)
+       {
+               assert(object);
+
+               return *PUGI__GETPAGE(object)->allocator;
+       }
+
+       template <typename Object> inline xml_document_struct& get_document(const Object* object)
+       {
+               assert(object);
+
+               return *static_cast<xml_document_struct*>(PUGI__GETPAGE(object)->allocator);
+       }
+PUGI__NS_END
+
+// Low-level DOM operations
+PUGI__NS_BEGIN
+       inline xml_attribute_struct* allocate_attribute(xml_allocator& alloc)
+       {
+               xml_memory_page* page;
+               void* memory = alloc.allocate_object(sizeof(xml_attribute_struct), page);
+               if (!memory) return 0;
+
+               return new (memory) xml_attribute_struct(page);
+       }
+
+       inline xml_node_struct* allocate_node(xml_allocator& alloc, xml_node_type type)
+       {
+               xml_memory_page* page;
+               void* memory = alloc.allocate_object(sizeof(xml_node_struct), page);
+               if (!memory) return 0;
+
+               return new (memory) xml_node_struct(page, type);
+       }
+
+       inline void destroy_attribute(xml_attribute_struct* a, xml_allocator& alloc)
+       {
+               if (a->header & impl::xml_memory_page_name_allocated_mask)
+                       alloc.deallocate_string(a->name);
+
+               if (a->header & impl::xml_memory_page_value_allocated_mask)
+                       alloc.deallocate_string(a->value);
+
+               alloc.deallocate_memory(a, sizeof(xml_attribute_struct), PUGI__GETPAGE(a));
+       }
+
+       inline void destroy_node(xml_node_struct* n, xml_allocator& alloc)
+       {
+               if (n->header & impl::xml_memory_page_name_allocated_mask)
+                       alloc.deallocate_string(n->name);
+
+               if (n->header & impl::xml_memory_page_value_allocated_mask)
+                       alloc.deallocate_string(n->value);
+
+               for (xml_attribute_struct* attr = n->first_attribute; attr; )
+               {
+                       xml_attribute_struct* next = attr->next_attribute;
+
+                       destroy_attribute(attr, alloc);
+
+                       attr = next;
+               }
+
+               for (xml_node_struct* child = n->first_child; child; )
+               {
+                       xml_node_struct* next = child->next_sibling;
+
+                       destroy_node(child, alloc);
+
+                       child = next;
+               }
+
+               alloc.deallocate_memory(n, sizeof(xml_node_struct), PUGI__GETPAGE(n));
+       }
+
+       inline void append_node(xml_node_struct* child, xml_node_struct* node)
+       {
+               child->parent = node;
+
+               xml_node_struct* head = node->first_child;
+
+               if (head)
+               {
+                       xml_node_struct* tail = head->prev_sibling_c;
+
+                       tail->next_sibling = child;
+                       child->prev_sibling_c = tail;
+                       head->prev_sibling_c = child;
+               }
+               else
+               {
+                       node->first_child = child;
+                       child->prev_sibling_c = child;
+               }
+       }
+
+       inline void prepend_node(xml_node_struct* child, xml_node_struct* node)
+       {
+               child->parent = node;
+
+               xml_node_struct* head = node->first_child;
+
+               if (head)
+               {
+                       child->prev_sibling_c = head->prev_sibling_c;
+                       head->prev_sibling_c = child;
+               }
+               else
+                       child->prev_sibling_c = child;
+
+               child->next_sibling = head;
+               node->first_child = child;
+       }
+
+       inline void insert_node_after(xml_node_struct* child, xml_node_struct* node)
+       {
+               xml_node_struct* parent = node->parent;
+
+               child->parent = parent;
+
+               if (node->next_sibling)
+                       node->next_sibling->prev_sibling_c = child;
+               else
+                       parent->first_child->prev_sibling_c = child;
+
+               child->next_sibling = node->next_sibling;
+               child->prev_sibling_c = node;
+
+               node->next_sibling = child;
+       }
+
+       inline void insert_node_before(xml_node_struct* child, xml_node_struct* node)
+       {
+               xml_node_struct* parent = node->parent;
+
+               child->parent = parent;
+
+               if (node->prev_sibling_c->next_sibling)
+                       node->prev_sibling_c->next_sibling = child;
+               else
+                       parent->first_child = child;
+
+               child->prev_sibling_c = node->prev_sibling_c;
+               child->next_sibling = node;
+
+               node->prev_sibling_c = child;
+       }
+
+       inline void remove_node(xml_node_struct* node)
+       {
+               xml_node_struct* parent = node->parent;
+
+               if (node->next_sibling)
+                       node->next_sibling->prev_sibling_c = node->prev_sibling_c;
+               else
+                       parent->first_child->prev_sibling_c = node->prev_sibling_c;
+
+               if (node->prev_sibling_c->next_sibling)
+                       node->prev_sibling_c->next_sibling = node->next_sibling;
+               else
+                       parent->first_child = node->next_sibling;
+
+               node->parent = 0;
+               node->prev_sibling_c = 0;
+               node->next_sibling = 0;
+       }
+
+       inline void append_attribute(xml_attribute_struct* attr, xml_node_struct* node)
+       {
+               xml_attribute_struct* head = node->first_attribute;
+
+               if (head)
+               {
+                       xml_attribute_struct* tail = head->prev_attribute_c;
+
+                       tail->next_attribute = attr;
+                       attr->prev_attribute_c = tail;
+                       head->prev_attribute_c = attr;
+               }
+               else
+               {
+                       node->first_attribute = attr;
+                       attr->prev_attribute_c = attr;
+               }
+       }
+
+       inline void prepend_attribute(xml_attribute_struct* attr, xml_node_struct* node)
+       {
+               xml_attribute_struct* head = node->first_attribute;
+
+               if (head)
+               {
+                       attr->prev_attribute_c = head->prev_attribute_c;
+                       head->prev_attribute_c = attr;
+               }
+               else
+                       attr->prev_attribute_c = attr;
+
+               attr->next_attribute = head;
+               node->first_attribute = attr;
+       }
+
+       inline void insert_attribute_after(xml_attribute_struct* attr, xml_attribute_struct* place, xml_node_struct* node)
+       {
+               if (place->next_attribute)
+                       place->next_attribute->prev_attribute_c = attr;
+               else
+                       node->first_attribute->prev_attribute_c = attr;
+
+               attr->next_attribute = place->next_attribute;
+               attr->prev_attribute_c = place;
+               place->next_attribute = attr;
+       }
+
+       inline void insert_attribute_before(xml_attribute_struct* attr, xml_attribute_struct* place, xml_node_struct* node)
+       {
+               if (place->prev_attribute_c->next_attribute)
+                       place->prev_attribute_c->next_attribute = attr;
+               else
+                       node->first_attribute = attr;
+
+               attr->prev_attribute_c = place->prev_attribute_c;
+               attr->next_attribute = place;
+               place->prev_attribute_c = attr;
+       }
+
+       inline void remove_attribute(xml_attribute_struct* attr, xml_node_struct* node)
+       {
+               if (attr->next_attribute)
+                       attr->next_attribute->prev_attribute_c = attr->prev_attribute_c;
+               else
+                       node->first_attribute->prev_attribute_c = attr->prev_attribute_c;
+
+               if (attr->prev_attribute_c->next_attribute)
+                       attr->prev_attribute_c->next_attribute = attr->next_attribute;
+               else
+                       node->first_attribute = attr->next_attribute;
+
+               attr->prev_attribute_c = 0;
+               attr->next_attribute = 0;
+       }
+
+       PUGI__FN_NO_INLINE xml_node_struct* append_new_node(xml_node_struct* node, xml_allocator& alloc, xml_node_type type = node_element)
+       {
+               if (!alloc.reserve()) return 0;
+
+               xml_node_struct* child = allocate_node(alloc, type);
+               if (!child) return 0;
+
+               append_node(child, node);
+
+               return child;
+       }
+
+       PUGI__FN_NO_INLINE xml_attribute_struct* append_new_attribute(xml_node_struct* node, xml_allocator& alloc)
+       {
+               if (!alloc.reserve()) return 0;
+
+               xml_attribute_struct* attr = allocate_attribute(alloc);
+               if (!attr) return 0;
+
+               append_attribute(attr, node);
+
+               return attr;
+       }
+PUGI__NS_END
+
+// Helper classes for code generation
+PUGI__NS_BEGIN
+       struct opt_false
+       {
+               enum { value = 0 };
+       };
+
+       struct opt_true
+       {
+               enum { value = 1 };
+       };
+PUGI__NS_END
+
+// Unicode utilities
+PUGI__NS_BEGIN
+       inline uint16_t endian_swap(uint16_t value)
+       {
+               return static_cast<uint16_t>(((value & 0xff) << 8) | (value >> 8));
+       }
+
+       inline uint32_t endian_swap(uint32_t value)
+       {
+               return ((value & 0xff) << 24) | ((value & 0xff00) << 8) | ((value & 0xff0000) >> 8) | (value >> 24);
+       }
+
+       struct utf8_counter
+       {
+               typedef size_t value_type;
+
+               static value_type low(value_type result, uint32_t ch)
+               {
+                       // U+0000..U+007F
+                       if (ch < 0x80) return result + 1;
+                       // U+0080..U+07FF
+                       else if (ch < 0x800) return result + 2;
+                       // U+0800..U+FFFF
+                       else return result + 3;
+               }
+
+               static value_type high(value_type result, uint32_t)
+               {
+                       // U+10000..U+10FFFF
+                       return result + 4;
+               }
+       };
+
+       struct utf8_writer
+       {
+               typedef uint8_t* value_type;
+
+               static value_type low(value_type result, uint32_t ch)
+               {
+                       // U+0000..U+007F
+                       if (ch < 0x80)
+                       {
+                               *result = static_cast<uint8_t>(ch);
+                               return result + 1;
+                       }
+                       // U+0080..U+07FF
+                       else if (ch < 0x800)
+                       {
+                               result[0] = static_cast<uint8_t>(0xC0 | (ch >> 6));
+                               result[1] = static_cast<uint8_t>(0x80 | (ch & 0x3F));
+                               return result + 2;
+                       }
+                       // U+0800..U+FFFF
+                       else
+                       {
+                               result[0] = static_cast<uint8_t>(0xE0 | (ch >> 12));
+                               result[1] = static_cast<uint8_t>(0x80 | ((ch >> 6) & 0x3F));
+                               result[2] = static_cast<uint8_t>(0x80 | (ch & 0x3F));
+                               return result + 3;
+                       }
+               }
+
+               static value_type high(value_type result, uint32_t ch)
+               {
+                       // U+10000..U+10FFFF
+                       result[0] = static_cast<uint8_t>(0xF0 | (ch >> 18));
+                       result[1] = static_cast<uint8_t>(0x80 | ((ch >> 12) & 0x3F));
+                       result[2] = static_cast<uint8_t>(0x80 | ((ch >> 6) & 0x3F));
+                       result[3] = static_cast<uint8_t>(0x80 | (ch & 0x3F));
+                       return result + 4;
+               }
+
+               static value_type any(value_type result, uint32_t ch)
+               {
+                       return (ch < 0x10000) ? low(result, ch) : high(result, ch);
+               }
+       };
+
+       struct utf16_counter
+       {
+               typedef size_t value_type;
+
+               static value_type low(value_type result, uint32_t)
+               {
+                       return result + 1;
+               }
+
+               static value_type high(value_type result, uint32_t)
+               {
+                       return result + 2;
+               }
+       };
+
+       struct utf16_writer
+       {
+               typedef uint16_t* value_type;
+
+               static value_type low(value_type result, uint32_t ch)
+               {
+                       *result = static_cast<uint16_t>(ch);
+
+                       return result + 1;
+               }
+
+               static value_type high(value_type result, uint32_t ch)
+               {
+                       uint32_t msh = static_cast<uint32_t>(ch - 0x10000) >> 10;
+                       uint32_t lsh = static_cast<uint32_t>(ch - 0x10000) & 0x3ff;
+
+                       result[0] = static_cast<uint16_t>(0xD800 + msh);
+                       result[1] = static_cast<uint16_t>(0xDC00 + lsh);
+
+                       return result + 2;
+               }
+
+               static value_type any(value_type result, uint32_t ch)
+               {
+                       return (ch < 0x10000) ? low(result, ch) : high(result, ch);
+               }
+       };
+
+       struct utf32_counter
+       {
+               typedef size_t value_type;
+
+               static value_type low(value_type result, uint32_t)
+               {
+                       return result + 1;
+               }
+
+               static value_type high(value_type result, uint32_t)
+               {
+                       return result + 1;
+               }
+       };
+
+       struct utf32_writer
+       {
+               typedef uint32_t* value_type;
+
+               static value_type low(value_type result, uint32_t ch)
+               {
+                       *result = ch;
+
+                       return result + 1;
+               }
+
+               static value_type high(value_type result, uint32_t ch)
+               {
+                       *result = ch;
+
+                       return result + 1;
+               }
+
+               static value_type any(value_type result, uint32_t ch)
+               {
+                       *result = ch;
+
+                       return result + 1;
+               }
+       };
+
+       struct latin1_writer
+       {
+               typedef uint8_t* value_type;
+
+               static value_type low(value_type result, uint32_t ch)
+               {
+                       *result = static_cast<uint8_t>(ch > 255 ? '?' : ch);
+
+                       return result + 1;
+               }
+
+               static value_type high(value_type result, uint32_t ch)
+               {
+                       (void)ch;
+
+                       *result = '?';
+
+                       return result + 1;
+               }
+       };
+
+       struct utf8_decoder
+       {
+               typedef uint8_t type;
+
+               template <typename Traits> static inline typename Traits::value_type process(const uint8_t* data, size_t size, typename Traits::value_type result, Traits)
+               {
+                       const uint8_t utf8_byte_mask = 0x3f;
+
+                       while (size)
+                       {
+                               uint8_t lead = *data;
+
+                               // 0xxxxxxx -> U+0000..U+007F
+                               if (lead < 0x80)
+                               {
+                                       result = Traits::low(result, lead);
+                                       data += 1;
+                                       size -= 1;
+
+                                       // process aligned single-byte (ascii) blocks
+                                       if ((reinterpret_cast<uintptr_t>(data) & 3) == 0)
+                                       {
+                                               // round-trip through void* to silence 'cast increases required alignment of target type' warnings
+                                               while (size >= 4 && (*static_cast<const uint32_t*>(static_cast<const void*>(data)) & 0x80808080) == 0)
+                                               {
+                                                       result = Traits::low(result, data[0]);
+                                                       result = Traits::low(result, data[1]);
+                                                       result = Traits::low(result, data[2]);
+                                                       result = Traits::low(result, data[3]);
+                                                       data += 4;
+                                                       size -= 4;
+                                               }
+                                       }
+                               }
+                               // 110xxxxx -> U+0080..U+07FF
+                               else if (static_cast<unsigned int>(lead - 0xC0) < 0x20 && size >= 2 && (data[1] & 0xc0) == 0x80)
+                               {
+                                       result = Traits::low(result, ((lead & ~0xC0) << 6) | (data[1] & utf8_byte_mask));
+                                       data += 2;
+                                       size -= 2;
+                               }
+                               // 1110xxxx -> U+0800-U+FFFF
+                               else if (static_cast<unsigned int>(lead - 0xE0) < 0x10 && size >= 3 && (data[1] & 0xc0) == 0x80 && (data[2] & 0xc0) == 0x80)
+                               {
+                                       result = Traits::low(result, ((lead & ~0xE0) << 12) | ((data[1] & utf8_byte_mask) << 6) | (data[2] & utf8_byte_mask));
+                                       data += 3;
+                                       size -= 3;
+                               }
+                               // 11110xxx -> U+10000..U+10FFFF
+                               else if (static_cast<unsigned int>(lead - 0xF0) < 0x08 && size >= 4 && (data[1] & 0xc0) == 0x80 && (data[2] & 0xc0) == 0x80 && (data[3] & 0xc0) == 0x80)
+                               {
+                                       result = Traits::high(result, ((lead & ~0xF0) << 18) | ((data[1] & utf8_byte_mask) << 12) | ((data[2] & utf8_byte_mask) << 6) | (data[3] & utf8_byte_mask));
+                                       data += 4;
+                                       size -= 4;
+                               }
+                               // 10xxxxxx or 11111xxx -> invalid
+                               else
+                               {
+                                       data += 1;
+                                       size -= 1;
+                               }
+                       }
+
+                       return result;
+               }
+       };
+
+       template <typename opt_swap> struct utf16_decoder
+       {
+               typedef uint16_t type;
+
+               template <typename Traits> static inline typename Traits::value_type process(const uint16_t* data, size_t size, typename Traits::value_type result, Traits)
+               {
+                       while (size)
+                       {
+                               uint16_t lead = opt_swap::value ? endian_swap(*data) : *data;
+
+                               // U+0000..U+D7FF
+                               if (lead < 0xD800)
+                               {
+                                       result = Traits::low(result, lead);
+                                       data += 1;
+                                       size -= 1;
+                               }
+                               // U+E000..U+FFFF
+                               else if (static_cast<unsigned int>(lead - 0xE000) < 0x2000)
+                               {
+                                       result = Traits::low(result, lead);
+                                       data += 1;
+                                       size -= 1;
+                               }
+                               // surrogate pair lead
+                               else if (static_cast<unsigned int>(lead - 0xD800) < 0x400 && size >= 2)
+                               {
+                                       uint16_t next = opt_swap::value ? endian_swap(data[1]) : data[1];
+
+                                       if (static_cast<unsigned int>(next - 0xDC00) < 0x400)
+                                       {
+                                               result = Traits::high(result, 0x10000 + ((lead & 0x3ff) << 10) + (next & 0x3ff));
+                                               data += 2;
+                                               size -= 2;
+                                       }
+                                       else
+                                       {
+                                               data += 1;
+                                               size -= 1;
+                                       }
+                               }
+                               else
+                               {
+                                       data += 1;
+                                       size -= 1;
+                               }
+                       }
+
+                       return result;
+               }
+       };
+
+       template <typename opt_swap> struct utf32_decoder
+       {
+               typedef uint32_t type;
+
+               template <typename Traits> static inline typename Traits::value_type process(const uint32_t* data, size_t size, typename Traits::value_type result, Traits)
+               {
+                       while (size)
+                       {
+                               uint32_t lead = opt_swap::value ? endian_swap(*data) : *data;
+
+                               // U+0000..U+FFFF
+                               if (lead < 0x10000)
+                               {
+                                       result = Traits::low(result, lead);
+                                       data += 1;
+                                       size -= 1;
+                               }
+                               // U+10000..U+10FFFF
+                               else
+                               {
+                                       result = Traits::high(result, lead);
+                                       data += 1;
+                                       size -= 1;
+                               }
+                       }
+
+                       return result;
+               }
+       };
+
+       struct latin1_decoder
+       {
+               typedef uint8_t type;
+
+               template <typename Traits> static inline typename Traits::value_type process(const uint8_t* data, size_t size, typename Traits::value_type result, Traits)
+               {
+                       while (size)
+                       {
+                               result = Traits::low(result, *data);
+                               data += 1;
+                               size -= 1;
+                       }
+
+                       return result;
+               }
+       };
+
+       template <size_t size> struct wchar_selector;
+
+       template <> struct wchar_selector<2>
+       {
+               typedef uint16_t type;
+               typedef utf16_counter counter;
+               typedef utf16_writer writer;
+               typedef utf16_decoder<opt_false> decoder;
+       };
+
+       template <> struct wchar_selector<4>
+       {
+               typedef uint32_t type;
+               typedef utf32_counter counter;
+               typedef utf32_writer writer;
+               typedef utf32_decoder<opt_false> decoder;
+       };
+
+       typedef wchar_selector<sizeof(wchar_t)>::counter wchar_counter;
+       typedef wchar_selector<sizeof(wchar_t)>::writer wchar_writer;
+
+       struct wchar_decoder
+       {
+               typedef wchar_t type;
+
+               template <typename Traits> static inline typename Traits::value_type process(const wchar_t* data, size_t size, typename Traits::value_type result, Traits traits)
+               {
+                       typedef wchar_selector<sizeof(wchar_t)>::decoder decoder;
+
+                       return decoder::process(reinterpret_cast<const typename decoder::type*>(data), size, result, traits);
+               }
+       };
+
+#ifdef PUGIXML_WCHAR_MODE
+       PUGI__FN void convert_wchar_endian_swap(wchar_t* result, const wchar_t* data, size_t length)
+       {
+               for (size_t i = 0; i < length; ++i)
+                       result[i] = static_cast<wchar_t>(endian_swap(static_cast<wchar_selector<sizeof(wchar_t)>::type>(data[i])));
+       }
+#endif
+PUGI__NS_END
+
+PUGI__NS_BEGIN
+       enum chartype_t
+       {
+               ct_parse_pcdata = 1,    // \0, &, \r, <
+               ct_parse_attr = 2,              // \0, &, \r, ', "
+               ct_parse_attr_ws = 4,   // \0, &, \r, ', ", \n, tab
+               ct_space = 8,                   // \r, \n, space, tab
+               ct_parse_cdata = 16,    // \0, ], >, \r
+               ct_parse_comment = 32,  // \0, -, >, \r
+               ct_symbol = 64,                 // Any symbol > 127, a-z, A-Z, 0-9, _, :, -, .
+               ct_start_symbol = 128   // Any symbol > 127, a-z, A-Z, _, :
+       };
+
+       static const unsigned char chartype_table[256] =
+       {
+               55,  0,   0,   0,   0,   0,   0,   0,      0,   12,  12,  0,   0,   63,  0,   0,   // 0-15
+               0,   0,   0,   0,   0,   0,   0,   0,      0,   0,   0,   0,   0,   0,   0,   0,   // 16-31
+               8,   0,   6,   0,   0,   0,   7,   6,      0,   0,   0,   0,   0,   96,  64,  0,   // 32-47
+               64,  64,  64,  64,  64,  64,  64,  64,     64,  64,  192, 0,   1,   0,   48,  0,   // 48-63
+               0,   192, 192, 192, 192, 192, 192, 192,    192, 192, 192, 192, 192, 192, 192, 192, // 64-79
+               192, 192, 192, 192, 192, 192, 192, 192,    192, 192, 192, 0,   0,   16,  0,   192, // 80-95
+               0,   192, 192, 192, 192, 192, 192, 192,    192, 192, 192, 192, 192, 192, 192, 192, // 96-111
+               192, 192, 192, 192, 192, 192, 192, 192,    192, 192, 192, 0, 0, 0, 0, 0,           // 112-127
+
+               192, 192, 192, 192, 192, 192, 192, 192,    192, 192, 192, 192, 192, 192, 192, 192, // 128+
+               192, 192, 192, 192, 192, 192, 192, 192,    192, 192, 192, 192, 192, 192, 192, 192,
+               192, 192, 192, 192, 192, 192, 192, 192,    192, 192, 192, 192, 192, 192, 192, 192,
+               192, 192, 192, 192, 192, 192, 192, 192,    192, 192, 192, 192, 192, 192, 192, 192,
+               192, 192, 192, 192, 192, 192, 192, 192,    192, 192, 192, 192, 192, 192, 192, 192,
+               192, 192, 192, 192, 192, 192, 192, 192,    192, 192, 192, 192, 192, 192, 192, 192,
+               192, 192, 192, 192, 192, 192, 192, 192,    192, 192, 192, 192, 192, 192, 192, 192,
+               192, 192, 192, 192, 192, 192, 192, 192,    192, 192, 192, 192, 192, 192, 192, 192
+       };
+
+       enum chartypex_t
+       {
+               ctx_special_pcdata = 1,   // Any symbol >= 0 and < 32 (except \t, \r, \n), &, <, >
+               ctx_special_attr = 2,     // Any symbol >= 0 and < 32 (except \t), &, <, >, "
+               ctx_start_symbol = 4,     // Any symbol > 127, a-z, A-Z, _
+               ctx_digit = 8,                    // 0-9
+               ctx_symbol = 16                   // Any symbol > 127, a-z, A-Z, 0-9, _, -, .
+       };
+
+       static const unsigned char chartypex_table[256] =
+       {
+               3,  3,  3,  3,  3,  3,  3,  3,     3,  0,  2,  3,  3,  2,  3,  3,     // 0-15
+               3,  3,  3,  3,  3,  3,  3,  3,     3,  3,  3,  3,  3,  3,  3,  3,     // 16-31
+               0,  0,  2,  0,  0,  0,  3,  0,     0,  0,  0,  0,  0, 16, 16,  0,     // 32-47
+               24, 24, 24, 24, 24, 24, 24, 24,    24, 24, 0,  0,  3,  0,  3,  0,     // 48-63
+
+               0,  20, 20, 20, 20, 20, 20, 20,    20, 20, 20, 20, 20, 20, 20, 20,    // 64-79
+               20, 20, 20, 20, 20, 20, 20, 20,    20, 20, 20, 0,  0,  0,  0,  20,    // 80-95
+               0,  20, 20, 20, 20, 20, 20, 20,    20, 20, 20, 20, 20, 20, 20, 20,    // 96-111
+               20, 20, 20, 20, 20, 20, 20, 20,    20, 20, 20, 0,  0,  0,  0,  0,     // 112-127
+
+               20, 20, 20, 20, 20, 20, 20, 20,    20, 20, 20, 20, 20, 20, 20, 20,    // 128+
+               20, 20, 20, 20, 20, 20, 20, 20,    20, 20, 20, 20, 20, 20, 20, 20,
+               20, 20, 20, 20, 20, 20, 20, 20,    20, 20, 20, 20, 20, 20, 20, 20,
+               20, 20, 20, 20, 20, 20, 20, 20,    20, 20, 20, 20, 20, 20, 20, 20,
+               20, 20, 20, 20, 20, 20, 20, 20,    20, 20, 20, 20, 20, 20, 20, 20,
+               20, 20, 20, 20, 20, 20, 20, 20,    20, 20, 20, 20, 20, 20, 20, 20,
+               20, 20, 20, 20, 20, 20, 20, 20,    20, 20, 20, 20, 20, 20, 20, 20,
+               20, 20, 20, 20, 20, 20, 20, 20,    20, 20, 20, 20, 20, 20, 20, 20
+       };
+
+#ifdef PUGIXML_WCHAR_MODE
+       #define PUGI__IS_CHARTYPE_IMPL(c, ct, table) ((static_cast<unsigned int>(c) < 128 ? table[static_cast<unsigned int>(c)] : table[128]) & (ct))
+#else
+       #define PUGI__IS_CHARTYPE_IMPL(c, ct, table) (table[static_cast<unsigned char>(c)] & (ct))
+#endif
+
+       #define PUGI__IS_CHARTYPE(c, ct) PUGI__IS_CHARTYPE_IMPL(c, ct, chartype_table)
+       #define PUGI__IS_CHARTYPEX(c, ct) PUGI__IS_CHARTYPE_IMPL(c, ct, chartypex_table)
+
+       PUGI__FN bool is_little_endian()
+       {
+               unsigned int ui = 1;
+
+               return *reinterpret_cast<unsigned char*>(&ui) == 1;
+       }
+
+       PUGI__FN xml_encoding get_wchar_encoding()
+       {
+               PUGI__STATIC_ASSERT(sizeof(wchar_t) == 2 || sizeof(wchar_t) == 4);
+
+               if (sizeof(wchar_t) == 2)
+                       return is_little_endian() ? encoding_utf16_le : encoding_utf16_be;
+               else
+                       return is_little_endian() ? encoding_utf32_le : encoding_utf32_be;
+       }
+
+       PUGI__FN bool parse_declaration_encoding(const uint8_t* data, size_t size, const uint8_t*& out_encoding, size_t& out_length)
+       {
+       #define PUGI__SCANCHAR(ch) { if (offset >= size || data[offset] != ch) return false; offset++; }
+       #define PUGI__SCANCHARTYPE(ct) { while (offset < size && PUGI__IS_CHARTYPE(data[offset], ct)) offset++; }
+
+               // check if we have a non-empty XML declaration
+               if (size < 6 || !((data[0] == '<') & (data[1] == '?') & (data[2] == 'x') & (data[3] == 'm') & (data[4] == 'l') && PUGI__IS_CHARTYPE(data[5], ct_space)))
+                       return false;
+
+               // scan XML declaration until the encoding field
+               for (size_t i = 6; i + 1 < size; ++i)
+               {
+                       // declaration can not contain ? in quoted values
+                       if (data[i] == '?')
+                               return false;
+
+                       if (data[i] == 'e' && data[i + 1] == 'n')
+                       {
+                               size_t offset = i;
+
+                               // encoding follows the version field which can't contain 'en' so this has to be the encoding if XML is well formed
+                               PUGI__SCANCHAR('e'); PUGI__SCANCHAR('n'); PUGI__SCANCHAR('c'); PUGI__SCANCHAR('o');
+                               PUGI__SCANCHAR('d'); PUGI__SCANCHAR('i'); PUGI__SCANCHAR('n'); PUGI__SCANCHAR('g');
+
+                               // S? = S?
+                               PUGI__SCANCHARTYPE(ct_space);
+                               PUGI__SCANCHAR('=');
+                               PUGI__SCANCHARTYPE(ct_space);
+
+                               // the only two valid delimiters are ' and "
+                               uint8_t delimiter = (offset < size && data[offset] == '"') ? '"' : '\'';
+
+                               PUGI__SCANCHAR(delimiter);
+
+                               size_t start = offset;
+
+                               out_encoding = data + offset;
+
+                               PUGI__SCANCHARTYPE(ct_symbol);
+
+                               out_length = offset - start;
+
+                               PUGI__SCANCHAR(delimiter);
+
+                               return true;
+                       }
+               }
+
+               return false;
+
+       #undef PUGI__SCANCHAR
+       #undef PUGI__SCANCHARTYPE
+       }
+
+       PUGI__FN xml_encoding guess_buffer_encoding(const uint8_t* data, size_t size)
+       {
+               // skip encoding autodetection if input buffer is too small
+               if (size < 4) return encoding_utf8;
+
+               uint8_t d0 = data[0], d1 = data[1], d2 = data[2], d3 = data[3];
+
+               // look for BOM in first few bytes
+               if (d0 == 0 && d1 == 0 && d2 == 0xfe && d3 == 0xff) return encoding_utf32_be;
+               if (d0 == 0xff && d1 == 0xfe && d2 == 0 && d3 == 0) return encoding_utf32_le;
+               if (d0 == 0xfe && d1 == 0xff) return encoding_utf16_be;
+               if (d0 == 0xff && d1 == 0xfe) return encoding_utf16_le;
+               if (d0 == 0xef && d1 == 0xbb && d2 == 0xbf) return encoding_utf8;
+
+               // look for <, <? or <?xm in various encodings
+               if (d0 == 0 && d1 == 0 && d2 == 0 && d3 == 0x3c) return encoding_utf32_be;
+               if (d0 == 0x3c && d1 == 0 && d2 == 0 && d3 == 0) return encoding_utf32_le;
+               if (d0 == 0 && d1 == 0x3c && d2 == 0 && d3 == 0x3f) return encoding_utf16_be;
+               if (d0 == 0x3c && d1 == 0 && d2 == 0x3f && d3 == 0) return encoding_utf16_le;
+
+               // look for utf16 < followed by node name (this may fail, but is better than utf8 since it's zero terminated so early)
+               if (d0 == 0 && d1 == 0x3c) return encoding_utf16_be;
+               if (d0 == 0x3c && d1 == 0) return encoding_utf16_le;
+
+               // no known BOM detected; parse declaration
+               const uint8_t* enc = 0;
+               size_t enc_length = 0;
+
+               if (d0 == 0x3c && d1 == 0x3f && d2 == 0x78 && d3 == 0x6d && parse_declaration_encoding(data, size, enc, enc_length))
+               {
+                       // iso-8859-1 (case-insensitive)
+                       if (enc_length == 10
+                               && (enc[0] | ' ') == 'i' && (enc[1] | ' ') == 's' && (enc[2] | ' ') == 'o'
+                               && enc[3] == '-' && enc[4] == '8' && enc[5] == '8' && enc[6] == '5' && enc[7] == '9'
+                               && enc[8] == '-' && enc[9] == '1')
+                               return encoding_latin1;
+
+                       // latin1 (case-insensitive)
+                       if (enc_length == 6
+                               && (enc[0] | ' ') == 'l' && (enc[1] | ' ') == 'a' && (enc[2] | ' ') == 't'
+                               && (enc[3] | ' ') == 'i' && (enc[4] | ' ') == 'n'
+                               && enc[5] == '1')
+                               return encoding_latin1;
+               }
+
+               return encoding_utf8;
+       }
+
+       PUGI__FN xml_encoding get_buffer_encoding(xml_encoding encoding, const void* contents, size_t size)
+       {
+               // replace wchar encoding with utf implementation
+               if (encoding == encoding_wchar) return get_wchar_encoding();
+
+               // replace utf16 encoding with utf16 with specific endianness
+               if (encoding == encoding_utf16) return is_little_endian() ? encoding_utf16_le : encoding_utf16_be;
+
+               // replace utf32 encoding with utf32 with specific endianness
+               if (encoding == encoding_utf32) return is_little_endian() ? encoding_utf32_le : encoding_utf32_be;
+
+               // only do autodetection if no explicit encoding is requested
+               if (encoding != encoding_auto) return encoding;
+
+               // try to guess encoding (based on XML specification, Appendix F.1)
+               const uint8_t* data = static_cast<const uint8_t*>(contents);
+
+               return guess_buffer_encoding(data, size);
+       }
+
+       PUGI__FN bool get_mutable_buffer(char_t*& out_buffer, size_t& out_length, const void* contents, size_t size, bool is_mutable)
+       {
+               size_t length = size / sizeof(char_t);
+
+               if (is_mutable)
+               {
+                       out_buffer = static_cast<char_t*>(const_cast<void*>(contents));
+                       out_length = length;
+               }
+               else
+               {
+                       char_t* buffer = static_cast<char_t*>(xml_memory::allocate((length + 1) * sizeof(char_t)));
+                       if (!buffer) return false;
+
+                       if (contents)
+                               memcpy(buffer, contents, length * sizeof(char_t));
+                       else
+                               assert(length == 0);
+
+                       buffer[length] = 0;
+
+                       out_buffer = buffer;
+                       out_length = length + 1;
+               }
+
+               return true;
+       }
+
+#ifdef PUGIXML_WCHAR_MODE
+       PUGI__FN bool need_endian_swap_utf(xml_encoding le, xml_encoding re)
+       {
+               return (le == encoding_utf16_be && re == encoding_utf16_le) || (le == encoding_utf16_le && re == encoding_utf16_be) ||
+                          (le == encoding_utf32_be && re == encoding_utf32_le) || (le == encoding_utf32_le && re == encoding_utf32_be);
+       }
+
+       PUGI__FN bool convert_buffer_endian_swap(char_t*& out_buffer, size_t& out_length, const void* contents, size_t size, bool is_mutable)
+       {
+               const char_t* data = static_cast<const char_t*>(contents);
+               size_t length = size / sizeof(char_t);
+
+               if (is_mutable)
+               {
+                       char_t* buffer = const_cast<char_t*>(data);
+
+                       convert_wchar_endian_swap(buffer, data, length);
+
+                       out_buffer = buffer;
+                       out_length = length;
+               }
+               else
+               {
+                       char_t* buffer = static_cast<char_t*>(xml_memory::allocate((length + 1) * sizeof(char_t)));
+                       if (!buffer) return false;
+
+                       convert_wchar_endian_swap(buffer, data, length);
+                       buffer[length] = 0;
+
+                       out_buffer = buffer;
+                       out_length = length + 1;
+               }
+
+               return true;
+       }
+
+       template <typename D> PUGI__FN bool convert_buffer_generic(char_t*& out_buffer, size_t& out_length, const void* contents, size_t size, D)
+       {
+               const typename D::type* data = static_cast<const typename D::type*>(contents);
+               size_t data_length = size / sizeof(typename D::type);
+
+               // first pass: get length in wchar_t units
+               size_t length = D::process(data, data_length, 0, wchar_counter());
+
+               // allocate buffer of suitable length
+               char_t* buffer = static_cast<char_t*>(xml_memory::allocate((length + 1) * sizeof(char_t)));
+               if (!buffer) return false;
+
+               // second pass: convert utf16 input to wchar_t
+               wchar_writer::value_type obegin = reinterpret_cast<wchar_writer::value_type>(buffer);
+               wchar_writer::value_type oend = D::process(data, data_length, obegin, wchar_writer());
+
+               assert(oend == obegin + length);
+               *oend = 0;
+
+               out_buffer = buffer;
+               out_length = length + 1;
+
+               return true;
+       }
+
+       PUGI__FN bool convert_buffer(char_t*& out_buffer, size_t& out_length, xml_encoding encoding, const void* contents, size_t size, bool is_mutable)
+       {
+               // get native encoding
+               xml_encoding wchar_encoding = get_wchar_encoding();
+
+               // fast path: no conversion required
+               if (encoding == wchar_encoding)
+                       return get_mutable_buffer(out_buffer, out_length, contents, size, is_mutable);
+
+               // only endian-swapping is required
+               if (need_endian_swap_utf(encoding, wchar_encoding))
+                       return convert_buffer_endian_swap(out_buffer, out_length, contents, size, is_mutable);
+
+               // source encoding is utf8
+               if (encoding == encoding_utf8)
+                       return convert_buffer_generic(out_buffer, out_length, contents, size, utf8_decoder());
+
+               // source encoding is utf16
+               if (encoding == encoding_utf16_be || encoding == encoding_utf16_le)
+               {
+                       xml_encoding native_encoding = is_little_endian() ? encoding_utf16_le : encoding_utf16_be;
+
+                       return (native_encoding == encoding) ?
+                               convert_buffer_generic(out_buffer, out_length, contents, size, utf16_decoder<opt_false>()) :
+                               convert_buffer_generic(out_buffer, out_length, contents, size, utf16_decoder<opt_true>());
+               }
+
+               // source encoding is utf32
+               if (encoding == encoding_utf32_be || encoding == encoding_utf32_le)
+               {
+                       xml_encoding native_encoding = is_little_endian() ? encoding_utf32_le : encoding_utf32_be;
+
+                       return (native_encoding == encoding) ?
+                               convert_buffer_generic(out_buffer, out_length, contents, size, utf32_decoder<opt_false>()) :
+                               convert_buffer_generic(out_buffer, out_length, contents, size, utf32_decoder<opt_true>());
+               }
+
+               // source encoding is latin1
+               if (encoding == encoding_latin1)
+                       return convert_buffer_generic(out_buffer, out_length, contents, size, latin1_decoder());
+
+               assert(false && "Invalid encoding"); // unreachable
+               return false;
+       }
+#else
+       template <typename D> PUGI__FN bool convert_buffer_generic(char_t*& out_buffer, size_t& out_length, const void* contents, size_t size, D)
+       {
+               const typename D::type* data = static_cast<const typename D::type*>(contents);
+               size_t data_length = size / sizeof(typename D::type);
+
+               // first pass: get length in utf8 units
+               size_t length = D::process(data, data_length, 0, utf8_counter());
+
+               // allocate buffer of suitable length
+               char_t* buffer = static_cast<char_t*>(xml_memory::allocate((length + 1) * sizeof(char_t)));
+               if (!buffer) return false;
+
+               // second pass: convert utf16 input to utf8
+               uint8_t* obegin = reinterpret_cast<uint8_t*>(buffer);
+               uint8_t* oend = D::process(data, data_length, obegin, utf8_writer());
+
+               assert(oend == obegin + length);
+               *oend = 0;
+
+               out_buffer = buffer;
+               out_length = length + 1;
+
+               return true;
+       }
+
+       PUGI__FN size_t get_latin1_7bit_prefix_length(const uint8_t* data, size_t size)
+       {
+               for (size_t i = 0; i < size; ++i)
+                       if (data[i] > 127)
+                               return i;
+
+               return size;
+       }
+
+       PUGI__FN bool convert_buffer_latin1(char_t*& out_buffer, size_t& out_length, const void* contents, size_t size, bool is_mutable)
+       {
+               const uint8_t* data = static_cast<const uint8_t*>(contents);
+               size_t data_length = size;
+
+               // get size of prefix that does not need utf8 conversion
+               size_t prefix_length = get_latin1_7bit_prefix_length(data, data_length);
+               assert(prefix_length <= data_length);
+
+               const uint8_t* postfix = data + prefix_length;
+               size_t postfix_length = data_length - prefix_length;
+
+               // if no conversion is needed, just return the original buffer
+               if (postfix_length == 0) return get_mutable_buffer(out_buffer, out_length, contents, size, is_mutable);
+
+               // first pass: get length in utf8 units
+               size_t length = prefix_length + latin1_decoder::process(postfix, postfix_length, 0, utf8_counter());
+
+               // allocate buffer of suitable length
+               char_t* buffer = static_cast<char_t*>(xml_memory::allocate((length + 1) * sizeof(char_t)));
+               if (!buffer) return false;
+
+               // second pass: convert latin1 input to utf8
+               memcpy(buffer, data, prefix_length);
+
+               uint8_t* obegin = reinterpret_cast<uint8_t*>(buffer);
+               uint8_t* oend = latin1_decoder::process(postfix, postfix_length, obegin + prefix_length, utf8_writer());
+
+               assert(oend == obegin + length);
+               *oend = 0;
+
+               out_buffer = buffer;
+               out_length = length + 1;
+
+               return true;
+       }
+
+       PUGI__FN bool convert_buffer(char_t*& out_buffer, size_t& out_length, xml_encoding encoding, const void* contents, size_t size, bool is_mutable)
+       {
+               // fast path: no conversion required
+               if (encoding == encoding_utf8)
+                       return get_mutable_buffer(out_buffer, out_length, contents, size, is_mutable);
+
+               // source encoding is utf16
+               if (encoding == encoding_utf16_be || encoding == encoding_utf16_le)
+               {
+                       xml_encoding native_encoding = is_little_endian() ? encoding_utf16_le : encoding_utf16_be;
+
+                       return (native_encoding == encoding) ?
+                               convert_buffer_generic(out_buffer, out_length, contents, size, utf16_decoder<opt_false>()) :
+                               convert_buffer_generic(out_buffer, out_length, contents, size, utf16_decoder<opt_true>());
+               }
+
+               // source encoding is utf32
+               if (encoding == encoding_utf32_be || encoding == encoding_utf32_le)
+               {
+                       xml_encoding native_encoding = is_little_endian() ? encoding_utf32_le : encoding_utf32_be;
+
+                       return (native_encoding == encoding) ?
+                               convert_buffer_generic(out_buffer, out_length, contents, size, utf32_decoder<opt_false>()) :
+                               convert_buffer_generic(out_buffer, out_length, contents, size, utf32_decoder<opt_true>());
+               }
+
+               // source encoding is latin1
+               if (encoding == encoding_latin1)
+                       return convert_buffer_latin1(out_buffer, out_length, contents, size, is_mutable);
+
+               assert(false && "Invalid encoding"); // unreachable
+               return false;
+       }
+#endif
+
+       PUGI__FN size_t as_utf8_begin(const wchar_t* str, size_t length)
+       {
+               // get length in utf8 characters
+               return wchar_decoder::process(str, length, 0, utf8_counter());
+       }
+
+       PUGI__FN void as_utf8_end(char* buffer, size_t size, const wchar_t* str, size_t length)
+       {
+               // convert to utf8
+               uint8_t* begin = reinterpret_cast<uint8_t*>(buffer);
+               uint8_t* end = wchar_decoder::process(str, length, begin, utf8_writer());
+
+               assert(begin + size == end);
+               (void)!end;
+               (void)!size;
+       }
+
+#ifndef PUGIXML_NO_STL
+       PUGI__FN std::string as_utf8_impl(const wchar_t* str, size_t length)
+       {
+               // first pass: get length in utf8 characters
+               size_t size = as_utf8_begin(str, length);
+
+               // allocate resulting string
+               std::string result;
+               result.resize(size);
+
+               // second pass: convert to utf8
+               if (size > 0) as_utf8_end(&result[0], size, str, length);
+
+               return result;
+       }
+
+       PUGI__FN std::basic_string<wchar_t> as_wide_impl(const char* str, size_t size)
+       {
+               const uint8_t* data = reinterpret_cast<const uint8_t*>(str);
+
+               // first pass: get length in wchar_t units
+               size_t length = utf8_decoder::process(data, size, 0, wchar_counter());
+
+               // allocate resulting string
+               std::basic_string<wchar_t> result;
+               result.resize(length);
+
+               // second pass: convert to wchar_t
+               if (length > 0)
+               {
+                       wchar_writer::value_type begin = reinterpret_cast<wchar_writer::value_type>(&result[0]);
+                       wchar_writer::value_type end = utf8_decoder::process(data, size, begin, wchar_writer());
+
+                       assert(begin + length == end);
+                       (void)!end;
+               }
+
+               return result;
+       }
+#endif
+
+       template <typename Header>
+       inline bool strcpy_insitu_allow(size_t length, const Header& header, uintptr_t header_mask, char_t* target)
+       {
+               // never reuse shared memory
+               if (header & xml_memory_page_contents_shared_mask) return false;
+
+               size_t target_length = strlength(target);
+
+               // always reuse document buffer memory if possible
+               if ((header & header_mask) == 0) return target_length >= length;
+
+               // reuse heap memory if waste is not too great
+               const size_t reuse_threshold = 32;
+
+               return target_length >= length && (target_length < reuse_threshold || target_length - length < target_length / 2);
+       }
+
+       template <typename String, typename Header>
+       PUGI__FN bool strcpy_insitu(String& dest, Header& header, uintptr_t header_mask, const char_t* source, size_t source_length)
+       {
+               if (source_length == 0)
+               {
+                       // empty string and null pointer are equivalent, so just deallocate old memory
+                       xml_allocator* alloc = PUGI__GETPAGE_IMPL(header)->allocator;
+
+                       if (header & header_mask) alloc->deallocate_string(dest);
+
+                       // mark the string as not allocated
+                       dest = 0;
+                       header &= ~header_mask;
+
+                       return true;
+               }
+               else if (dest && strcpy_insitu_allow(source_length, header, header_mask, dest))
+               {
+                       // we can reuse old buffer, so just copy the new data (including zero terminator)
+                       memcpy(dest, source, source_length * sizeof(char_t));
+                       dest[source_length] = 0;
+
+                       return true;
+               }
+               else
+               {
+                       xml_allocator* alloc = PUGI__GETPAGE_IMPL(header)->allocator;
+
+                       if (!alloc->reserve()) return false;
+
+                       // allocate new buffer
+                       char_t* buf = alloc->allocate_string(source_length + 1);
+                       if (!buf) return false;
+
+                       // copy the string (including zero terminator)
+                       memcpy(buf, source, source_length * sizeof(char_t));
+                       buf[source_length] = 0;
+
+                       // deallocate old buffer (*after* the above to protect against overlapping memory and/or allocation failures)
+                       if (header & header_mask) alloc->deallocate_string(dest);
+
+                       // the string is now allocated, so set the flag
+                       dest = buf;
+                       header |= header_mask;
+
+                       return true;
+               }
+       }
+
+       struct gap
+       {
+               char_t* end;
+               size_t size;
+
+               gap(): end(0), size(0)
+               {
+               }
+
+               // Push new gap, move s count bytes further (skipping the gap).
+               // Collapse previous gap.
+               void push(char_t*& s, size_t count)
+               {
+                       if (end) // there was a gap already; collapse it
+                       {
+                               // Move [old_gap_end, new_gap_start) to [old_gap_start, ...)
+                               assert(s >= end);
+                               memmove(end - size, end, reinterpret_cast<char*>(s) - reinterpret_cast<char*>(end));
+                       }
+
+                       s += count; // end of current gap
+
+                       // "merge" two gaps
+                       end = s;
+                       size += count;
+               }
+
+               // Collapse all gaps, return past-the-end pointer
+               char_t* flush(char_t* s)
+               {
+                       if (end)
+                       {
+                               // Move [old_gap_end, current_pos) to [old_gap_start, ...)
+                               assert(s >= end);
+                               memmove(end - size, end, reinterpret_cast<char*>(s) - reinterpret_cast<char*>(end));
+
+                               return s - size;
+                       }
+                       else return s;
+               }
+       };
+
+       PUGI__FN char_t* strconv_escape(char_t* s, gap& g)
+       {
+               char_t* stre = s + 1;
+
+               switch (*stre)
+               {
+                       case '#':       // &#...
+                       {
+                               unsigned int ucsc = 0;
+
+                               if (stre[1] == 'x') // &#x... (hex code)
+                               {
+                                       stre += 2;
+
+                                       char_t ch = *stre;
+
+                                       if (ch == ';') return stre;
+
+                                       for (;;)
+                                       {
+                                               if (static_cast<unsigned int>(ch - '0') <= 9)
+                                                       ucsc = 16 * ucsc + (ch - '0');
+                                               else if (static_cast<unsigned int>((ch | ' ') - 'a') <= 5)
+                                                       ucsc = 16 * ucsc + ((ch | ' ') - 'a' + 10);
+                                               else if (ch == ';')
+                                                       break;
+                                               else // cancel
+                                                       return stre;
+
+                                               ch = *++stre;
+                                       }
+
+                                       ++stre;
+                               }
+                               else    // &#... (dec code)
+                               {
+                                       char_t ch = *++stre;
+
+                                       if (ch == ';') return stre;
+
+                                       for (;;)
+                                       {
+                                               if (static_cast<unsigned int>(ch - '0') <= 9)
+                                                       ucsc = 10 * ucsc + (ch - '0');
+                                               else if (ch == ';')
+                                                       break;
+                                               else // cancel
+                                                       return stre;
+
+                                               ch = *++stre;
+                                       }
+
+                                       ++stre;
+                               }
+
+                       #ifdef PUGIXML_WCHAR_MODE
+                               s = reinterpret_cast<char_t*>(wchar_writer::any(reinterpret_cast<wchar_writer::value_type>(s), ucsc));
+                       #else
+                               s = reinterpret_cast<char_t*>(utf8_writer::any(reinterpret_cast<uint8_t*>(s), ucsc));
+                       #endif
+
+                               g.push(s, stre - s);
+                               return stre;
+                       }
+
+                       case 'a':       // &a
+                       {
+                               ++stre;
+
+                               if (*stre == 'm') // &am
+                               {
+                                       if (*++stre == 'p' && *++stre == ';') // &amp;
+                                       {
+                                               *s++ = '&';
+                                               ++stre;
+
+                                               g.push(s, stre - s);
+                                               return stre;
+                                       }
+                               }
+                               else if (*stre == 'p') // &ap
+                               {
+                                       if (*++stre == 'o' && *++stre == 's' && *++stre == ';') // &apos;
+                                       {
+                                               *s++ = '\'';
+                                               ++stre;
+
+                                               g.push(s, stre - s);
+                                               return stre;
+                                       }
+                               }
+                               break;
+                       }
+
+                       case 'g': // &g
+                       {
+                               if (*++stre == 't' && *++stre == ';') // &gt;
+                               {
+                                       *s++ = '>';
+                                       ++stre;
+
+                                       g.push(s, stre - s);
+                                       return stre;
+                               }
+                               break;
+                       }
+
+                       case 'l': // &l
+                       {
+                               if (*++stre == 't' && *++stre == ';') // &lt;
+                               {
+                                       *s++ = '<';
+                                       ++stre;
+
+                                       g.push(s, stre - s);
+                                       return stre;
+                               }
+                               break;
+                       }
+
+                       case 'q': // &q
+                       {
+                               if (*++stre == 'u' && *++stre == 'o' && *++stre == 't' && *++stre == ';') // &quot;
+                               {
+                                       *s++ = '"';
+                                       ++stre;
+
+                                       g.push(s, stre - s);
+                                       return stre;
+                               }
+                               break;
+                       }
+
+                       default:
+                               break;
+               }
+
+               return stre;
+       }
+
+       // Parser utilities
+       #define PUGI__ENDSWITH(c, e)        ((c) == (e) || ((c) == 0 && endch == (e)))
+       #define PUGI__SKIPWS()              { while (PUGI__IS_CHARTYPE(*s, ct_space)) ++s; }
+       #define PUGI__OPTSET(OPT)           ( optmsk & (OPT) )
+       #define PUGI__PUSHNODE(TYPE)        { cursor = append_new_node(cursor, *alloc, TYPE); if (!cursor) PUGI__THROW_ERROR(status_out_of_memory, s); }
+       #define PUGI__POPNODE()             { cursor = cursor->parent; }
+       #define PUGI__SCANFOR(X)            { while (*s != 0 && !(X)) ++s; }
+       #define PUGI__SCANWHILE(X)          { while (X) ++s; }
+       #define PUGI__SCANWHILE_UNROLL(X)   { for (;;) { char_t ss = s[0]; if (PUGI__UNLIKELY(!(X))) { break; } ss = s[1]; if (PUGI__UNLIKELY(!(X))) { s += 1; break; } ss = s[2]; if (PUGI__UNLIKELY(!(X))) { s += 2; break; } ss = s[3]; if (PUGI__UNLIKELY(!(X))) { s += 3; break; } s += 4; } }
+       #define PUGI__ENDSEG()              { ch = *s; *s = 0; ++s; }
+       #define PUGI__THROW_ERROR(err, m)   return error_offset = m, error_status = err, static_cast<char_t*>(0)
+       #define PUGI__CHECK_ERROR(err, m)   { if (*s == 0) PUGI__THROW_ERROR(err, m); }
+
+       PUGI__FN char_t* strconv_comment(char_t* s, char_t endch)
+       {
+               gap g;
+
+               while (true)
+               {
+                       PUGI__SCANWHILE_UNROLL(!PUGI__IS_CHARTYPE(ss, ct_parse_comment));
+
+                       if (*s == '\r') // Either a single 0x0d or 0x0d 0x0a pair
+                       {
+                               *s++ = '\n'; // replace first one with 0x0a
+
+                               if (*s == '\n') g.push(s, 1);
+                       }
+                       else if (s[0] == '-' && s[1] == '-' && PUGI__ENDSWITH(s[2], '>')) // comment ends here
+                       {
+                               *g.flush(s) = 0;
+
+                               return s + (s[2] == '>' ? 3 : 2);
+                       }
+                       else if (*s == 0)
+                       {
+                               return 0;
+                       }
+                       else ++s;
+               }
+       }
+
+       PUGI__FN char_t* strconv_cdata(char_t* s, char_t endch)
+       {
+               gap g;
+
+               while (true)
+               {
+                       PUGI__SCANWHILE_UNROLL(!PUGI__IS_CHARTYPE(ss, ct_parse_cdata));
+
+                       if (*s == '\r') // Either a single 0x0d or 0x0d 0x0a pair
+                       {
+                               *s++ = '\n'; // replace first one with 0x0a
+
+                               if (*s == '\n') g.push(s, 1);
+                       }
+                       else if (s[0] == ']' && s[1] == ']' && PUGI__ENDSWITH(s[2], '>')) // CDATA ends here
+                       {
+                               *g.flush(s) = 0;
+
+                               return s + 1;
+                       }
+                       else if (*s == 0)
+                       {
+                               return 0;
+                       }
+                       else ++s;
+               }
+       }
+
+       typedef char_t* (*strconv_pcdata_t)(char_t*);
+
+       template <typename opt_trim, typename opt_eol, typename opt_escape> struct strconv_pcdata_impl
+       {
+               static char_t* parse(char_t* s)
+               {
+                       gap g;
+
+                       char_t* begin = s;
+
+                       while (true)
+                       {
+                               PUGI__SCANWHILE_UNROLL(!PUGI__IS_CHARTYPE(ss, ct_parse_pcdata));
+
+                               if (*s == '<') // PCDATA ends here
+                               {
+                                       char_t* end = g.flush(s);
+
+                                       if (opt_trim::value)
+                                               while (end > begin && PUGI__IS_CHARTYPE(end[-1], ct_space))
+                                                       --end;
+
+                                       *end = 0;
+
+                                       return s + 1;
+                               }
+                               else if (opt_eol::value && *s == '\r') // Either a single 0x0d or 0x0d 0x0a pair
+                               {
+                                       *s++ = '\n'; // replace first one with 0x0a
+
+                                       if (*s == '\n') g.push(s, 1);
+                               }
+                               else if (opt_escape::value && *s == '&')
+                               {
+                                       s = strconv_escape(s, g);
+                               }
+                               else if (*s == 0)
+                               {
+                                       char_t* end = g.flush(s);
+
+                                       if (opt_trim::value)
+                                               while (end > begin && PUGI__IS_CHARTYPE(end[-1], ct_space))
+                                                       --end;
+
+                                       *end = 0;
+
+                                       return s;
+                               }
+                               else ++s;
+                       }
+               }
+       };
+
+       PUGI__FN strconv_pcdata_t get_strconv_pcdata(unsigned int optmask)
+       {
+               PUGI__STATIC_ASSERT(parse_escapes == 0x10 && parse_eol == 0x20 && parse_trim_pcdata == 0x0800);
+
+               switch (((optmask >> 4) & 3) | ((optmask >> 9) & 4)) // get bitmask for flags (trim eol escapes); this simultaneously checks 3 options from assertion above
+               {
+               case 0: return strconv_pcdata_impl<opt_false, opt_false, opt_false>::parse;
+               case 1: return strconv_pcdata_impl<opt_false, opt_false, opt_true>::parse;
+               case 2: return strconv_pcdata_impl<opt_false, opt_true, opt_false>::parse;
+               case 3: return strconv_pcdata_impl<opt_false, opt_true, opt_true>::parse;
+               case 4: return strconv_pcdata_impl<opt_true, opt_false, opt_false>::parse;
+               case 5: return strconv_pcdata_impl<opt_true, opt_false, opt_true>::parse;
+               case 6: return strconv_pcdata_impl<opt_true, opt_true, opt_false>::parse;
+               case 7: return strconv_pcdata_impl<opt_true, opt_true, opt_true>::parse;
+               default: assert(false); return 0; // unreachable
+               }
+       }
+
+       typedef char_t* (*strconv_attribute_t)(char_t*, char_t);
+
+       template <typename opt_escape> struct strconv_attribute_impl
+       {
+               static char_t* parse_wnorm(char_t* s, char_t end_quote)
+               {
+                       gap g;
+
+                       // trim leading whitespaces
+                       if (PUGI__IS_CHARTYPE(*s, ct_space))
+                       {
+                               char_t* str = s;
+
+                               do ++str;
+                               while (PUGI__IS_CHARTYPE(*str, ct_space));
+
+                               g.push(s, str - s);
+                       }
+
+                       while (true)
+                       {
+                               PUGI__SCANWHILE_UNROLL(!PUGI__IS_CHARTYPE(ss, ct_parse_attr_ws | ct_space));
+
+                               if (*s == end_quote)
+                               {
+                                       char_t* str = g.flush(s);
+
+                                       do *str-- = 0;
+                                       while (PUGI__IS_CHARTYPE(*str, ct_space));
+
+                                       return s + 1;
+                               }
+                               else if (PUGI__IS_CHARTYPE(*s, ct_space))
+                               {
+                                       *s++ = ' ';
+
+                                       if (PUGI__IS_CHARTYPE(*s, ct_space))
+                                       {
+                                               char_t* str = s + 1;
+                                               while (PUGI__IS_CHARTYPE(*str, ct_space)) ++str;
+
+                                               g.push(s, str - s);
+                                       }
+                               }
+                               else if (opt_escape::value && *s == '&')
+                               {
+                                       s = strconv_escape(s, g);
+                               }
+                               else if (!*s)
+                               {
+                                       return 0;
+                               }
+                               else ++s;
+                       }
+               }
+
+               static char_t* parse_wconv(char_t* s, char_t end_quote)
+               {
+                       gap g;
+
+                       while (true)
+                       {
+                               PUGI__SCANWHILE_UNROLL(!PUGI__IS_CHARTYPE(ss, ct_parse_attr_ws));
+
+                               if (*s == end_quote)
+                               {
+                                       *g.flush(s) = 0;
+
+                                       return s + 1;
+                               }
+                               else if (PUGI__IS_CHARTYPE(*s, ct_space))
+                               {
+                                       if (*s == '\r')
+                                       {
+                                               *s++ = ' ';
+
+                                               if (*s == '\n') g.push(s, 1);
+                                       }
+                                       else *s++ = ' ';
+                               }
+                               else if (opt_escape::value && *s == '&')
+                               {
+                                       s = strconv_escape(s, g);
+                               }
+                               else if (!*s)
+                               {
+                                       return 0;
+                               }
+                               else ++s;
+                       }
+               }
+
+               static char_t* parse_eol(char_t* s, char_t end_quote)
+               {
+                       gap g;
+
+                       while (true)
+                       {
+                               PUGI__SCANWHILE_UNROLL(!PUGI__IS_CHARTYPE(ss, ct_parse_attr));
+
+                               if (*s == end_quote)
+                               {
+                                       *g.flush(s) = 0;
+
+                                       return s + 1;
+                               }
+                               else if (*s == '\r')
+                               {
+                                       *s++ = '\n';
+
+                                       if (*s == '\n') g.push(s, 1);
+                               }
+                               else if (opt_escape::value && *s == '&')
+                               {
+                                       s = strconv_escape(s, g);
+                               }
+                               else if (!*s)
+                               {
+                                       return 0;
+                               }
+                               else ++s;
+                       }
+               }
+
+               static char_t* parse_simple(char_t* s, char_t end_quote)
+               {
+                       gap g;
+
+                       while (true)
+                       {
+                               PUGI__SCANWHILE_UNROLL(!PUGI__IS_CHARTYPE(ss, ct_parse_attr));
+
+                               if (*s == end_quote)
+                               {
+                                       *g.flush(s) = 0;
+
+                                       return s + 1;
+                               }
+                               else if (opt_escape::value && *s == '&')
+                               {
+                                       s = strconv_escape(s, g);
+                               }
+                               else if (!*s)
+                               {
+                                       return 0;
+                               }
+                               else ++s;
+                       }
+               }
+       };
+
+       PUGI__FN strconv_attribute_t get_strconv_attribute(unsigned int optmask)
+       {
+               PUGI__STATIC_ASSERT(parse_escapes == 0x10 && parse_eol == 0x20 && parse_wconv_attribute == 0x40 && parse_wnorm_attribute == 0x80);
+
+               switch ((optmask >> 4) & 15) // get bitmask for flags (wnorm wconv eol escapes); this simultaneously checks 4 options from assertion above
+               {
+               case 0:  return strconv_attribute_impl<opt_false>::parse_simple;
+               case 1:  return strconv_attribute_impl<opt_true>::parse_simple;
+               case 2:  return strconv_attribute_impl<opt_false>::parse_eol;
+               case 3:  return strconv_attribute_impl<opt_true>::parse_eol;
+               case 4:  return strconv_attribute_impl<opt_false>::parse_wconv;
+               case 5:  return strconv_attribute_impl<opt_true>::parse_wconv;
+               case 6:  return strconv_attribute_impl<opt_false>::parse_wconv;
+               case 7:  return strconv_attribute_impl<opt_true>::parse_wconv;
+               case 8:  return strconv_attribute_impl<opt_false>::parse_wnorm;
+               case 9:  return strconv_attribute_impl<opt_true>::parse_wnorm;
+               case 10: return strconv_attribute_impl<opt_false>::parse_wnorm;
+               case 11: return strconv_attribute_impl<opt_true>::parse_wnorm;
+               case 12: return strconv_attribute_impl<opt_false>::parse_wnorm;
+               case 13: return strconv_attribute_impl<opt_true>::parse_wnorm;
+               case 14: return strconv_attribute_impl<opt_false>::parse_wnorm;
+               case 15: return strconv_attribute_impl<opt_true>::parse_wnorm;
+               default: assert(false); return 0; // unreachable
+               }
+       }
+
+       inline xml_parse_result make_parse_result(xml_parse_status status, ptrdiff_t offset = 0)
+       {
+               xml_parse_result result;
+               result.status = status;
+               result.offset = offset;
+
+               return result;
+       }
+
+       struct xml_parser
+       {
+               xml_allocator* alloc;
+               char_t* error_offset;
+               xml_parse_status error_status;
+
+               xml_parser(xml_allocator* alloc_): alloc(alloc_), error_offset(0), error_status(status_ok)
+               {
+               }
+
+               // DOCTYPE consists of nested sections of the following possible types:
+               // <!-- ... -->, <? ... ?>, "...", '...'
+               // <![...]]>
+               // <!...>
+               // First group can not contain nested groups
+               // Second group can contain nested groups of the same type
+               // Third group can contain all other groups
+               char_t* parse_doctype_primitive(char_t* s)
+               {
+                       if (*s == '"' || *s == '\'')
+                       {
+                               // quoted string
+                               char_t ch = *s++;
+                               PUGI__SCANFOR(*s == ch);
+                               if (!*s) PUGI__THROW_ERROR(status_bad_doctype, s);
+
+                               s++;
+                       }
+                       else if (s[0] == '<' && s[1] == '?')
+                       {
+                               // <? ... ?>
+                               s += 2;
+                               PUGI__SCANFOR(s[0] == '?' && s[1] == '>'); // no need for ENDSWITH because ?> can't terminate proper doctype
+                               if (!*s) PUGI__THROW_ERROR(status_bad_doctype, s);
+
+                               s += 2;
+                       }
+                       else if (s[0] == '<' && s[1] == '!' && s[2] == '-' && s[3] == '-')
+                       {
+                               s += 4;
+                               PUGI__SCANFOR(s[0] == '-' && s[1] == '-' && s[2] == '>'); // no need for ENDSWITH because --> can't terminate proper doctype
+                               if (!*s) PUGI__THROW_ERROR(status_bad_doctype, s);
+
+                               s += 3;
+                       }
+                       else PUGI__THROW_ERROR(status_bad_doctype, s);
+
+                       return s;
+               }
+
+               char_t* parse_doctype_ignore(char_t* s)
+               {
+                       size_t depth = 0;
+
+                       assert(s[0] == '<' && s[1] == '!' && s[2] == '[');
+                       s += 3;
+
+                       while (*s)
+                       {
+                               if (s[0] == '<' && s[1] == '!' && s[2] == '[')
+                               {
+                                       // nested ignore section
+                                       s += 3;
+                                       depth++;
+                               }
+                               else if (s[0] == ']' && s[1] == ']' && s[2] == '>')
+                               {
+                                       // ignore section end
+                                       s += 3;
+
+                                       if (depth == 0)
+                                               return s;
+
+                                       depth--;
+                               }
+                               else s++;
+                       }
+
+                       PUGI__THROW_ERROR(status_bad_doctype, s);
+               }
+
+               char_t* parse_doctype_group(char_t* s, char_t endch)
+               {
+                       size_t depth = 0;
+
+                       assert((s[0] == '<' || s[0] == 0) && s[1] == '!');
+                       s += 2;
+
+                       while (*s)
+                       {
+                               if (s[0] == '<' && s[1] == '!' && s[2] != '-')
+                               {
+                                       if (s[2] == '[')
+                                       {
+                                               // ignore
+                                               s = parse_doctype_ignore(s);
+                                               if (!s) return s;
+                                       }
+                                       else
+                                       {
+                                               // some control group
+                                               s += 2;
+                                               depth++;
+                                       }
+                               }
+                               else if (s[0] == '<' || s[0] == '"' || s[0] == '\'')
+                               {
+                                       // unknown tag (forbidden), or some primitive group
+                                       s = parse_doctype_primitive(s);
+                                       if (!s) return s;
+                               }
+                               else if (*s == '>')
+                               {
+                                       if (depth == 0)
+                                               return s;
+
+                                       depth--;
+                                       s++;
+                               }
+                               else s++;
+                       }
+
+                       if (depth != 0 || endch != '>') PUGI__THROW_ERROR(status_bad_doctype, s);
+
+                       return s;
+               }
+
+               char_t* parse_exclamation(char_t* s, xml_node_struct* cursor, unsigned int optmsk, char_t endch)
+               {
+                       // parse node contents, starting with exclamation mark
+                       ++s;
+
+                       if (*s == '-') // '<!-...'
+                       {
+                               ++s;
+
+                               if (*s == '-') // '<!--...'
+                               {
+                                       ++s;
+
+                                       if (PUGI__OPTSET(parse_comments))
+                                       {
+                                               PUGI__PUSHNODE(node_comment); // Append a new node on the tree.
+                                               cursor->value = s; // Save the offset.
+                                       }
+
+                                       if (PUGI__OPTSET(parse_eol) && PUGI__OPTSET(parse_comments))
+                                       {
+                                               s = strconv_comment(s, endch);
+
+                                               if (!s) PUGI__THROW_ERROR(status_bad_comment, cursor->value);
+                                       }
+                                       else
+                                       {
+                                               // Scan for terminating '-->'.
+                                               PUGI__SCANFOR(s[0] == '-' && s[1] == '-' && PUGI__ENDSWITH(s[2], '>'));
+                                               PUGI__CHECK_ERROR(status_bad_comment, s);
+
+                                               if (PUGI__OPTSET(parse_comments))
+                                                       *s = 0; // Zero-terminate this segment at the first terminating '-'.
+
+                                               s += (s[2] == '>' ? 3 : 2); // Step over the '\0->'.
+                                       }
+                               }
+                               else PUGI__THROW_ERROR(status_bad_comment, s);
+                       }
+                       else if (*s == '[')
+                       {
+                               // '<![CDATA[...'
+                               if (*++s=='C' && *++s=='D' && *++s=='A' && *++s=='T' && *++s=='A' && *++s == '[')
+                               {
+                                       ++s;
+
+                                       if (PUGI__OPTSET(parse_cdata))
+                                       {
+                                               PUGI__PUSHNODE(node_cdata); // Append a new node on the tree.
+                                               cursor->value = s; // Save the offset.
+
+                                               if (PUGI__OPTSET(parse_eol))
+                                               {
+                                                       s = strconv_cdata(s, endch);
+
+                                                       if (!s) PUGI__THROW_ERROR(status_bad_cdata, cursor->value);
+                                               }
+                                               else
+                                               {
+                                                       // Scan for terminating ']]>'.
+                                                       PUGI__SCANFOR(s[0] == ']' && s[1] == ']' && PUGI__ENDSWITH(s[2], '>'));
+                                                       PUGI__CHECK_ERROR(status_bad_cdata, s);
+
+                                                       *s++ = 0; // Zero-terminate this segment.
+                                               }
+                                       }
+                                       else // Flagged for discard, but we still have to scan for the terminator.
+                                       {
+                                               // Scan for terminating ']]>'.
+                                               PUGI__SCANFOR(s[0] == ']' && s[1] == ']' && PUGI__ENDSWITH(s[2], '>'));
+                                               PUGI__CHECK_ERROR(status_bad_cdata, s);
+
+                                               ++s;
+                                       }
+
+                                       s += (s[1] == '>' ? 2 : 1); // Step over the last ']>'.
+                               }
+                               else PUGI__THROW_ERROR(status_bad_cdata, s);
+                       }
+                       else if (s[0] == 'D' && s[1] == 'O' && s[2] == 'C' && s[3] == 'T' && s[4] == 'Y' && s[5] == 'P' && PUGI__ENDSWITH(s[6], 'E'))
+                       {
+                               s -= 2;
+
+                               if (cursor->parent) PUGI__THROW_ERROR(status_bad_doctype, s);
+
+                               char_t* mark = s + 9;
+
+                               s = parse_doctype_group(s, endch);
+                               if (!s) return s;
+
+                               assert((*s == 0 && endch == '>') || *s == '>');
+                               if (*s) *s++ = 0;
+
+                               if (PUGI__OPTSET(parse_doctype))
+                               {
+                                       while (PUGI__IS_CHARTYPE(*mark, ct_space)) ++mark;
+
+                                       PUGI__PUSHNODE(node_doctype);
+
+                                       cursor->value = mark;
+                               }
+                       }
+                       else if (*s == 0 && endch == '-') PUGI__THROW_ERROR(status_bad_comment, s);
+                       else if (*s == 0 && endch == '[') PUGI__THROW_ERROR(status_bad_cdata, s);
+                       else PUGI__THROW_ERROR(status_unrecognized_tag, s);
+
+                       return s;
+               }
+
+               char_t* parse_question(char_t* s, xml_node_struct*& ref_cursor, unsigned int optmsk, char_t endch)
+               {
+                       // load into registers
+                       xml_node_struct* cursor = ref_cursor;
+                       char_t ch = 0;
+
+                       // parse node contents, starting with question mark
+                       ++s;
+
+                       // read PI target
+                       char_t* target = s;
+
+                       if (!PUGI__IS_CHARTYPE(*s, ct_start_symbol)) PUGI__THROW_ERROR(status_bad_pi, s);
+
+                       PUGI__SCANWHILE(PUGI__IS_CHARTYPE(*s, ct_symbol));
+                       PUGI__CHECK_ERROR(status_bad_pi, s);
+
+                       // determine node type; stricmp / strcasecmp is not portable
+                       bool declaration = (target[0] | ' ') == 'x' && (target[1] | ' ') == 'm' && (target[2] | ' ') == 'l' && target + 3 == s;
+
+                       if (declaration ? PUGI__OPTSET(parse_declaration) : PUGI__OPTSET(parse_pi))
+                       {
+                               if (declaration)
+                               {
+                                       // disallow non top-level declarations
+                                       if (cursor->parent) PUGI__THROW_ERROR(status_bad_pi, s);
+
+                                       PUGI__PUSHNODE(node_declaration);
+                               }
+                               else
+                               {
+                                       PUGI__PUSHNODE(node_pi);
+                               }
+
+                               cursor->name = target;
+
+                               PUGI__ENDSEG();
+
+                               // parse value/attributes
+                               if (ch == '?')
+                               {
+                                       // empty node
+                                       if (!PUGI__ENDSWITH(*s, '>')) PUGI__THROW_ERROR(status_bad_pi, s);
+                                       s += (*s == '>');
+
+                                       PUGI__POPNODE();
+                               }
+                               else if (PUGI__IS_CHARTYPE(ch, ct_space))
+                               {
+                                       PUGI__SKIPWS();
+
+                                       // scan for tag end
+                                       char_t* value = s;
+
+                                       PUGI__SCANFOR(s[0] == '?' && PUGI__ENDSWITH(s[1], '>'));
+                                       PUGI__CHECK_ERROR(status_bad_pi, s);
+
+                                       if (declaration)
+                                       {
+                                               // replace ending ? with / so that 'element' terminates properly
+                                               *s = '/';
+
+                                               // we exit from this function with cursor at node_declaration, which is a signal to parse() to go to LOC_ATTRIBUTES
+                                               s = value;
+                                       }
+                                       else
+                                       {
+                                               // store value and step over >
+                                               cursor->value = value;
+
+                                               PUGI__POPNODE();
+
+                                               PUGI__ENDSEG();
+
+                                               s += (*s == '>');
+                                       }
+                               }
+                               else PUGI__THROW_ERROR(status_bad_pi, s);
+                       }
+                       else
+                       {
+                               // scan for tag end
+                               PUGI__SCANFOR(s[0] == '?' && PUGI__ENDSWITH(s[1], '>'));
+                               PUGI__CHECK_ERROR(status_bad_pi, s);
+
+                               s += (s[1] == '>' ? 2 : 1);
+                       }
+
+                       // store from registers
+                       ref_cursor = cursor;
+
+                       return s;
+               }
+
+               char_t* parse_tree(char_t* s, xml_node_struct* root, unsigned int optmsk, char_t endch)
+               {
+                       strconv_attribute_t strconv_attribute = get_strconv_attribute(optmsk);
+                       strconv_pcdata_t strconv_pcdata = get_strconv_pcdata(optmsk);
+
+                       char_t ch = 0;
+                       xml_node_struct* cursor = root;
+                       char_t* mark = s;
+
+                       while (*s != 0)
+                       {
+                               if (*s == '<')
+                               {
+                                       ++s;
+
+                               LOC_TAG:
+                                       if (PUGI__IS_CHARTYPE(*s, ct_start_symbol)) // '<#...'
+                                       {
+                                               PUGI__PUSHNODE(node_element); // Append a new node to the tree.
+
+                                               cursor->name = s;
+
+                                               PUGI__SCANWHILE_UNROLL(PUGI__IS_CHARTYPE(ss, ct_symbol)); // Scan for a terminator.
+                                               PUGI__ENDSEG(); // Save char in 'ch', terminate & step over.
+
+                                               if (ch == '>')
+                                               {
+                                                       // end of tag
+                                               }
+                                               else if (PUGI__IS_CHARTYPE(ch, ct_space))
+                                               {
+                                               LOC_ATTRIBUTES:
+                                                       while (true)
+                                                       {
+                                                               PUGI__SKIPWS(); // Eat any whitespace.
+
+                                                               if (PUGI__IS_CHARTYPE(*s, ct_start_symbol)) // <... #...
+                                                               {
+                                                                       xml_attribute_struct* a = append_new_attribute(cursor, *alloc); // Make space for this attribute.
+                                                                       if (!a) PUGI__THROW_ERROR(status_out_of_memory, s);
+
+                                                                       a->name = s; // Save the offset.
+
+                                                                       PUGI__SCANWHILE_UNROLL(PUGI__IS_CHARTYPE(ss, ct_symbol)); // Scan for a terminator.
+                                                                       PUGI__ENDSEG(); // Save char in 'ch', terminate & step over.
+
+                                                                       if (PUGI__IS_CHARTYPE(ch, ct_space))
+                                                                       {
+                                                                               PUGI__SKIPWS(); // Eat any whitespace.
+
+                                                                               ch = *s;
+                                                                               ++s;
+                                                                       }
+
+                                                                       if (ch == '=') // '<... #=...'
+                                                                       {
+                                                                               PUGI__SKIPWS(); // Eat any whitespace.
+
+                                                                               if (*s == '"' || *s == '\'') // '<... #="...'
+                                                                               {
+                                                                                       ch = *s; // Save quote char to avoid breaking on "''" -or- '""'.
+                                                                                       ++s; // Step over the quote.
+                                                                                       a->value = s; // Save the offset.
+
+                                                                                       s = strconv_attribute(s, ch);
+
+                                                                                       if (!s) PUGI__THROW_ERROR(status_bad_attribute, a->value);
+
+                                                                                       // After this line the loop continues from the start;
+                                                                                       // Whitespaces, / and > are ok, symbols and EOF are wrong,
+                                                                                       // everything else will be detected
+                                                                                       if (PUGI__IS_CHARTYPE(*s, ct_start_symbol)) PUGI__THROW_ERROR(status_bad_attribute, s);
+                                                                               }
+                                                                               else PUGI__THROW_ERROR(status_bad_attribute, s);
+                                                                       }
+                                                                       else PUGI__THROW_ERROR(status_bad_attribute, s);
+                                                               }
+                                                               else if (*s == '/')
+                                                               {
+                                                                       ++s;
+
+                                                                       if (*s == '>')
+                                                                       {
+                                                                               PUGI__POPNODE();
+                                                                               s++;
+                                                                               break;
+                                                                       }
+                                                                       else if (*s == 0 && endch == '>')
+                                                                       {
+                                                                               PUGI__POPNODE();
+                                                                               break;
+                                                                       }
+                                                                       else PUGI__THROW_ERROR(status_bad_start_element, s);
+                                                               }
+                                                               else if (*s == '>')
+                                                               {
+                                                                       ++s;
+
+                                                                       break;
+                                                               }
+                                                               else if (*s == 0 && endch == '>')
+                                                               {
+                                                                       break;
+                                                               }
+                                                               else PUGI__THROW_ERROR(status_bad_start_element, s);
+                                                       }
+
+                                                       // !!!
+                                               }
+                                               else if (ch == '/') // '<#.../'
+                                               {
+                                                       if (!PUGI__ENDSWITH(*s, '>')) PUGI__THROW_ERROR(status_bad_start_element, s);
+
+                                                       PUGI__POPNODE(); // Pop.
+
+                                                       s += (*s == '>');
+                                               }
+                                               else if (ch == 0)
+                                               {
+                                                       // we stepped over null terminator, backtrack & handle closing tag
+                                                       --s;
+
+                                                       if (endch != '>') PUGI__THROW_ERROR(status_bad_start_element, s);
+                                               }
+                                               else PUGI__THROW_ERROR(status_bad_start_element, s);
+                                       }
+                                       else if (*s == '/')
+                                       {
+                                               ++s;
+
+                                               mark = s;
+
+                                               char_t* name = cursor->name;
+                                               if (!name) PUGI__THROW_ERROR(status_end_element_mismatch, mark);
+
+                                               while (PUGI__IS_CHARTYPE(*s, ct_symbol))
+                                               {
+                                                       if (*s++ != *name++) PUGI__THROW_ERROR(status_end_element_mismatch, mark);
+                                               }
+
+                                               if (*name)
+                                               {
+                                                       if (*s == 0 && name[0] == endch && name[1] == 0) PUGI__THROW_ERROR(status_bad_end_element, s);
+                                                       else PUGI__THROW_ERROR(status_end_element_mismatch, mark);
+                                               }
+
+                                               PUGI__POPNODE(); // Pop.
+
+                                               PUGI__SKIPWS();
+
+                                               if (*s == 0)
+                                               {
+                                                       if (endch != '>') PUGI__THROW_ERROR(status_bad_end_element, s);
+                                               }
+                                               else
+                                               {
+                                                       if (*s != '>') PUGI__THROW_ERROR(status_bad_end_element, s);
+                                                       ++s;
+                                               }
+                                       }
+                                       else if (*s == '?') // '<?...'
+                                       {
+                                               s = parse_question(s, cursor, optmsk, endch);
+                                               if (!s) return s;
+
+                                               assert(cursor);
+                                               if (PUGI__NODETYPE(cursor) == node_declaration) goto LOC_ATTRIBUTES;
+                                       }
+                                       else if (*s == '!') // '<!...'
+                                       {
+                                               s = parse_exclamation(s, cursor, optmsk, endch);
+                                               if (!s) return s;
+                                       }
+                                       else if (*s == 0 && endch == '?') PUGI__THROW_ERROR(status_bad_pi, s);
+                                       else PUGI__THROW_ERROR(status_unrecognized_tag, s);
+                               }
+                               else
+                               {
+                                       mark = s; // Save this offset while searching for a terminator.
+
+                                       PUGI__SKIPWS(); // Eat whitespace if no genuine PCDATA here.
+
+                                       if (*s == '<' || !*s)
+                                       {
+                                               // We skipped some whitespace characters because otherwise we would take the tag branch instead of PCDATA one
+                                               assert(mark != s);
+
+                                               if (!PUGI__OPTSET(parse_ws_pcdata | parse_ws_pcdata_single) || PUGI__OPTSET(parse_trim_pcdata))
+                                               {
+                                                       continue;
+                                               }
+                                               else if (PUGI__OPTSET(parse_ws_pcdata_single))
+                                               {
+                                                       if (s[0] != '<' || s[1] != '/' || cursor->first_child) continue;
+                                               }
+                                       }
+
+                                       if (!PUGI__OPTSET(parse_trim_pcdata))
+                                               s = mark;
+
+                                       if (cursor->parent || PUGI__OPTSET(parse_fragment))
+                                       {
+                                               if (PUGI__OPTSET(parse_embed_pcdata) && cursor->parent && !cursor->first_child && !cursor->value)
+                                               {
+                                                       cursor->value = s; // Save the offset.
+                                               }
+                                               else
+                                               {
+                                                       PUGI__PUSHNODE(node_pcdata); // Append a new node on the tree.
+
+                                                       cursor->value = s; // Save the offset.
+
+                                                       PUGI__POPNODE(); // Pop since this is a standalone.
+                                               }
+
+                                               s = strconv_pcdata(s);
+
+                                               if (!*s) break;
+                                       }
+                                       else
+                                       {
+                                               PUGI__SCANFOR(*s == '<'); // '...<'
+                                               if (!*s) break;
+
+                                               ++s;
+                                       }
+
+                                       // We're after '<'
+                                       goto LOC_TAG;
+                               }
+                       }
+
+                       // check that last tag is closed
+                       if (cursor != root) PUGI__THROW_ERROR(status_end_element_mismatch, s);
+
+                       return s;
+               }
+
+       #ifdef PUGIXML_WCHAR_MODE
+               static char_t* parse_skip_bom(char_t* s)
+               {
+                       unsigned int bom = 0xfeff;
+                       return (s[0] == static_cast<wchar_t>(bom)) ? s + 1 : s;
+               }
+       #else
+               static char_t* parse_skip_bom(char_t* s)
+               {
+                       return (s[0] == '\xef' && s[1] == '\xbb' && s[2] == '\xbf') ? s + 3 : s;
+               }
+       #endif
+
+               static bool has_element_node_siblings(xml_node_struct* node)
+               {
+                       while (node)
+                       {
+                               if (PUGI__NODETYPE(node) == node_element) return true;
+
+                               node = node->next_sibling;
+                       }
+
+                       return false;
+               }
+
+               static xml_parse_result parse(char_t* buffer, size_t length, xml_document_struct* xmldoc, xml_node_struct* root, unsigned int optmsk)
+               {
+                       // early-out for empty documents
+                       if (length == 0)
+                               return make_parse_result(PUGI__OPTSET(parse_fragment) ? status_ok : status_no_document_element);
+
+                       // get last child of the root before parsing
+                       xml_node_struct* last_root_child = root->first_child ? root->first_child->prev_sibling_c + 0 : 0;
+
+                       // create parser on stack
+                       xml_parser parser(static_cast<xml_allocator*>(xmldoc));
+
+                       // save last character and make buffer zero-terminated (speeds up parsing)
+                       char_t endch = buffer[length - 1];
+                       buffer[length - 1] = 0;
+
+                       // skip BOM to make sure it does not end up as part of parse output
+                       char_t* buffer_data = parse_skip_bom(buffer);
+
+                       // perform actual parsing
+                       parser.parse_tree(buffer_data, root, optmsk, endch);
+
+                       xml_parse_result result = make_parse_result(parser.error_status, parser.error_offset ? parser.error_offset - buffer : 0);
+                       assert(result.offset >= 0 && static_cast<size_t>(result.offset) <= length);
+
+                       if (result)
+                       {
+                               // since we removed last character, we have to handle the only possible false positive (stray <)
+                               if (endch == '<')
+                                       return make_parse_result(status_unrecognized_tag, length - 1);
+
+                               // check if there are any element nodes parsed
+                               xml_node_struct* first_root_child_parsed = last_root_child ? last_root_child->next_sibling + 0 : root->first_child+ 0;
+
+                               if (!PUGI__OPTSET(parse_fragment) && !has_element_node_siblings(first_root_child_parsed))
+                                       return make_parse_result(status_no_document_element, length - 1);
+                       }
+                       else
+                       {
+                               // roll back offset if it occurs on a null terminator in the source buffer
+                               if (result.offset > 0 && static_cast<size_t>(result.offset) == length - 1 && endch == 0)
+                                       result.offset--;
+                       }
+
+                       return result;
+               }
+       };
+
+       // Output facilities
+       PUGI__FN xml_encoding get_write_native_encoding()
+       {
+       #ifdef PUGIXML_WCHAR_MODE
+               return get_wchar_encoding();
+       #else
+               return encoding_utf8;
+       #endif
+       }
+
+       PUGI__FN xml_encoding get_write_encoding(xml_encoding encoding)
+       {
+               // replace wchar encoding with utf implementation
+               if (encoding == encoding_wchar) return get_wchar_encoding();
+
+               // replace utf16 encoding with utf16 with specific endianness
+               if (encoding == encoding_utf16) return is_little_endian() ? encoding_utf16_le : encoding_utf16_be;
+
+               // replace utf32 encoding with utf32 with specific endianness
+               if (encoding == encoding_utf32) return is_little_endian() ? encoding_utf32_le : encoding_utf32_be;
+
+               // only do autodetection if no explicit encoding is requested
+               if (encoding != encoding_auto) return encoding;
+
+               // assume utf8 encoding
+               return encoding_utf8;
+       }
+
+       template <typename D, typename T> PUGI__FN size_t convert_buffer_output_generic(typename T::value_type dest, const char_t* data, size_t length, D, T)
+       {
+               PUGI__STATIC_ASSERT(sizeof(char_t) == sizeof(typename D::type));
+
+               typename T::value_type end = D::process(reinterpret_cast<const typename D::type*>(data), length, dest, T());
+
+               return static_cast<size_t>(end - dest) * sizeof(*dest);
+       }
+
+       template <typename D, typename T> PUGI__FN size_t convert_buffer_output_generic(typename T::value_type dest, const char_t* data, size_t length, D, T, bool opt_swap)
+       {
+               PUGI__STATIC_ASSERT(sizeof(char_t) == sizeof(typename D::type));
+
+               typename T::value_type end = D::process(reinterpret_cast<const typename D::type*>(data), length, dest, T());
+
+               if (opt_swap)
+               {
+                       for (typename T::value_type i = dest; i != end; ++i)
+                               *i = endian_swap(*i);
+               }
+
+               return static_cast<size_t>(end - dest) * sizeof(*dest);
+       }
+
+#ifdef PUGIXML_WCHAR_MODE
+       PUGI__FN size_t get_valid_length(const char_t* data, size_t length)
+       {
+               if (length < 1) return 0;
+
+               // discard last character if it's the lead of a surrogate pair
+               return (sizeof(wchar_t) == 2 && static_cast<unsigned int>(static_cast<uint16_t>(data[length - 1]) - 0xD800) < 0x400) ? length - 1 : length;
+       }
+
+       PUGI__FN size_t convert_buffer_output(char_t* r_char, uint8_t* r_u8, uint16_t* r_u16, uint32_t* r_u32, const char_t* data, size_t length, xml_encoding encoding)
+       {
+               // only endian-swapping is required
+               if (need_endian_swap_utf(encoding, get_wchar_encoding()))
+               {
+                       convert_wchar_endian_swap(r_char, data, length);
+
+                       return length * sizeof(char_t);
+               }
+
+               // convert to utf8
+               if (encoding == encoding_utf8)
+                       return convert_buffer_output_generic(r_u8, data, length, wchar_decoder(), utf8_writer());
+
+               // convert to utf16
+               if (encoding == encoding_utf16_be || encoding == encoding_utf16_le)
+               {
+                       xml_encoding native_encoding = is_little_endian() ? encoding_utf16_le : encoding_utf16_be;
+
+                       return convert_buffer_output_generic(r_u16, data, length, wchar_decoder(), utf16_writer(), native_encoding != encoding);
+               }
+
+               // convert to utf32
+               if (encoding == encoding_utf32_be || encoding == encoding_utf32_le)
+               {
+                       xml_encoding native_encoding = is_little_endian() ? encoding_utf32_le : encoding_utf32_be;
+
+                       return convert_buffer_output_generic(r_u32, data, length, wchar_decoder(), utf32_writer(), native_encoding != encoding);
+               }
+
+               // convert to latin1
+               if (encoding == encoding_latin1)
+                       return convert_buffer_output_generic(r_u8, data, length, wchar_decoder(), latin1_writer());
+
+               assert(false && "Invalid encoding"); // unreachable
+               return 0;
+       }
+#else
+       PUGI__FN size_t get_valid_length(const char_t* data, size_t length)
+       {
+               if (length < 5) return 0;
+
+               for (size_t i = 1; i <= 4; ++i)
+               {
+                       uint8_t ch = static_cast<uint8_t>(data[length - i]);
+
+                       // either a standalone character or a leading one
+                       if ((ch & 0xc0) != 0x80) return length - i;
+               }
+
+               // there are four non-leading characters at the end, sequence tail is broken so might as well process the whole chunk
+               return length;
+       }
+
+       PUGI__FN size_t convert_buffer_output(char_t* /* r_char */, uint8_t* r_u8, uint16_t* r_u16, uint32_t* r_u32, const char_t* data, size_t length, xml_encoding encoding)
+       {
+               if (encoding == encoding_utf16_be || encoding == encoding_utf16_le)
+               {
+                       xml_encoding native_encoding = is_little_endian() ? encoding_utf16_le : encoding_utf16_be;
+
+                       return convert_buffer_output_generic(r_u16, data, length, utf8_decoder(), utf16_writer(), native_encoding != encoding);
+               }
+
+               if (encoding == encoding_utf32_be || encoding == encoding_utf32_le)
+               {
+                       xml_encoding native_encoding = is_little_endian() ? encoding_utf32_le : encoding_utf32_be;
+
+                       return convert_buffer_output_generic(r_u32, data, length, utf8_decoder(), utf32_writer(), native_encoding != encoding);
+               }
+
+               if (encoding == encoding_latin1)
+                       return convert_buffer_output_generic(r_u8, data, length, utf8_decoder(), latin1_writer());
+
+               assert(false && "Invalid encoding"); // unreachable
+               return 0;
+       }
+#endif
+
+       class xml_buffered_writer
+       {
+               xml_buffered_writer(const xml_buffered_writer&);
+               xml_buffered_writer& operator=(const xml_buffered_writer&);
+
+       public:
+               xml_buffered_writer(xml_writer& writer_, xml_encoding user_encoding): writer(writer_), bufsize(0), encoding(get_write_encoding(user_encoding))
+               {
+                       PUGI__STATIC_ASSERT(bufcapacity >= 8);
+               }
+
+               size_t flush()
+               {
+                       flush(buffer, bufsize);
+                       bufsize = 0;
+                       return 0;
+               }
+
+               void flush(const char_t* data, size_t size)
+               {
+                       if (size == 0) return;
+
+                       // fast path, just write data
+                       if (encoding == get_write_native_encoding())
+                               writer.write(data, size * sizeof(char_t));
+                       else
+                       {
+                               // convert chunk
+                               size_t result = convert_buffer_output(scratch.data_char, scratch.data_u8, scratch.data_u16, scratch.data_u32, data, size, encoding);
+                               assert(result <= sizeof(scratch));
+
+                               // write data
+                               writer.write(scratch.data_u8, result);
+                       }
+               }
+
+               void write_direct(const char_t* data, size_t length)
+               {
+                       // flush the remaining buffer contents
+                       flush();
+
+                       // handle large chunks
+                       if (length > bufcapacity)
+                       {
+                               if (encoding == get_write_native_encoding())
+                               {
+                                       // fast path, can just write data chunk
+                                       writer.write(data, length * sizeof(char_t));
+                                       return;
+                               }
+
+                               // need to convert in suitable chunks
+                               while (length > bufcapacity)
+                               {
+                                       // get chunk size by selecting such number of characters that are guaranteed to fit into scratch buffer
+                                       // and form a complete codepoint sequence (i.e. discard start of last codepoint if necessary)
+                                       size_t chunk_size = get_valid_length(data, bufcapacity);
+                                       assert(chunk_size);
+
+                                       // convert chunk and write
+                                       flush(data, chunk_size);
+
+                                       // iterate
+                                       data += chunk_size;
+                                       length -= chunk_size;
+                               }
+
+                               // small tail is copied below
+                               bufsize = 0;
+                       }
+
+                       memcpy(buffer + bufsize, data, length * sizeof(char_t));
+                       bufsize += length;
+               }
+
+               void write_buffer(const char_t* data, size_t length)
+               {
+                       size_t offset = bufsize;
+
+                       if (offset + length <= bufcapacity)
+                       {
+                               memcpy(buffer + offset, data, length * sizeof(char_t));
+                               bufsize = offset + length;
+                       }
+                       else
+                       {
+                               write_direct(data, length);
+                       }
+               }
+
+               void write_string(const char_t* data)
+               {
+                       // write the part of the string that fits in the buffer
+                       size_t offset = bufsize;
+
+                       while (*data && offset < bufcapacity)
+                               buffer[offset++] = *data++;
+
+                       // write the rest
+                       if (offset < bufcapacity)
+                       {
+                               bufsize = offset;
+                       }
+                       else
+                       {
+                               // backtrack a bit if we have split the codepoint
+                               size_t length = offset - bufsize;
+                               size_t extra = length - get_valid_length(data - length, length);
+
+                               bufsize = offset - extra;
+
+                               write_direct(data - extra, strlength(data) + extra);
+                       }
+               }
+
+               void write(char_t d0)
+               {
+                       size_t offset = bufsize;
+                       if (offset > bufcapacity - 1) offset = flush();
+
+                       buffer[offset + 0] = d0;
+                       bufsize = offset + 1;
+               }
+
+               void write(char_t d0, char_t d1)
+               {
+                       size_t offset = bufsize;
+                       if (offset > bufcapacity - 2) offset = flush();
+
+                       buffer[offset + 0] = d0;
+                       buffer[offset + 1] = d1;
+                       bufsize = offset + 2;
+               }
+
+               void write(char_t d0, char_t d1, char_t d2)
+               {
+                       size_t offset = bufsize;
+                       if (offset > bufcapacity - 3) offset = flush();
+
+                       buffer[offset + 0] = d0;
+                       buffer[offset + 1] = d1;
+                       buffer[offset + 2] = d2;
+                       bufsize = offset + 3;
+               }
+
+               void write(char_t d0, char_t d1, char_t d2, char_t d3)
+               {
+                       size_t offset = bufsize;
+                       if (offset > bufcapacity - 4) offset = flush();
+
+                       buffer[offset + 0] = d0;
+                       buffer[offset + 1] = d1;
+                       buffer[offset + 2] = d2;
+                       buffer[offset + 3] = d3;
+                       bufsize = offset + 4;
+               }
+
+               void write(char_t d0, char_t d1, char_t d2, char_t d3, char_t d4)
+               {
+                       size_t offset = bufsize;
+                       if (offset > bufcapacity - 5) offset = flush();
+
+                       buffer[offset + 0] = d0;
+                       buffer[offset + 1] = d1;
+                       buffer[offset + 2] = d2;
+                       buffer[offset + 3] = d3;
+                       buffer[offset + 4] = d4;
+                       bufsize = offset + 5;
+               }
+
+               void write(char_t d0, char_t d1, char_t d2, char_t d3, char_t d4, char_t d5)
+               {
+                       size_t offset = bufsize;
+                       if (offset > bufcapacity - 6) offset = flush();
+
+                       buffer[offset + 0] = d0;
+                       buffer[offset + 1] = d1;
+                       buffer[offset + 2] = d2;
+                       buffer[offset + 3] = d3;
+                       buffer[offset + 4] = d4;
+                       buffer[offset + 5] = d5;
+                       bufsize = offset + 6;
+               }
+
+               // utf8 maximum expansion: x4 (-> utf32)
+               // utf16 maximum expansion: x2 (-> utf32)
+               // utf32 maximum expansion: x1
+               enum
+               {
+                       bufcapacitybytes =
+                       #ifdef PUGIXML_MEMORY_OUTPUT_STACK
+                               PUGIXML_MEMORY_OUTPUT_STACK
+                       #else
+                               10240
+                       #endif
+                       ,
+                       bufcapacity = bufcapacitybytes / (sizeof(char_t) + 4)
+               };
+
+               char_t buffer[bufcapacity];
+
+               union
+               {
+                       uint8_t data_u8[4 * bufcapacity];
+                       uint16_t data_u16[2 * bufcapacity];
+                       uint32_t data_u32[bufcapacity];
+                       char_t data_char[bufcapacity];
+               } scratch;
+
+               xml_writer& writer;
+               size_t bufsize;
+               xml_encoding encoding;
+       };
+
+       PUGI__FN void text_output_escaped(xml_buffered_writer& writer, const char_t* s, chartypex_t type)
+       {
+               while (*s)
+               {
+                       const char_t* prev = s;
+
+                       // While *s is a usual symbol
+                       PUGI__SCANWHILE_UNROLL(!PUGI__IS_CHARTYPEX(ss, type));
+
+                       writer.write_buffer(prev, static_cast<size_t>(s - prev));
+
+                       switch (*s)
+                       {
+                               case 0: break;
+                               case '&':
+                                       writer.write('&', 'a', 'm', 'p', ';');
+                                       ++s;
+                                       break;
+                               case '<':
+                                       writer.write('&', 'l', 't', ';');
+                                       ++s;
+                                       break;
+                               case '>':
+                                       writer.write('&', 'g', 't', ';');
+                                       ++s;
+                                       break;
+                               case '"':
+                                       writer.write('&', 'q', 'u', 'o', 't', ';');
+                                       ++s;
+                                       break;
+                               default: // s is not a usual symbol
+                               {
+                                       unsigned int ch = static_cast<unsigned int>(*s++);
+                                       assert(ch < 32);
+
+                                       writer.write('&', '#', static_cast<char_t>((ch / 10) + '0'), static_cast<char_t>((ch % 10) + '0'), ';');
+                               }
+                       }
+               }
+       }
+
+       PUGI__FN void text_output(xml_buffered_writer& writer, const char_t* s, chartypex_t type, unsigned int flags)
+       {
+               if (flags & format_no_escapes)
+                       writer.write_string(s);
+               else
+                       text_output_escaped(writer, s, type);
+       }
+
+       PUGI__FN void text_output_cdata(xml_buffered_writer& writer, const char_t* s)
+       {
+               do
+               {
+                       writer.write('<', '!', '[', 'C', 'D');
+                       writer.write('A', 'T', 'A', '[');
+
+                       const char_t* prev = s;
+
+                       // look for ]]> sequence - we can't output it as is since it terminates CDATA
+                       while (*s && !(s[0] == ']' && s[1] == ']' && s[2] == '>')) ++s;
+
+                       // skip ]] if we stopped at ]]>, > will go to the next CDATA section
+                       if (*s) s += 2;
+
+                       writer.write_buffer(prev, static_cast<size_t>(s - prev));
+
+                       writer.write(']', ']', '>');
+               }
+               while (*s);
+       }
+
+       PUGI__FN void text_output_indent(xml_buffered_writer& writer, const char_t* indent, size_t indent_length, unsigned int depth)
+       {
+               switch (indent_length)
+               {
+               case 1:
+               {
+                       for (unsigned int i = 0; i < depth; ++i)
+                               writer.write(indent[0]);
+                       break;
+               }
+
+               case 2:
+               {
+                       for (unsigned int i = 0; i < depth; ++i)
+                               writer.write(indent[0], indent[1]);
+                       break;
+               }
+
+               case 3:
+               {
+                       for (unsigned int i = 0; i < depth; ++i)
+                               writer.write(indent[0], indent[1], indent[2]);
+                       break;
+               }
+
+               case 4:
+               {
+                       for (unsigned int i = 0; i < depth; ++i)
+                               writer.write(indent[0], indent[1], indent[2], indent[3]);
+                       break;
+               }
+
+               default:
+               {
+                       for (unsigned int i = 0; i < depth; ++i)
+                               writer.write_buffer(indent, indent_length);
+               }
+               }
+       }
+
+       PUGI__FN void node_output_comment(xml_buffered_writer& writer, const char_t* s)
+       {
+               writer.write('<', '!', '-', '-');
+
+               while (*s)
+               {
+                       const char_t* prev = s;
+
+                       // look for -\0 or -- sequence - we can't output it since -- is illegal in comment body
+                       while (*s && !(s[0] == '-' && (s[1] == '-' || s[1] == 0))) ++s;
+
+                       writer.write_buffer(prev, static_cast<size_t>(s - prev));
+
+                       if (*s)
+                       {
+                               assert(*s == '-');
+
+                               writer.write('-', ' ');
+                               ++s;
+                       }
+               }
+
+               writer.write('-', '-', '>');
+       }
+
+       PUGI__FN void node_output_pi_value(xml_buffered_writer& writer, const char_t* s)
+       {
+               while (*s)
+               {
+                       const char_t* prev = s;
+
+                       // look for ?> sequence - we can't output it since ?> terminates PI
+                       while (*s && !(s[0] == '?' && s[1] == '>')) ++s;
+
+                       writer.write_buffer(prev, static_cast<size_t>(s - prev));
+
+                       if (*s)
+                       {
+                               assert(s[0] == '?' && s[1] == '>');
+
+                               writer.write('?', ' ', '>');
+                               s += 2;
+                       }
+               }
+       }
+
+       PUGI__FN void node_output_attributes(xml_buffered_writer& writer, xml_node_struct* node, const char_t* indent, size_t indent_length, unsigned int flags, unsigned int depth)
+       {
+               const char_t* default_name = PUGIXML_TEXT(":anonymous");
+
+               for (xml_attribute_struct* a = node->first_attribute; a; a = a->next_attribute)
+               {
+                       if ((flags & (format_indent_attributes | format_raw)) == format_indent_attributes)
+                       {
+                               writer.write('\n');
+
+                               text_output_indent(writer, indent, indent_length, depth + 1);
+                       }
+                       else
+                       {
+                               writer.write(' ');
+                       }
+
+                       writer.write_string(a->name ? a->name + 0 : default_name);
+                       writer.write('=', '"');
+
+                       if (a->value)
+                               text_output(writer, a->value, ctx_special_attr, flags);
+
+                       writer.write('"');
+               }
+       }
+
+       PUGI__FN bool node_output_start(xml_buffered_writer& writer, xml_node_struct* node, const char_t* indent, size_t indent_length, unsigned int flags, unsigned int depth)
+       {
+               const char_t* default_name = PUGIXML_TEXT(":anonymous");
+               const char_t* name = node->name ? node->name + 0 : default_name;
+
+               writer.write('<');
+               writer.write_string(name);
+
+               if (node->first_attribute)
+                       node_output_attributes(writer, node, indent, indent_length, flags, depth);
+
+               // element nodes can have value if parse_embed_pcdata was used
+               if (!node->value)
+               {
+                       if (!node->first_child)
+                       {
+                               if (flags & format_no_empty_element_tags)
+                               {
+                                       writer.write('>', '<', '/');
+                                       writer.write_string(name);
+                                       writer.write('>');
+
+                                       return false;
+                               }
+                               else
+                               {
+                                       if ((flags & format_raw) == 0)
+                                               writer.write(' ');
+
+                                       writer.write('/', '>');
+
+                                       return false;
+                               }
+                       }
+                       else
+                       {
+                               writer.write('>');
+
+                               return true;
+                       }
+               }
+               else
+               {
+                       writer.write('>');
+
+                       text_output(writer, node->value, ctx_special_pcdata, flags);
+
+                       if (!node->first_child)
+                       {
+                               writer.write('<', '/');
+                               writer.write_string(name);
+                               writer.write('>');
+
+                               return false;
+                       }
+                       else
+                       {
+                               return true;
+                       }
+               }
+       }
+
+       PUGI__FN void node_output_end(xml_buffered_writer& writer, xml_node_struct* node)
+       {
+               const char_t* default_name = PUGIXML_TEXT(":anonymous");
+               const char_t* name = node->name ? node->name + 0 : default_name;
+
+               writer.write('<', '/');
+               writer.write_string(name);
+               writer.write('>');
+       }
+
+       PUGI__FN void node_output_simple(xml_buffered_writer& writer, xml_node_struct* node, unsigned int flags)
+       {
+               const char_t* default_name = PUGIXML_TEXT(":anonymous");
+
+               switch (PUGI__NODETYPE(node))
+               {
+                       case node_pcdata:
+                               text_output(writer, node->value ? node->value + 0 : PUGIXML_TEXT(""), ctx_special_pcdata, flags);
+                               break;
+
+                       case node_cdata:
+                               text_output_cdata(writer, node->value ? node->value + 0 : PUGIXML_TEXT(""));
+                               break;
+
+                       case node_comment:
+                               node_output_comment(writer, node->value ? node->value + 0 : PUGIXML_TEXT(""));
+                               break;
+
+                       case node_pi:
+                               writer.write('<', '?');
+                               writer.write_string(node->name ? node->name + 0 : default_name);
+
+                               if (node->value)
+                               {
+                                       writer.write(' ');
+                                       node_output_pi_value(writer, node->value);
+                               }
+
+                               writer.write('?', '>');
+                               break;
+
+                       case node_declaration:
+                               writer.write('<', '?');
+                               writer.write_string(node->name ? node->name + 0 : default_name);
+                               node_output_attributes(writer, node, PUGIXML_TEXT(""), 0, flags | format_raw, 0);
+                               writer.write('?', '>');
+                               break;
+
+                       case node_doctype:
+                               writer.write('<', '!', 'D', 'O', 'C');
+                               writer.write('T', 'Y', 'P', 'E');
+
+                               if (node->value)
+                               {
+                                       writer.write(' ');
+                                       writer.write_string(node->value);
+                               }
+
+                               writer.write('>');
+                               break;
+
+                       default:
+                               assert(false && "Invalid node type"); // unreachable
+               }
+       }
+
+       enum indent_flags_t
+       {
+               indent_newline = 1,
+               indent_indent = 2
+       };
+
+       PUGI__FN void node_output(xml_buffered_writer& writer, xml_node_struct* root, const char_t* indent, unsigned int flags, unsigned int depth)
+       {
+               size_t indent_length = ((flags & (format_indent | format_indent_attributes)) && (flags & format_raw) == 0) ? strlength(indent) : 0;
+               unsigned int indent_flags = indent_indent;
+
+               xml_node_struct* node = root;
+
+               do
+               {
+                       assert(node);
+
+                       // begin writing current node
+                       if (PUGI__NODETYPE(node) == node_pcdata || PUGI__NODETYPE(node) == node_cdata)
+                       {
+                               node_output_simple(writer, node, flags);
+
+                               indent_flags = 0;
+                       }
+                       else
+                       {
+                               if ((indent_flags & indent_newline) && (flags & format_raw) == 0)
+                                       writer.write('\n');
+
+                               if ((indent_flags & indent_indent) && indent_length)
+                                       text_output_indent(writer, indent, indent_length, depth);
+
+                               if (PUGI__NODETYPE(node) == node_element)
+                               {
+                                       indent_flags = indent_newline | indent_indent;
+
+                                       if (node_output_start(writer, node, indent, indent_length, flags, depth))
+                                       {
+                                               // element nodes can have value if parse_embed_pcdata was used
+                                               if (node->value)
+                                                       indent_flags = 0;
+
+                                               node = node->first_child;
+                                               depth++;
+                                               continue;
+                                       }
+                               }
+                               else if (PUGI__NODETYPE(node) == node_document)
+                               {
+                                       indent_flags = indent_indent;
+
+                                       if (node->first_child)
+                                       {
+                                               node = node->first_child;
+                                               continue;
+                                       }
+                               }
+                               else
+                               {
+                                       node_output_simple(writer, node, flags);
+
+                                       indent_flags = indent_newline | indent_indent;
+                               }
+                       }
+
+                       // continue to the next node
+                       while (node != root)
+                       {
+                               if (node->next_sibling)
+                               {
+                                       node = node->next_sibling;
+                                       break;
+                               }
+
+                               node = node->parent;
+
+                               // write closing node
+                               if (PUGI__NODETYPE(node) == node_element)
+                               {
+                                       depth--;
+
+                                       if ((indent_flags & indent_newline) && (flags & format_raw) == 0)
+                                               writer.write('\n');
+
+                                       if ((indent_flags & indent_indent) && indent_length)
+                                               text_output_indent(writer, indent, indent_length, depth);
+
+                                       node_output_end(writer, node);
+
+                                       indent_flags = indent_newline | indent_indent;
+                               }
+                       }
+               }
+               while (node != root);
+
+               if ((indent_flags & indent_newline) && (flags & format_raw) == 0)
+                       writer.write('\n');
+       }
+
+       PUGI__FN bool has_declaration(xml_node_struct* node)
+       {
+               for (xml_node_struct* child = node->first_child; child; child = child->next_sibling)
+               {
+                       xml_node_type type = PUGI__NODETYPE(child);
+
+                       if (type == node_declaration) return true;
+                       if (type == node_element) return false;
+               }
+
+               return false;
+       }
+
+       PUGI__FN bool is_attribute_of(xml_attribute_struct* attr, xml_node_struct* node)
+       {
+               for (xml_attribute_struct* a = node->first_attribute; a; a = a->next_attribute)
+                       if (a == attr)
+                               return true;
+
+               return false;
+       }
+
+       PUGI__FN bool allow_insert_attribute(xml_node_type parent)
+       {
+               return parent == node_element || parent == node_declaration;
+       }
+
+       PUGI__FN bool allow_insert_child(xml_node_type parent, xml_node_type child)
+       {
+               if (parent != node_document && parent != node_element) return false;
+               if (child == node_document || child == node_null) return false;
+               if (parent != node_document && (child == node_declaration || child == node_doctype)) return false;
+
+               return true;
+       }
+
+       PUGI__FN bool allow_move(xml_node parent, xml_node child)
+       {
+               // check that child can be a child of parent
+               if (!allow_insert_child(parent.type(), child.type()))
+                       return false;
+
+               // check that node is not moved between documents
+               if (parent.root() != child.root())
+                       return false;
+
+               // check that new parent is not in the child subtree
+               xml_node cur = parent;
+
+               while (cur)
+               {
+                       if (cur == child)
+                               return false;
+
+                       cur = cur.parent();
+               }
+
+               return true;
+       }
+
+       template <typename String, typename Header>
+       PUGI__FN void node_copy_string(String& dest, Header& header, uintptr_t header_mask, char_t* source, Header& source_header, xml_allocator* alloc)
+       {
+               assert(!dest && (header & header_mask) == 0);
+
+               if (source)
+               {
+                       if (alloc && (source_header & header_mask) == 0)
+                       {
+                               dest = source;
+
+                               // since strcpy_insitu can reuse document buffer memory we need to mark both source and dest as shared
+                               header |= xml_memory_page_contents_shared_mask;
+                               source_header |= xml_memory_page_contents_shared_mask;
+                       }
+                       else
+                               strcpy_insitu(dest, header, header_mask, source, strlength(source));
+               }
+       }
+
+       PUGI__FN void node_copy_contents(xml_node_struct* dn, xml_node_struct* sn, xml_allocator* shared_alloc)
+       {
+               node_copy_string(dn->name, dn->header, xml_memory_page_name_allocated_mask, sn->name, sn->header, shared_alloc);
+               node_copy_string(dn->value, dn->header, xml_memory_page_value_allocated_mask, sn->value, sn->header, shared_alloc);
+
+               for (xml_attribute_struct* sa = sn->first_attribute; sa; sa = sa->next_attribute)
+               {
+                       xml_attribute_struct* da = append_new_attribute(dn, get_allocator(dn));
+
+                       if (da)
+                       {
+                               node_copy_string(da->name, da->header, xml_memory_page_name_allocated_mask, sa->name, sa->header, shared_alloc);
+                               node_copy_string(da->value, da->header, xml_memory_page_value_allocated_mask, sa->value, sa->header, shared_alloc);
+                       }
+               }
+       }
+
+       PUGI__FN void node_copy_tree(xml_node_struct* dn, xml_node_struct* sn)
+       {
+               xml_allocator& alloc = get_allocator(dn);
+               xml_allocator* shared_alloc = (&alloc == &get_allocator(sn)) ? &alloc : 0;
+
+               node_copy_contents(dn, sn, shared_alloc);
+
+               xml_node_struct* dit = dn;
+               xml_node_struct* sit = sn->first_child;
+
+               while (sit && sit != sn)
+               {
+                       // when a tree is copied into one of the descendants, we need to skip that subtree to avoid an infinite loop
+                       if (sit != dn)
+                       {
+                               xml_node_struct* copy = append_new_node(dit, alloc, PUGI__NODETYPE(sit));
+
+                               if (copy)
+                               {
+                                       node_copy_contents(copy, sit, shared_alloc);
+
+                                       if (sit->first_child)
+                                       {
+                                               dit = copy;
+                                               sit = sit->first_child;
+                                               continue;
+                                       }
+                               }
+                       }
+
+                       // continue to the next node
+                       do
+                       {
+                               if (sit->next_sibling)
+                               {
+                                       sit = sit->next_sibling;
+                                       break;
+                               }
+
+                               sit = sit->parent;
+                               dit = dit->parent;
+                       }
+                       while (sit != sn);
+               }
+       }
+
+       PUGI__FN void node_copy_attribute(xml_attribute_struct* da, xml_attribute_struct* sa)
+       {
+               xml_allocator& alloc = get_allocator(da);
+               xml_allocator* shared_alloc = (&alloc == &get_allocator(sa)) ? &alloc : 0;
+
+               node_copy_string(da->name, da->header, xml_memory_page_name_allocated_mask, sa->name, sa->header, shared_alloc);
+               node_copy_string(da->value, da->header, xml_memory_page_value_allocated_mask, sa->value, sa->header, shared_alloc);
+       }
+
+       inline bool is_text_node(xml_node_struct* node)
+       {
+               xml_node_type type = PUGI__NODETYPE(node);
+
+               return type == node_pcdata || type == node_cdata;
+       }
+
+       // get value with conversion functions
+       template <typename U> PUGI__FN PUGI__UNSIGNED_OVERFLOW U string_to_integer(const char_t* value, U minv, U maxv)
+       {
+               U result = 0;
+               const char_t* s = value;
+
+               while (PUGI__IS_CHARTYPE(*s, ct_space))
+                       s++;
+
+               bool negative = (*s == '-');
+
+               s += (*s == '+' || *s == '-');
+
+               bool overflow = false;
+
+               if (s[0] == '0' && (s[1] | ' ') == 'x')
+               {
+                       s += 2;
+
+                       // since overflow detection relies on length of the sequence skip leading zeros
+                       while (*s == '0')
+                               s++;
+
+                       const char_t* start = s;
+
+                       for (;;)
+                       {
+                               if (static_cast<unsigned>(*s - '0') < 10)
+                                       result = result * 16 + (*s - '0');
+                               else if (static_cast<unsigned>((*s | ' ') - 'a') < 6)
+                                       result = result * 16 + ((*s | ' ') - 'a' + 10);
+                               else
+                                       break;
+
+                               s++;
+                       }
+
+                       size_t digits = static_cast<size_t>(s - start);
+
+                       overflow = digits > sizeof(U) * 2;
+               }
+               else
+               {
+                       // since overflow detection relies on length of the sequence skip leading zeros
+                       while (*s == '0')
+                               s++;
+
+                       const char_t* start = s;
+
+                       for (;;)
+                       {
+                               if (static_cast<unsigned>(*s - '0') < 10)
+                                       result = result * 10 + (*s - '0');
+                               else
+                                       break;
+
+                               s++;
+                       }
+
+                       size_t digits = static_cast<size_t>(s - start);
+
+                       PUGI__STATIC_ASSERT(sizeof(U) == 8 || sizeof(U) == 4 || sizeof(U) == 2);
+
+                       const size_t max_digits10 = sizeof(U) == 8 ? 20 : sizeof(U) == 4 ? 10 : 5;
+                       const char_t max_lead = sizeof(U) == 8 ? '1' : sizeof(U) == 4 ? '4' : '6';
+                       const size_t high_bit = sizeof(U) * 8 - 1;
+
+                       overflow = digits >= max_digits10 && !(digits == max_digits10 && (*start < max_lead || (*start == max_lead && result >> high_bit)));
+               }
+
+               if (negative)
+               {
+                       // Workaround for crayc++ CC-3059: Expected no overflow in routine.
+               #ifdef _CRAYC
+                       return (overflow || result > ~minv + 1) ? minv : ~result + 1;
+               #else
+                       return (overflow || result > 0 - minv) ? minv : 0 - result;
+               #endif
+               }
+               else
+                       return (overflow || result > maxv) ? maxv : result;
+       }
+
+       PUGI__FN int get_value_int(const char_t* value)
+       {
+               return string_to_integer<unsigned int>(value, static_cast<unsigned int>(INT_MIN), INT_MAX);
+       }
+
+       PUGI__FN unsigned int get_value_uint(const char_t* value)
+       {
+               return string_to_integer<unsigned int>(value, 0, UINT_MAX);
+       }
+
+       PUGI__FN double get_value_double(const char_t* value)
+       {
+       #ifdef PUGIXML_WCHAR_MODE
+               return wcstod(value, 0);
+       #else
+               return strtod(value, 0);
+       #endif
+       }
+
+       PUGI__FN float get_value_float(const char_t* value)
+       {
+       #ifdef PUGIXML_WCHAR_MODE
+               return static_cast<float>(wcstod(value, 0));
+       #else
+               return static_cast<float>(strtod(value, 0));
+       #endif
+       }
+
+       PUGI__FN bool get_value_bool(const char_t* value)
+       {
+               // only look at first char
+               char_t first = *value;
+
+               // 1*, t* (true), T* (True), y* (yes), Y* (YES)
+               return (first == '1' || first == 't' || first == 'T' || first == 'y' || first == 'Y');
+       }
+
+#ifdef PUGIXML_HAS_LONG_LONG
+       PUGI__FN long long get_value_llong(const char_t* value)
+       {
+               return string_to_integer<unsigned long long>(value, static_cast<unsigned long long>(LLONG_MIN), LLONG_MAX);
+       }
+
+       PUGI__FN unsigned long long get_value_ullong(const char_t* value)
+       {
+               return string_to_integer<unsigned long long>(value, 0, ULLONG_MAX);
+       }
+#endif
+
+       template <typename U> PUGI__FN PUGI__UNSIGNED_OVERFLOW char_t* integer_to_string(char_t* begin, char_t* end, U value, bool negative)
+       {
+               char_t* result = end - 1;
+               U rest = negative ? 0 - value : value;
+
+               do
+               {
+                       *result-- = static_cast<char_t>('0' + (rest % 10));
+                       rest /= 10;
+               }
+               while (rest);
+
+               assert(result >= begin);
+               (void)begin;
+
+               *result = '-';
+
+               return result + !negative;
+       }
+
+       // set value with conversion functions
+       template <typename String, typename Header>
+       PUGI__FN bool set_value_ascii(String& dest, Header& header, uintptr_t header_mask, char* buf)
+       {
+       #ifdef PUGIXML_WCHAR_MODE
+               char_t wbuf[128];
+               assert(strlen(buf) < sizeof(wbuf) / sizeof(wbuf[0]));
+
+               size_t offset = 0;
+               for (; buf[offset]; ++offset) wbuf[offset] = buf[offset];
+
+               return strcpy_insitu(dest, header, header_mask, wbuf, offset);
+       #else
+               return strcpy_insitu(dest, header, header_mask, buf, strlen(buf));
+       #endif
+       }
+
+       template <typename U, typename String, typename Header>
+       PUGI__FN bool set_value_integer(String& dest, Header& header, uintptr_t header_mask, U value, bool negative)
+       {
+               char_t buf[64];
+               char_t* end = buf + sizeof(buf) / sizeof(buf[0]);
+               char_t* begin = integer_to_string(buf, end, value, negative);
+
+               return strcpy_insitu(dest, header, header_mask, begin, end - begin);
+       }
+
+       template <typename String, typename Header>
+       PUGI__FN bool set_value_convert(String& dest, Header& header, uintptr_t header_mask, float value)
+       {
+               char buf[128];
+               PUGI__SNPRINTF(buf, "%.9g", value);
+
+               return set_value_ascii(dest, header, header_mask, buf);
+       }
+
+       template <typename String, typename Header>
+       PUGI__FN bool set_value_convert(String& dest, Header& header, uintptr_t header_mask, double value)
+       {
+               char buf[128];
+               PUGI__SNPRINTF(buf, "%.17g", value);
+
+               return set_value_ascii(dest, header, header_mask, buf);
+       }
+
+       template <typename String, typename Header>
+       PUGI__FN bool set_value_bool(String& dest, Header& header, uintptr_t header_mask, bool value)
+       {
+               return strcpy_insitu(dest, header, header_mask, value ? PUGIXML_TEXT("true") : PUGIXML_TEXT("false"), value ? 4 : 5);
+       }
+
+       PUGI__FN xml_parse_result load_buffer_impl(xml_document_struct* doc, xml_node_struct* root, void* contents, size_t size, unsigned int options, xml_encoding encoding, bool is_mutable, bool own, char_t** out_buffer)
+       {
+               // check input buffer
+               if (!contents && size) return make_parse_result(status_io_error);
+
+               // get actual encoding
+               xml_encoding buffer_encoding = impl::get_buffer_encoding(encoding, contents, size);
+
+               // get private buffer
+               char_t* buffer = 0;
+               size_t length = 0;
+
+               if (!impl::convert_buffer(buffer, length, buffer_encoding, contents, size, is_mutable)) return impl::make_parse_result(status_out_of_memory);
+
+               // delete original buffer if we performed a conversion
+               if (own && buffer != contents && contents) impl::xml_memory::deallocate(contents);
+
+               // grab onto buffer if it's our buffer, user is responsible for deallocating contents himself
+               if (own || buffer != contents) *out_buffer = buffer;
+
+               // store buffer for offset_debug
+               doc->buffer = buffer;
+
+               // parse
+               xml_parse_result res = impl::xml_parser::parse(buffer, length, doc, root, options);
+
+               // remember encoding
+               res.encoding = buffer_encoding;
+
+               return res;
+       }
+
+       // we need to get length of entire file to load it in memory; the only (relatively) sane way to do it is via seek/tell trick
+       PUGI__FN xml_parse_status get_file_size(FILE* file, size_t& out_result)
+       {
+       #if defined(PUGI__MSVC_CRT_VERSION) && PUGI__MSVC_CRT_VERSION >= 1400 && !defined(_WIN32_WCE)
+               // there are 64-bit versions of fseek/ftell, let's use them
+               typedef __int64 length_type;
+
+               _fseeki64(file, 0, SEEK_END);
+               length_type length = _ftelli64(file);
+               _fseeki64(file, 0, SEEK_SET);
+       #elif defined(__MINGW32__) && !defined(__NO_MINGW_LFS) && (!defined(__STRICT_ANSI__) || defined(__MINGW64_VERSION_MAJOR))
+               // there are 64-bit versions of fseek/ftell, let's use them
+               typedef off64_t length_type;
+
+               fseeko64(file, 0, SEEK_END);
+               length_type length = ftello64(file);
+               fseeko64(file, 0, SEEK_SET);
+       #else
+               // if this is a 32-bit OS, long is enough; if this is a unix system, long is 64-bit, which is enough; otherwise we can't do anything anyway.
+               typedef long length_type;
+
+               fseek(file, 0, SEEK_END);
+               length_type length = ftell(file);
+               fseek(file, 0, SEEK_SET);
+       #endif
+
+               // check for I/O errors
+               if (length < 0) return status_io_error;
+
+               // check for overflow
+               size_t result = static_cast<size_t>(length);
+
+               if (static_cast<length_type>(result) != length) return status_out_of_memory;
+
+               // finalize
+               out_result = result;
+
+               return status_ok;
+       }
+
+       // This function assumes that buffer has extra sizeof(char_t) writable bytes after size
+       PUGI__FN size_t zero_terminate_buffer(void* buffer, size_t size, xml_encoding encoding)
+       {
+               // We only need to zero-terminate if encoding conversion does not do it for us
+       #ifdef PUGIXML_WCHAR_MODE
+               xml_encoding wchar_encoding = get_wchar_encoding();
+
+               if (encoding == wchar_encoding || need_endian_swap_utf(encoding, wchar_encoding))
+               {
+                       size_t length = size / sizeof(char_t);
+
+                       static_cast<char_t*>(buffer)[length] = 0;
+                       return (length + 1) * sizeof(char_t);
+               }
+       #else
+               if (encoding == encoding_utf8)
+               {
+                       static_cast<char*>(buffer)[size] = 0;
+                       return size + 1;
+               }
+       #endif
+
+               return size;
+       }
+
+       PUGI__FN xml_parse_result load_file_impl(xml_document_struct* doc, FILE* file, unsigned int options, xml_encoding encoding, char_t** out_buffer)
+       {
+               if (!file) return make_parse_result(status_file_not_found);
+
+               // get file size (can result in I/O errors)
+               size_t size = 0;
+               xml_parse_status size_status = get_file_size(file, size);
+               if (size_status != status_ok) return make_parse_result(size_status);
+
+               size_t max_suffix_size = sizeof(char_t);
+
+               // allocate buffer for the whole file
+               char* contents = static_cast<char*>(xml_memory::allocate(size + max_suffix_size));
+               if (!contents) return make_parse_result(status_out_of_memory);
+
+               // read file in memory
+               size_t read_size = fread(contents, 1, size, file);
+
+               if (read_size != size)
+               {
+                       xml_memory::deallocate(contents);
+                       return make_parse_result(status_io_error);
+               }
+
+               xml_encoding real_encoding = get_buffer_encoding(encoding, contents, size);
+
+               return load_buffer_impl(doc, doc, contents, zero_terminate_buffer(contents, size, real_encoding), options, real_encoding, true, true, out_buffer);
+       }
+
+       PUGI__FN void close_file(FILE* file)
+       {
+               fclose(file);
+       }
+
+#ifndef PUGIXML_NO_STL
+       template <typename T> struct xml_stream_chunk
+       {
+               static xml_stream_chunk* create()
+               {
+                       void* memory = xml_memory::allocate(sizeof(xml_stream_chunk));
+                       if (!memory) return 0;
+
+                       return new (memory) xml_stream_chunk();
+               }
+
+               static void destroy(xml_stream_chunk* chunk)
+               {
+                       // free chunk chain
+                       while (chunk)
+                       {
+                               xml_stream_chunk* next_ = chunk->next;
+
+                               xml_memory::deallocate(chunk);
+
+                               chunk = next_;
+                       }
+               }
+
+               xml_stream_chunk(): next(0), size(0)
+               {
+               }
+
+               xml_stream_chunk* next;
+               size_t size;
+
+               T data[xml_memory_page_size / sizeof(T)];
+       };
+
+       template <typename T> PUGI__FN xml_parse_status load_stream_data_noseek(std::basic_istream<T>& stream, void** out_buffer, size_t* out_size)
+       {
+               auto_deleter<xml_stream_chunk<T> > chunks(0, xml_stream_chunk<T>::destroy);
+
+               // read file to a chunk list
+               size_t total = 0;
+               xml_stream_chunk<T>* last = 0;
+
+               while (!stream.eof())
+               {
+                       // allocate new chunk
+                       xml_stream_chunk<T>* chunk = xml_stream_chunk<T>::create();
+                       if (!chunk) return status_out_of_memory;
+
+                       // append chunk to list
+                       if (last) last = last->next = chunk;
+                       else chunks.data = last = chunk;
+
+                       // read data to chunk
+                       stream.read(chunk->data, static_cast<std::streamsize>(sizeof(chunk->data) / sizeof(T)));
+                       chunk->size = static_cast<size_t>(stream.gcount()) * sizeof(T);
+
+                       // read may set failbit | eofbit in case gcount() is less than read length, so check for other I/O errors
+                       if (stream.bad() || (!stream.eof() && stream.fail())) return status_io_error;
+
+                       // guard against huge files (chunk size is small enough to make this overflow check work)
+                       if (total + chunk->size < total) return status_out_of_memory;
+                       total += chunk->size;
+               }
+
+               size_t max_suffix_size = sizeof(char_t);
+
+               // copy chunk list to a contiguous buffer
+               char* buffer = static_cast<char*>(xml_memory::allocate(total + max_suffix_size));
+               if (!buffer) return status_out_of_memory;
+
+               char* write = buffer;
+
+               for (xml_stream_chunk<T>* chunk = chunks.data; chunk; chunk = chunk->next)
+               {
+                       assert(write + chunk->size <= buffer + total);
+                       memcpy(write, chunk->data, chunk->size);
+                       write += chunk->size;
+               }
+
+               assert(write == buffer + total);
+
+               // return buffer
+               *out_buffer = buffer;
+               *out_size = total;
+
+               return status_ok;
+       }
+
+       template <typename T> PUGI__FN xml_parse_status load_stream_data_seek(std::basic_istream<T>& stream, void** out_buffer, size_t* out_size)
+       {
+               // get length of remaining data in stream
+               typename std::basic_istream<T>::pos_type pos = stream.tellg();
+               stream.seekg(0, std::ios::end);
+               std::streamoff length = stream.tellg() - pos;
+               stream.seekg(pos);
+
+               if (stream.fail() || pos < 0) return status_io_error;
+
+               // guard against huge files
+               size_t read_length = static_cast<size_t>(length);
+
+               if (static_cast<std::streamsize>(read_length) != length || length < 0) return status_out_of_memory;
+
+               size_t max_suffix_size = sizeof(char_t);
+
+               // read stream data into memory (guard against stream exceptions with buffer holder)
+               auto_deleter<void> buffer(xml_memory::allocate(read_length * sizeof(T) + max_suffix_size), xml_memory::deallocate);
+               if (!buffer.data) return status_out_of_memory;
+
+               stream.read(static_cast<T*>(buffer.data), static_cast<std::streamsize>(read_length));
+
+               // read may set failbit | eofbit in case gcount() is less than read_length (i.e. line ending conversion), so check for other I/O errors
+               if (stream.bad() || (!stream.eof() && stream.fail())) return status_io_error;
+
+               // return buffer
+               size_t actual_length = static_cast<size_t>(stream.gcount());
+               assert(actual_length <= read_length);
+
+               *out_buffer = buffer.release();
+               *out_size = actual_length * sizeof(T);
+
+               return status_ok;
+       }
+
+       template <typename T> PUGI__FN xml_parse_result load_stream_impl(xml_document_struct* doc, std::basic_istream<T>& stream, unsigned int options, xml_encoding encoding, char_t** out_buffer)
+       {
+               void* buffer = 0;
+               size_t size = 0;
+               xml_parse_status status = status_ok;
+
+               // if stream has an error bit set, bail out (otherwise tellg() can fail and we'll clear error bits)
+               if (stream.fail()) return make_parse_result(status_io_error);
+
+               // load stream to memory (using seek-based implementation if possible, since it's faster and takes less memory)
+               if (stream.tellg() < 0)
+               {
+                       stream.clear(); // clear error flags that could be set by a failing tellg
+                       status = load_stream_data_noseek(stream, &buffer, &size);
+               }
+               else
+                       status = load_stream_data_seek(stream, &buffer, &size);
+
+               if (status != status_ok) return make_parse_result(status);
+
+               xml_encoding real_encoding = get_buffer_encoding(encoding, buffer, size);
+
+               return load_buffer_impl(doc, doc, buffer, zero_terminate_buffer(buffer, size, real_encoding), options, real_encoding, true, true, out_buffer);
+       }
+#endif
+
+#if defined(PUGI__MSVC_CRT_VERSION) || defined(__BORLANDC__) || (defined(__MINGW32__) && (!defined(__STRICT_ANSI__) || defined(__MINGW64_VERSION_MAJOR)))
+       PUGI__FN FILE* open_file_wide(const wchar_t* path, const wchar_t* mode)
+       {
+               return _wfopen(path, mode);
+       }
+#else
+       PUGI__FN char* convert_path_heap(const wchar_t* str)
+       {
+               assert(str);
+
+               // first pass: get length in utf8 characters
+               size_t length = strlength_wide(str);
+               size_t size = as_utf8_begin(str, length);
+
+               // allocate resulting string
+               char* result = static_cast<char*>(xml_memory::allocate(size + 1));
+               if (!result) return 0;
+
+               // second pass: convert to utf8
+               as_utf8_end(result, size, str, length);
+
+               // zero-terminate
+               result[size] = 0;
+
+               return result;
+       }
+
+       PUGI__FN FILE* open_file_wide(const wchar_t* path, const wchar_t* mode)
+       {
+               // there is no standard function to open wide paths, so our best bet is to try utf8 path
+               char* path_utf8 = convert_path_heap(path);
+               if (!path_utf8) return 0;
+
+               // convert mode to ASCII (we mirror _wfopen interface)
+               char mode_ascii[4] = {0};
+               for (size_t i = 0; mode[i]; ++i) mode_ascii[i] = static_cast<char>(mode[i]);
+
+               // try to open the utf8 path
+               FILE* result = fopen(path_utf8, mode_ascii);
+
+               // free dummy buffer
+               xml_memory::deallocate(path_utf8);
+
+               return result;
+       }
+#endif
+
+       PUGI__FN bool save_file_impl(const xml_document& doc, FILE* file, const char_t* indent, unsigned int flags, xml_encoding encoding)
+       {
+               if (!file) return false;
+
+               xml_writer_file writer(file);
+               doc.save(writer, indent, flags, encoding);
+
+               return ferror(file) == 0;
+       }
+
+       struct name_null_sentry
+       {
+               xml_node_struct* node;
+               char_t* name;
+
+               name_null_sentry(xml_node_struct* node_): node(node_), name(node_->name)
+               {
+                       node->name = 0;
+               }
+
+               ~name_null_sentry()
+               {
+                       node->name = name;
+               }
+       };
+PUGI__NS_END
+
+namespace pugi
+{
+       PUGI__FN xml_writer_file::xml_writer_file(void* file_): file(file_)
+       {
+       }
+
+       PUGI__FN void xml_writer_file::write(const void* data, size_t size)
+       {
+               size_t result = fwrite(data, 1, size, static_cast<FILE*>(file));
+               (void)!result; // unfortunately we can't do proper error handling here
+       }
+
+#ifndef PUGIXML_NO_STL
+       PUGI__FN xml_writer_stream::xml_writer_stream(std::basic_ostream<char, std::char_traits<char> >& stream): narrow_stream(&stream), wide_stream(0)
+       {
+       }
+
+       PUGI__FN xml_writer_stream::xml_writer_stream(std::basic_ostream<wchar_t, std::char_traits<wchar_t> >& stream): narrow_stream(0), wide_stream(&stream)
+       {
+       }
+
+       PUGI__FN void xml_writer_stream::write(const void* data, size_t size)
+       {
+               if (narrow_stream)
+               {
+                       assert(!wide_stream);
+                       narrow_stream->write(reinterpret_cast<const char*>(data), static_cast<std::streamsize>(size));
+               }
+               else
+               {
+                       assert(wide_stream);
+                       assert(size % sizeof(wchar_t) == 0);
+
+                       wide_stream->write(reinterpret_cast<const wchar_t*>(data), static_cast<std::streamsize>(size / sizeof(wchar_t)));
+               }
+       }
+#endif
+
+       PUGI__FN xml_tree_walker::xml_tree_walker(): _depth(0)
+       {
+       }
+
+       PUGI__FN xml_tree_walker::~xml_tree_walker()
+       {
+       }
+
+       PUGI__FN int xml_tree_walker::depth() const
+       {
+               return _depth;
+       }
+
+       PUGI__FN bool xml_tree_walker::begin(xml_node&)
+       {
+               return true;
+       }
+
+       PUGI__FN bool xml_tree_walker::end(xml_node&)
+       {
+               return true;
+       }
+
+       PUGI__FN xml_attribute::xml_attribute(): _attr(0)
+       {
+       }
+
+       PUGI__FN xml_attribute::xml_attribute(xml_attribute_struct* attr): _attr(attr)
+       {
+       }
+
+       PUGI__FN static void unspecified_bool_xml_attribute(xml_attribute***)
+       {
+       }
+
+       PUGI__FN xml_attribute::operator xml_attribute::unspecified_bool_type() const
+       {
+               return _attr ? unspecified_bool_xml_attribute : 0;
+       }
+
+       PUGI__FN bool xml_attribute::operator!() const
+       {
+               return !_attr;
+       }
+
+       PUGI__FN bool xml_attribute::operator==(const xml_attribute& r) const
+       {
+               return (_attr == r._attr);
+       }
+
+       PUGI__FN bool xml_attribute::operator!=(const xml_attribute& r) const
+       {
+               return (_attr != r._attr);
+       }
+
+       PUGI__FN bool xml_attribute::operator<(const xml_attribute& r) const
+       {
+               return (_attr < r._attr);
+       }
+
+       PUGI__FN bool xml_attribute::operator>(const xml_attribute& r) const
+       {
+               return (_attr > r._attr);
+       }
+
+       PUGI__FN bool xml_attribute::operator<=(const xml_attribute& r) const
+       {
+               return (_attr <= r._attr);
+       }
+
+       PUGI__FN bool xml_attribute::operator>=(const xml_attribute& r) const
+       {
+               return (_attr >= r._attr);
+       }
+
+       PUGI__FN xml_attribute xml_attribute::next_attribute() const
+       {
+               return _attr ? xml_attribute(_attr->next_attribute) : xml_attribute();
+       }
+
+       PUGI__FN xml_attribute xml_attribute::previous_attribute() const
+       {
+               return _attr && _attr->prev_attribute_c->next_attribute ? xml_attribute(_attr->prev_attribute_c) : xml_attribute();
+       }
+
+       PUGI__FN const char_t* xml_attribute::as_string(const char_t* def) const
+       {
+               return (_attr && _attr->value) ? _attr->value + 0 : def;
+       }
+
+       PUGI__FN int xml_attribute::as_int(int def) const
+       {
+               return (_attr && _attr->value) ? impl::get_value_int(_attr->value) : def;
+       }
+
+       PUGI__FN unsigned int xml_attribute::as_uint(unsigned int def) const
+       {
+               return (_attr && _attr->value) ? impl::get_value_uint(_attr->value) : def;
+       }
+
+       PUGI__FN double xml_attribute::as_double(double def) const
+       {
+               return (_attr && _attr->value) ? impl::get_value_double(_attr->value) : def;
+       }
+
+       PUGI__FN float xml_attribute::as_float(float def) const
+       {
+               return (_attr && _attr->value) ? impl::get_value_float(_attr->value) : def;
+       }
+
+       PUGI__FN bool xml_attribute::as_bool(bool def) const
+       {
+               return (_attr && _attr->value) ? impl::get_value_bool(_attr->value) : def;
+       }
+
+#ifdef PUGIXML_HAS_LONG_LONG
+       PUGI__FN long long xml_attribute::as_llong(long long def) const
+       {
+               return (_attr && _attr->value) ? impl::get_value_llong(_attr->value) : def;
+       }
+
+       PUGI__FN unsigned long long xml_attribute::as_ullong(unsigned long long def) const
+       {
+               return (_attr && _attr->value) ? impl::get_value_ullong(_attr->value) : def;
+       }
+#endif
+
+       PUGI__FN bool xml_attribute::empty() const
+       {
+               return !_attr;
+       }
+
+       PUGI__FN const char_t* xml_attribute::name() const
+       {
+               return (_attr && _attr->name) ? _attr->name + 0 : PUGIXML_TEXT("");
+       }
+
+       PUGI__FN const char_t* xml_attribute::value() const
+       {
+               return (_attr && _attr->value) ? _attr->value + 0 : PUGIXML_TEXT("");
+       }
+
+       PUGI__FN size_t xml_attribute::hash_value() const
+       {
+               return static_cast<size_t>(reinterpret_cast<uintptr_t>(_attr) / sizeof(xml_attribute_struct));
+       }
+
+       PUGI__FN xml_attribute_struct* xml_attribute::internal_object() const
+       {
+               return _attr;
+       }
+
+       PUGI__FN xml_attribute& xml_attribute::operator=(const char_t* rhs)
+       {
+               set_value(rhs);
+               return *this;
+       }
+
+       PUGI__FN xml_attribute& xml_attribute::operator=(int rhs)
+       {
+               set_value(rhs);
+               return *this;
+       }
+
+       PUGI__FN xml_attribute& xml_attribute::operator=(unsigned int rhs)
+       {
+               set_value(rhs);
+               return *this;
+       }
+
+       PUGI__FN xml_attribute& xml_attribute::operator=(long rhs)
+       {
+               set_value(rhs);
+               return *this;
+       }
+
+       PUGI__FN xml_attribute& xml_attribute::operator=(unsigned long rhs)
+       {
+               set_value(rhs);
+               return *this;
+       }
+
+       PUGI__FN xml_attribute& xml_attribute::operator=(double rhs)
+       {
+               set_value(rhs);
+               return *this;
+       }
+
+       PUGI__FN xml_attribute& xml_attribute::operator=(float rhs)
+       {
+               set_value(rhs);
+               return *this;
+       }
+
+       PUGI__FN xml_attribute& xml_attribute::operator=(bool rhs)
+       {
+               set_value(rhs);
+               return *this;
+       }
+
+#ifdef PUGIXML_HAS_LONG_LONG
+       PUGI__FN xml_attribute& xml_attribute::operator=(long long rhs)
+       {
+               set_value(rhs);
+               return *this;
+       }
+
+       PUGI__FN xml_attribute& xml_attribute::operator=(unsigned long long rhs)
+       {
+               set_value(rhs);
+               return *this;
+       }
+#endif
+
+       PUGI__FN bool xml_attribute::set_name(const char_t* rhs)
+       {
+               if (!_attr) return false;
+
+               return impl::strcpy_insitu(_attr->name, _attr->header, impl::xml_memory_page_name_allocated_mask, rhs, impl::strlength(rhs));
+       }
+
+       PUGI__FN bool xml_attribute::set_value(const char_t* rhs)
+       {
+               if (!_attr) return false;
+
+               return impl::strcpy_insitu(_attr->value, _attr->header, impl::xml_memory_page_value_allocated_mask, rhs, impl::strlength(rhs));
+       }
+
+       PUGI__FN bool xml_attribute::set_value(int rhs)
+       {
+               if (!_attr) return false;
+
+               return impl::set_value_integer<unsigned int>(_attr->value, _attr->header, impl::xml_memory_page_value_allocated_mask, rhs, rhs < 0);
+       }
+
+       PUGI__FN bool xml_attribute::set_value(unsigned int rhs)
+       {
+               if (!_attr) return false;
+
+               return impl::set_value_integer<unsigned int>(_attr->value, _attr->header, impl::xml_memory_page_value_allocated_mask, rhs, false);
+       }
+
+       PUGI__FN bool xml_attribute::set_value(long rhs)
+       {
+               if (!_attr) return false;
+
+               return impl::set_value_integer<unsigned long>(_attr->value, _attr->header, impl::xml_memory_page_value_allocated_mask, rhs, rhs < 0);
+       }
+
+       PUGI__FN bool xml_attribute::set_value(unsigned long rhs)
+       {
+               if (!_attr) return false;
+
+               return impl::set_value_integer<unsigned long>(_attr->value, _attr->header, impl::xml_memory_page_value_allocated_mask, rhs, false);
+       }
+
+       PUGI__FN bool xml_attribute::set_value(double rhs)
+       {
+               if (!_attr) return false;
+
+               return impl::set_value_convert(_attr->value, _attr->header, impl::xml_memory_page_value_allocated_mask, rhs);
+       }
+
+       PUGI__FN bool xml_attribute::set_value(float rhs)
+       {
+               if (!_attr) return false;
+
+               return impl::set_value_convert(_attr->value, _attr->header, impl::xml_memory_page_value_allocated_mask, rhs);
+       }
+
+       PUGI__FN bool xml_attribute::set_value(bool rhs)
+       {
+               if (!_attr) return false;
+
+               return impl::set_value_bool(_attr->value, _attr->header, impl::xml_memory_page_value_allocated_mask, rhs);
+       }
+
+#ifdef PUGIXML_HAS_LONG_LONG
+       PUGI__FN bool xml_attribute::set_value(long long rhs)
+       {
+               if (!_attr) return false;
+
+               return impl::set_value_integer<unsigned long long>(_attr->value, _attr->header, impl::xml_memory_page_value_allocated_mask, rhs, rhs < 0);
+       }
+
+       PUGI__FN bool xml_attribute::set_value(unsigned long long rhs)
+       {
+               if (!_attr) return false;
+
+               return impl::set_value_integer<unsigned long long>(_attr->value, _attr->header, impl::xml_memory_page_value_allocated_mask, rhs, false);
+       }
+#endif
+
+#ifdef __BORLANDC__
+       PUGI__FN bool operator&&(const xml_attribute& lhs, bool rhs)
+       {
+               return (bool)lhs && rhs;
+       }
+
+       PUGI__FN bool operator||(const xml_attribute& lhs, bool rhs)
+       {
+               return (bool)lhs || rhs;
+       }
+#endif
+
+       PUGI__FN xml_node::xml_node(): _root(0)
+       {
+       }
+
+       PUGI__FN xml_node::xml_node(xml_node_struct* p): _root(p)
+       {
+       }
+
+       PUGI__FN static void unspecified_bool_xml_node(xml_node***)
+       {
+       }
+
+       PUGI__FN xml_node::operator xml_node::unspecified_bool_type() const
+       {
+               return _root ? unspecified_bool_xml_node : 0;
+       }
+
+       PUGI__FN bool xml_node::operator!() const
+       {
+               return !_root;
+       }
+
+       PUGI__FN xml_node::iterator xml_node::begin() const
+       {
+               return iterator(_root ? _root->first_child + 0 : 0, _root);
+       }
+
+       PUGI__FN xml_node::iterator xml_node::end() const
+       {
+               return iterator(0, _root);
+       }
+
+       PUGI__FN xml_node::attribute_iterator xml_node::attributes_begin() const
+       {
+               return attribute_iterator(_root ? _root->first_attribute + 0 : 0, _root);
+       }
+
+       PUGI__FN xml_node::attribute_iterator xml_node::attributes_end() const
+       {
+               return attribute_iterator(0, _root);
+       }
+
+       PUGI__FN xml_object_range<xml_node_iterator> xml_node::children() const
+       {
+               return xml_object_range<xml_node_iterator>(begin(), end());
+       }
+
+       PUGI__FN xml_object_range<xml_named_node_iterator> xml_node::children(const char_t* name_) const
+       {
+               return xml_object_range<xml_named_node_iterator>(xml_named_node_iterator(child(name_)._root, _root, name_), xml_named_node_iterator(0, _root, name_));
+       }
+
+       PUGI__FN xml_object_range<xml_attribute_iterator> xml_node::attributes() const
+       {
+               return xml_object_range<xml_attribute_iterator>(attributes_begin(), attributes_end());
+       }
+
+       PUGI__FN bool xml_node::operator==(const xml_node& r) const
+       {
+               return (_root == r._root);
+       }
+
+       PUGI__FN bool xml_node::operator!=(const xml_node& r) const
+       {
+               return (_root != r._root);
+       }
+
+       PUGI__FN bool xml_node::operator<(const xml_node& r) const
+       {
+               return (_root < r._root);
+       }
+
+       PUGI__FN bool xml_node::operator>(const xml_node& r) const
+       {
+               return (_root > r._root);
+       }
+
+       PUGI__FN bool xml_node::operator<=(const xml_node& r) const
+       {
+               return (_root <= r._root);
+       }
+
+       PUGI__FN bool xml_node::operator>=(const xml_node& r) const
+       {
+               return (_root >= r._root);
+       }
+
+       PUGI__FN bool xml_node::empty() const
+       {
+               return !_root;
+       }
+
+       PUGI__FN const char_t* xml_node::name() const
+       {
+               return (_root && _root->name) ? _root->name + 0 : PUGIXML_TEXT("");
+       }
+
+       PUGI__FN xml_node_type xml_node::type() const
+       {
+               return _root ? PUGI__NODETYPE(_root) : node_null;
+       }
+
+       PUGI__FN const char_t* xml_node::value() const
+       {
+               return (_root && _root->value) ? _root->value + 0 : PUGIXML_TEXT("");
+       }
+
+       PUGI__FN xml_node xml_node::child(const char_t* name_) const
+       {
+               if (!_root) return xml_node();
+
+               for (xml_node_struct* i = _root->first_child; i; i = i->next_sibling)
+                       if (i->name && impl::strequal(name_, i->name)) return xml_node(i);
+
+               return xml_node();
+       }
+
+       PUGI__FN xml_attribute xml_node::attribute(const char_t* name_) const
+       {
+               if (!_root) return xml_attribute();
+
+               for (xml_attribute_struct* i = _root->first_attribute; i; i = i->next_attribute)
+                       if (i->name && impl::strequal(name_, i->name))
+                               return xml_attribute(i);
+
+               return xml_attribute();
+       }
+
+       PUGI__FN xml_node xml_node::next_sibling(const char_t* name_) const
+       {
+               if (!_root) return xml_node();
+
+               for (xml_node_struct* i = _root->next_sibling; i; i = i->next_sibling)
+                       if (i->name && impl::strequal(name_, i->name)) return xml_node(i);
+
+               return xml_node();
+       }
+
+       PUGI__FN xml_node xml_node::next_sibling() const
+       {
+               return _root ? xml_node(_root->next_sibling) : xml_node();
+       }
+
+       PUGI__FN xml_node xml_node::previous_sibling(const char_t* name_) const
+       {
+               if (!_root) return xml_node();
+
+               for (xml_node_struct* i = _root->prev_sibling_c; i->next_sibling; i = i->prev_sibling_c)
+                       if (i->name && impl::strequal(name_, i->name)) return xml_node(i);
+
+               return xml_node();
+       }
+
+       PUGI__FN xml_attribute xml_node::attribute(const char_t* name_, xml_attribute& hint_) const
+       {
+               xml_attribute_struct* hint = hint_._attr;
+
+               // if hint is not an attribute of node, behavior is not defined
+               assert(!hint || (_root && impl::is_attribute_of(hint, _root)));
+
+               if (!_root) return xml_attribute();
+
+               // optimistically search from hint up until the end
+               for (xml_attribute_struct* i = hint; i; i = i->next_attribute)
+                       if (i->name && impl::strequal(name_, i->name))
+                       {
+                               // update hint to maximize efficiency of searching for consecutive attributes
+                               hint_._attr = i->next_attribute;
+
+                               return xml_attribute(i);
+                       }
+
+               // wrap around and search from the first attribute until the hint
+               // 'j' null pointer check is technically redundant, but it prevents a crash in case the assertion above fails
+               for (xml_attribute_struct* j = _root->first_attribute; j && j != hint; j = j->next_attribute)
+                       if (j->name && impl::strequal(name_, j->name))
+                       {
+                               // update hint to maximize efficiency of searching for consecutive attributes
+                               hint_._attr = j->next_attribute;
+
+                               return xml_attribute(j);
+                       }
+
+               return xml_attribute();
+       }
+
+       PUGI__FN xml_node xml_node::previous_sibling() const
+       {
+               if (!_root) return xml_node();
+
+               if (_root->prev_sibling_c->next_sibling) return xml_node(_root->prev_sibling_c);
+               else return xml_node();
+       }
+
+       PUGI__FN xml_node xml_node::parent() const
+       {
+               return _root ? xml_node(_root->parent) : xml_node();
+       }
+
+       PUGI__FN xml_node xml_node::root() const
+       {
+               return _root ? xml_node(&impl::get_document(_root)) : xml_node();
+       }
+
+       PUGI__FN xml_text xml_node::text() const
+       {
+               return xml_text(_root);
+       }
+
+       PUGI__FN const char_t* xml_node::child_value() const
+       {
+               if (!_root) return PUGIXML_TEXT("");
+
+               // element nodes can have value if parse_embed_pcdata was used
+               if (PUGI__NODETYPE(_root) == node_element && _root->value)
+                       return _root->value;
+
+               for (xml_node_struct* i = _root->first_child; i; i = i->next_sibling)
+                       if (impl::is_text_node(i) && i->value)
+                               return i->value;
+
+               return PUGIXML_TEXT("");
+       }
+
+       PUGI__FN const char_t* xml_node::child_value(const char_t* name_) const
+       {
+               return child(name_).child_value();
+       }
+
+       PUGI__FN xml_attribute xml_node::first_attribute() const
+       {
+               return _root ? xml_attribute(_root->first_attribute) : xml_attribute();
+       }
+
+       PUGI__FN xml_attribute xml_node::last_attribute() const
+       {
+               return _root && _root->first_attribute ? xml_attribute(_root->first_attribute->prev_attribute_c) : xml_attribute();
+       }
+
+       PUGI__FN xml_node xml_node::first_child() const
+       {
+               return _root ? xml_node(_root->first_child) : xml_node();
+       }
+
+       PUGI__FN xml_node xml_node::last_child() const
+       {
+               return _root && _root->first_child ? xml_node(_root->first_child->prev_sibling_c) : xml_node();
+       }
+
+       PUGI__FN bool xml_node::set_name(const char_t* rhs)
+       {
+               xml_node_type type_ = _root ? PUGI__NODETYPE(_root) : node_null;
+
+               if (type_ != node_element && type_ != node_pi && type_ != node_declaration)
+                       return false;
+
+               return impl::strcpy_insitu(_root->name, _root->header, impl::xml_memory_page_name_allocated_mask, rhs, impl::strlength(rhs));
+       }
+
+       PUGI__FN bool xml_node::set_value(const char_t* rhs)
+       {
+               xml_node_type type_ = _root ? PUGI__NODETYPE(_root) : node_null;
+
+               if (type_ != node_pcdata && type_ != node_cdata && type_ != node_comment && type_ != node_pi && type_ != node_doctype)
+                       return false;
+
+               return impl::strcpy_insitu(_root->value, _root->header, impl::xml_memory_page_value_allocated_mask, rhs, impl::strlength(rhs));
+       }
+
+       PUGI__FN xml_attribute xml_node::append_attribute(const char_t* name_)
+       {
+               if (!impl::allow_insert_attribute(type())) return xml_attribute();
+
+               impl::xml_allocator& alloc = impl::get_allocator(_root);
+               if (!alloc.reserve()) return xml_attribute();
+
+               xml_attribute a(impl::allocate_attribute(alloc));
+               if (!a) return xml_attribute();
+
+               impl::append_attribute(a._attr, _root);
+
+               a.set_name(name_);
+
+               return a;
+       }
+
+       PUGI__FN xml_attribute xml_node::prepend_attribute(const char_t* name_)
+       {
+               if (!impl::allow_insert_attribute(type())) return xml_attribute();
+
+               impl::xml_allocator& alloc = impl::get_allocator(_root);
+               if (!alloc.reserve()) return xml_attribute();
+
+               xml_attribute a(impl::allocate_attribute(alloc));
+               if (!a) return xml_attribute();
+
+               impl::prepend_attribute(a._attr, _root);
+
+               a.set_name(name_);
+
+               return a;
+       }
+
+       PUGI__FN xml_attribute xml_node::insert_attribute_after(const char_t* name_, const xml_attribute& attr)
+       {
+               if (!impl::allow_insert_attribute(type())) return xml_attribute();
+               if (!attr || !impl::is_attribute_of(attr._attr, _root)) return xml_attribute();
+
+               impl::xml_allocator& alloc = impl::get_allocator(_root);
+               if (!alloc.reserve()) return xml_attribute();
+
+               xml_attribute a(impl::allocate_attribute(alloc));
+               if (!a) return xml_attribute();
+
+               impl::insert_attribute_after(a._attr, attr._attr, _root);
+
+               a.set_name(name_);
+
+               return a;
+       }
+
+       PUGI__FN xml_attribute xml_node::insert_attribute_before(const char_t* name_, const xml_attribute& attr)
+       {
+               if (!impl::allow_insert_attribute(type())) return xml_attribute();
+               if (!attr || !impl::is_attribute_of(attr._attr, _root)) return xml_attribute();
+
+               impl::xml_allocator& alloc = impl::get_allocator(_root);
+               if (!alloc.reserve()) return xml_attribute();
+
+               xml_attribute a(impl::allocate_attribute(alloc));
+               if (!a) return xml_attribute();
+
+               impl::insert_attribute_before(a._attr, attr._attr, _root);
+
+               a.set_name(name_);
+
+               return a;
+       }
+
+       PUGI__FN xml_attribute xml_node::append_copy(const xml_attribute& proto)
+       {
+               if (!proto) return xml_attribute();
+               if (!impl::allow_insert_attribute(type())) return xml_attribute();
+
+               impl::xml_allocator& alloc = impl::get_allocator(_root);
+               if (!alloc.reserve()) return xml_attribute();
+
+               xml_attribute a(impl::allocate_attribute(alloc));
+               if (!a) return xml_attribute();
+
+               impl::append_attribute(a._attr, _root);
+               impl::node_copy_attribute(a._attr, proto._attr);
+
+               return a;
+       }
+
+       PUGI__FN xml_attribute xml_node::prepend_copy(const xml_attribute& proto)
+       {
+               if (!proto) return xml_attribute();
+               if (!impl::allow_insert_attribute(type())) return xml_attribute();
+
+               impl::xml_allocator& alloc = impl::get_allocator(_root);
+               if (!alloc.reserve()) return xml_attribute();
+
+               xml_attribute a(impl::allocate_attribute(alloc));
+               if (!a) return xml_attribute();
+
+               impl::prepend_attribute(a._attr, _root);
+               impl::node_copy_attribute(a._attr, proto._attr);
+
+               return a;
+       }
+
+       PUGI__FN xml_attribute xml_node::insert_copy_after(const xml_attribute& proto, const xml_attribute& attr)
+       {
+               if (!proto) return xml_attribute();
+               if (!impl::allow_insert_attribute(type())) return xml_attribute();
+               if (!attr || !impl::is_attribute_of(attr._attr, _root)) return xml_attribute();
+
+               impl::xml_allocator& alloc = impl::get_allocator(_root);
+               if (!alloc.reserve()) return xml_attribute();
+
+               xml_attribute a(impl::allocate_attribute(alloc));
+               if (!a) return xml_attribute();
+
+               impl::insert_attribute_after(a._attr, attr._attr, _root);
+               impl::node_copy_attribute(a._attr, proto._attr);
+
+               return a;
+       }
+
+       PUGI__FN xml_attribute xml_node::insert_copy_before(const xml_attribute& proto, const xml_attribute& attr)
+       {
+               if (!proto) return xml_attribute();
+               if (!impl::allow_insert_attribute(type())) return xml_attribute();
+               if (!attr || !impl::is_attribute_of(attr._attr, _root)) return xml_attribute();
+
+               impl::xml_allocator& alloc = impl::get_allocator(_root);
+               if (!alloc.reserve()) return xml_attribute();
+
+               xml_attribute a(impl::allocate_attribute(alloc));
+               if (!a) return xml_attribute();
+
+               impl::insert_attribute_before(a._attr, attr._attr, _root);
+               impl::node_copy_attribute(a._attr, proto._attr);
+
+               return a;
+       }
+
+       PUGI__FN xml_node xml_node::append_child(xml_node_type type_)
+       {
+               if (!impl::allow_insert_child(type(), type_)) return xml_node();
+
+               impl::xml_allocator& alloc = impl::get_allocator(_root);
+               if (!alloc.reserve()) return xml_node();
+
+               xml_node n(impl::allocate_node(alloc, type_));
+               if (!n) return xml_node();
+
+               impl::append_node(n._root, _root);
+
+               if (type_ == node_declaration) n.set_name(PUGIXML_TEXT("xml"));
+
+               return n;
+       }
+
+       PUGI__FN xml_node xml_node::prepend_child(xml_node_type type_)
+       {
+               if (!impl::allow_insert_child(type(), type_)) return xml_node();
+
+               impl::xml_allocator& alloc = impl::get_allocator(_root);
+               if (!alloc.reserve()) return xml_node();
+
+               xml_node n(impl::allocate_node(alloc, type_));
+               if (!n) return xml_node();
+
+               impl::prepend_node(n._root, _root);
+
+               if (type_ == node_declaration) n.set_name(PUGIXML_TEXT("xml"));
+
+               return n;
+       }
+
+       PUGI__FN xml_node xml_node::insert_child_before(xml_node_type type_, const xml_node& node)
+       {
+               if (!impl::allow_insert_child(type(), type_)) return xml_node();
+               if (!node._root || node._root->parent != _root) return xml_node();
+
+               impl::xml_allocator& alloc = impl::get_allocator(_root);
+               if (!alloc.reserve()) return xml_node();
+
+               xml_node n(impl::allocate_node(alloc, type_));
+               if (!n) return xml_node();
+
+               impl::insert_node_before(n._root, node._root);
+
+               if (type_ == node_declaration) n.set_name(PUGIXML_TEXT("xml"));
+
+               return n;
+       }
+
+       PUGI__FN xml_node xml_node::insert_child_after(xml_node_type type_, const xml_node& node)
+       {
+               if (!impl::allow_insert_child(type(), type_)) return xml_node();
+               if (!node._root || node._root->parent != _root) return xml_node();
+
+               impl::xml_allocator& alloc = impl::get_allocator(_root);
+               if (!alloc.reserve()) return xml_node();
+
+               xml_node n(impl::allocate_node(alloc, type_));
+               if (!n) return xml_node();
+
+               impl::insert_node_after(n._root, node._root);
+
+               if (type_ == node_declaration) n.set_name(PUGIXML_TEXT("xml"));
+
+               return n;
+       }
+
+       PUGI__FN xml_node xml_node::append_child(const char_t* name_)
+       {
+               xml_node result = append_child(node_element);
+
+               result.set_name(name_);
+
+               return result;
+       }
+
+       PUGI__FN xml_node xml_node::prepend_child(const char_t* name_)
+       {
+               xml_node result = prepend_child(node_element);
+
+               result.set_name(name_);
+
+               return result;
+       }
+
+       PUGI__FN xml_node xml_node::insert_child_after(const char_t* name_, const xml_node& node)
+       {
+               xml_node result = insert_child_after(node_element, node);
+
+               result.set_name(name_);
+
+               return result;
+       }
+
+       PUGI__FN xml_node xml_node::insert_child_before(const char_t* name_, const xml_node& node)
+       {
+               xml_node result = insert_child_before(node_element, node);
+
+               result.set_name(name_);
+
+               return result;
+       }
+
+       PUGI__FN xml_node xml_node::append_copy(const xml_node& proto)
+       {
+               xml_node_type type_ = proto.type();
+               if (!impl::allow_insert_child(type(), type_)) return xml_node();
+
+               impl::xml_allocator& alloc = impl::get_allocator(_root);
+               if (!alloc.reserve()) return xml_node();
+
+               xml_node n(impl::allocate_node(alloc, type_));
+               if (!n) return xml_node();
+
+               impl::append_node(n._root, _root);
+               impl::node_copy_tree(n._root, proto._root);
+
+               return n;
+       }
+
+       PUGI__FN xml_node xml_node::prepend_copy(const xml_node& proto)
+       {
+               xml_node_type type_ = proto.type();
+               if (!impl::allow_insert_child(type(), type_)) return xml_node();
+
+               impl::xml_allocator& alloc = impl::get_allocator(_root);
+               if (!alloc.reserve()) return xml_node();
+
+               xml_node n(impl::allocate_node(alloc, type_));
+               if (!n) return xml_node();
+
+               impl::prepend_node(n._root, _root);
+               impl::node_copy_tree(n._root, proto._root);
+
+               return n;
+       }
+
+       PUGI__FN xml_node xml_node::insert_copy_after(const xml_node& proto, const xml_node& node)
+       {
+               xml_node_type type_ = proto.type();
+               if (!impl::allow_insert_child(type(), type_)) return xml_node();
+               if (!node._root || node._root->parent != _root) return xml_node();
+
+               impl::xml_allocator& alloc = impl::get_allocator(_root);
+               if (!alloc.reserve()) return xml_node();
+
+               xml_node n(impl::allocate_node(alloc, type_));
+               if (!n) return xml_node();
+
+               impl::insert_node_after(n._root, node._root);
+               impl::node_copy_tree(n._root, proto._root);
+
+               return n;
+       }
+
+       PUGI__FN xml_node xml_node::insert_copy_before(const xml_node& proto, const xml_node& node)
+       {
+               xml_node_type type_ = proto.type();
+               if (!impl::allow_insert_child(type(), type_)) return xml_node();
+               if (!node._root || node._root->parent != _root) return xml_node();
+
+               impl::xml_allocator& alloc = impl::get_allocator(_root);
+               if (!alloc.reserve()) return xml_node();
+
+               xml_node n(impl::allocate_node(alloc, type_));
+               if (!n) return xml_node();
+
+               impl::insert_node_before(n._root, node._root);
+               impl::node_copy_tree(n._root, proto._root);
+
+               return n;
+       }
+
+       PUGI__FN xml_node xml_node::append_move(const xml_node& moved)
+       {
+               if (!impl::allow_move(*this, moved)) return xml_node();
+
+               impl::xml_allocator& alloc = impl::get_allocator(_root);
+               if (!alloc.reserve()) return xml_node();
+
+               // disable document_buffer_order optimization since moving nodes around changes document order without changing buffer pointers
+               impl::get_document(_root).header |= impl::xml_memory_page_contents_shared_mask;
+
+               impl::remove_node(moved._root);
+               impl::append_node(moved._root, _root);
+
+               return moved;
+       }
+
+       PUGI__FN xml_node xml_node::prepend_move(const xml_node& moved)
+       {
+               if (!impl::allow_move(*this, moved)) return xml_node();
+
+               impl::xml_allocator& alloc = impl::get_allocator(_root);
+               if (!alloc.reserve()) return xml_node();
+
+               // disable document_buffer_order optimization since moving nodes around changes document order without changing buffer pointers
+               impl::get_document(_root).header |= impl::xml_memory_page_contents_shared_mask;
+
+               impl::remove_node(moved._root);
+               impl::prepend_node(moved._root, _root);
+
+               return moved;
+       }
+
+       PUGI__FN xml_node xml_node::insert_move_after(const xml_node& moved, const xml_node& node)
+       {
+               if (!impl::allow_move(*this, moved)) return xml_node();
+               if (!node._root || node._root->parent != _root) return xml_node();
+               if (moved._root == node._root) return xml_node();
+
+               impl::xml_allocator& alloc = impl::get_allocator(_root);
+               if (!alloc.reserve()) return xml_node();
+
+               // disable document_buffer_order optimization since moving nodes around changes document order without changing buffer pointers
+               impl::get_document(_root).header |= impl::xml_memory_page_contents_shared_mask;
+
+               impl::remove_node(moved._root);
+               impl::insert_node_after(moved._root, node._root);
+
+               return moved;
+       }
+
+       PUGI__FN xml_node xml_node::insert_move_before(const xml_node& moved, const xml_node& node)
+       {
+               if (!impl::allow_move(*this, moved)) return xml_node();
+               if (!node._root || node._root->parent != _root) return xml_node();
+               if (moved._root == node._root) return xml_node();
+
+               impl::xml_allocator& alloc = impl::get_allocator(_root);
+               if (!alloc.reserve()) return xml_node();
+
+               // disable document_buffer_order optimization since moving nodes around changes document order without changing buffer pointers
+               impl::get_document(_root).header |= impl::xml_memory_page_contents_shared_mask;
+
+               impl::remove_node(moved._root);
+               impl::insert_node_before(moved._root, node._root);
+
+               return moved;
+       }
+
+       PUGI__FN bool xml_node::remove_attribute(const char_t* name_)
+       {
+               return remove_attribute(attribute(name_));
+       }
+
+       PUGI__FN bool xml_node::remove_attribute(const xml_attribute& a)
+       {
+               if (!_root || !a._attr) return false;
+               if (!impl::is_attribute_of(a._attr, _root)) return false;
+
+               impl::xml_allocator& alloc = impl::get_allocator(_root);
+               if (!alloc.reserve()) return false;
+
+               impl::remove_attribute(a._attr, _root);
+               impl::destroy_attribute(a._attr, alloc);
+
+               return true;
+       }
+
+       PUGI__FN bool xml_node::remove_child(const char_t* name_)
+       {
+               return remove_child(child(name_));
+       }
+
+       PUGI__FN bool xml_node::remove_child(const xml_node& n)
+       {
+               if (!_root || !n._root || n._root->parent != _root) return false;
+
+               impl::xml_allocator& alloc = impl::get_allocator(_root);
+               if (!alloc.reserve()) return false;
+
+               impl::remove_node(n._root);
+               impl::destroy_node(n._root, alloc);
+
+               return true;
+       }
+
+       PUGI__FN xml_parse_result xml_node::append_buffer(const void* contents, size_t size, unsigned int options, xml_encoding encoding)
+       {
+               // append_buffer is only valid for elements/documents
+               if (!impl::allow_insert_child(type(), node_element)) return impl::make_parse_result(status_append_invalid_root);
+
+               // get document node
+               impl::xml_document_struct* doc = &impl::get_document(_root);
+
+               // disable document_buffer_order optimization since in a document with multiple buffers comparing buffer pointers does not make sense
+               doc->header |= impl::xml_memory_page_contents_shared_mask;
+
+               // get extra buffer element (we'll store the document fragment buffer there so that we can deallocate it later)
+               impl::xml_memory_page* page = 0;
+               impl::xml_extra_buffer* extra = static_cast<impl::xml_extra_buffer*>(doc->allocate_memory(sizeof(impl::xml_extra_buffer) + sizeof(void*), page));
+               (void)page;
+
+               if (!extra) return impl::make_parse_result(status_out_of_memory);
+
+       #ifdef PUGIXML_COMPACT
+               // align the memory block to a pointer boundary; this is required for compact mode where memory allocations are only 4b aligned
+               // note that this requires up to sizeof(void*)-1 additional memory, which the allocation above takes into account
+               extra = reinterpret_cast<impl::xml_extra_buffer*>((reinterpret_cast<uintptr_t>(extra) + (sizeof(void*) - 1)) & ~(sizeof(void*) - 1));
+       #endif
+
+               // add extra buffer to the list
+               extra->buffer = 0;
+               extra->next = doc->extra_buffers;
+               doc->extra_buffers = extra;
+
+               // name of the root has to be NULL before parsing - otherwise closing node mismatches will not be detected at the top level
+               impl::name_null_sentry sentry(_root);
+
+               return impl::load_buffer_impl(doc, _root, const_cast<void*>(contents), size, options, encoding, false, false, &extra->buffer);
+       }
+
+       PUGI__FN xml_node xml_node::find_child_by_attribute(const char_t* name_, const char_t* attr_name, const char_t* attr_value) const
+       {
+               if (!_root) return xml_node();
+
+               for (xml_node_struct* i = _root->first_child; i; i = i->next_sibling)
+                       if (i->name && impl::strequal(name_, i->name))
+                       {
+                               for (xml_attribute_struct* a = i->first_attribute; a; a = a->next_attribute)
+                                       if (a->name && impl::strequal(attr_name, a->name) && impl::strequal(attr_value, a->value ? a->value + 0 : PUGIXML_TEXT("")))
+                                               return xml_node(i);
+                       }
+
+               return xml_node();
+       }
+
+       PUGI__FN xml_node xml_node::find_child_by_attribute(const char_t* attr_name, const char_t* attr_value) const
+       {
+               if (!_root) return xml_node();
+
+               for (xml_node_struct* i = _root->first_child; i; i = i->next_sibling)
+                       for (xml_attribute_struct* a = i->first_attribute; a; a = a->next_attribute)
+                               if (a->name && impl::strequal(attr_name, a->name) && impl::strequal(attr_value, a->value ? a->value + 0 : PUGIXML_TEXT("")))
+                                       return xml_node(i);
+
+               return xml_node();
+       }
+
+#ifndef PUGIXML_NO_STL
+       PUGI__FN string_t xml_node::path(char_t delimiter) const
+       {
+               if (!_root) return string_t();
+
+               size_t offset = 0;
+
+               for (xml_node_struct* i = _root; i; i = i->parent)
+               {
+                       offset += (i != _root);
+                       offset += i->name ? impl::strlength(i->name) : 0;
+               }
+
+               string_t result;
+               result.resize(offset);
+
+               for (xml_node_struct* j = _root; j; j = j->parent)
+               {
+                       if (j != _root)
+                               result[--offset] = delimiter;
+
+                       if (j->name)
+                       {
+                               size_t length = impl::strlength(j->name);
+
+                               offset -= length;
+                               memcpy(&result[offset], j->name, length * sizeof(char_t));
+                       }
+               }
+
+               assert(offset == 0);
+
+               return result;
+       }
+#endif
+
+       PUGI__FN xml_node xml_node::first_element_by_path(const char_t* path_, char_t delimiter) const
+       {
+               xml_node found = *this; // Current search context.
+
+               if (!_root || !path_[0]) return found;
+
+               if (path_[0] == delimiter)
+               {
+                       // Absolute path; e.g. '/foo/bar'
+                       found = found.root();
+                       ++path_;
+               }
+
+               const char_t* path_segment = path_;
+
+               while (*path_segment == delimiter) ++path_segment;
+
+               const char_t* path_segment_end = path_segment;
+
+               while (*path_segment_end && *path_segment_end != delimiter) ++path_segment_end;
+
+               if (path_segment == path_segment_end) return found;
+
+               const char_t* next_segment = path_segment_end;
+
+               while (*next_segment == delimiter) ++next_segment;
+
+               if (*path_segment == '.' && path_segment + 1 == path_segment_end)
+                       return found.first_element_by_path(next_segment, delimiter);
+               else if (*path_segment == '.' && *(path_segment+1) == '.' && path_segment + 2 == path_segment_end)
+                       return found.parent().first_element_by_path(next_segment, delimiter);
+               else
+               {
+                       for (xml_node_struct* j = found._root->first_child; j; j = j->next_sibling)
+                       {
+                               if (j->name && impl::strequalrange(j->name, path_segment, static_cast<size_t>(path_segment_end - path_segment)))
+                               {
+                                       xml_node subsearch = xml_node(j).first_element_by_path(next_segment, delimiter);
+
+                                       if (subsearch) return subsearch;
+                               }
+                       }
+
+                       return xml_node();
+               }
+       }
+
+       PUGI__FN bool xml_node::traverse(xml_tree_walker& walker)
+       {
+               walker._depth = -1;
+
+               xml_node arg_begin(_root);
+               if (!walker.begin(arg_begin)) return false;
+
+               xml_node_struct* cur = _root ? _root->first_child + 0 : 0;
+
+               if (cur)
+               {
+                       ++walker._depth;
+
+                       do
+                       {
+                               xml_node arg_for_each(cur);
+                               if (!walker.for_each(arg_for_each))
+                                       return false;
+
+                               if (cur->first_child)
+                               {
+                                       ++walker._depth;
+                                       cur = cur->first_child;
+                               }
+                               else if (cur->next_sibling)
+                                       cur = cur->next_sibling;
+                               else
+                               {
+                                       while (!cur->next_sibling && cur != _root && cur->parent)
+                                       {
+                                               --walker._depth;
+                                               cur = cur->parent;
+                                       }
+
+                                       if (cur != _root)
+                                               cur = cur->next_sibling;
+                               }
+                       }
+                       while (cur && cur != _root);
+               }
+
+               assert(walker._depth == -1);
+
+               xml_node arg_end(_root);
+               return walker.end(arg_end);
+       }
+
+       PUGI__FN size_t xml_node::hash_value() const
+       {
+               return static_cast<size_t>(reinterpret_cast<uintptr_t>(_root) / sizeof(xml_node_struct));
+       }
+
+       PUGI__FN xml_node_struct* xml_node::internal_object() const
+       {
+               return _root;
+       }
+
+       PUGI__FN void xml_node::print(xml_writer& writer, const char_t* indent, unsigned int flags, xml_encoding encoding, unsigned int depth) const
+       {
+               if (!_root) return;
+
+               impl::xml_buffered_writer buffered_writer(writer, encoding);
+
+               impl::node_output(buffered_writer, _root, indent, flags, depth);
+
+               buffered_writer.flush();
+       }
+
+#ifndef PUGIXML_NO_STL
+       PUGI__FN void xml_node::print(std::basic_ostream<char, std::char_traits<char> >& stream, const char_t* indent, unsigned int flags, xml_encoding encoding, unsigned int depth) const
+       {
+               xml_writer_stream writer(stream);
+
+               print(writer, indent, flags, encoding, depth);
+       }
+
+       PUGI__FN void xml_node::print(std::basic_ostream<wchar_t, std::char_traits<wchar_t> >& stream, const char_t* indent, unsigned int flags, unsigned int depth) const
+       {
+               xml_writer_stream writer(stream);
+
+               print(writer, indent, flags, encoding_wchar, depth);
+       }
+#endif
+
+       PUGI__FN ptrdiff_t xml_node::offset_debug() const
+       {
+               if (!_root) return -1;
+
+               impl::xml_document_struct& doc = impl::get_document(_root);
+
+               // we can determine the offset reliably only if there is exactly once parse buffer
+               if (!doc.buffer || doc.extra_buffers) return -1;
+
+               switch (type())
+               {
+               case node_document:
+                       return 0;
+
+               case node_element:
+               case node_declaration:
+               case node_pi:
+                       return _root->name && (_root->header & impl::xml_memory_page_name_allocated_or_shared_mask) == 0 ? _root->name - doc.buffer : -1;
+
+               case node_pcdata:
+               case node_cdata:
+               case node_comment:
+               case node_doctype:
+                       return _root->value && (_root->header & impl::xml_memory_page_value_allocated_or_shared_mask) == 0 ? _root->value - doc.buffer : -1;
+
+               default:
+                       assert(false && "Invalid node type"); // unreachable
+                       return -1;
+               }
+       }
+
+#ifdef __BORLANDC__
+       PUGI__FN bool operator&&(const xml_node& lhs, bool rhs)
+       {
+               return (bool)lhs && rhs;
+       }
+
+       PUGI__FN bool operator||(const xml_node& lhs, bool rhs)
+       {
+               return (bool)lhs || rhs;
+       }
+#endif
+
+       PUGI__FN xml_text::xml_text(xml_node_struct* root): _root(root)
+       {
+       }
+
+       PUGI__FN xml_node_struct* xml_text::_data() const
+       {
+               if (!_root || impl::is_text_node(_root)) return _root;
+
+               // element nodes can have value if parse_embed_pcdata was used
+               if (PUGI__NODETYPE(_root) == node_element && _root->value)
+                       return _root;
+
+               for (xml_node_struct* node = _root->first_child; node; node = node->next_sibling)
+                       if (impl::is_text_node(node))
+                               return node;
+
+               return 0;
+       }
+
+       PUGI__FN xml_node_struct* xml_text::_data_new()
+       {
+               xml_node_struct* d = _data();
+               if (d) return d;
+
+               return xml_node(_root).append_child(node_pcdata).internal_object();
+       }
+
+       PUGI__FN xml_text::xml_text(): _root(0)
+       {
+       }
+
+       PUGI__FN static void unspecified_bool_xml_text(xml_text***)
+       {
+       }
+
+       PUGI__FN xml_text::operator xml_text::unspecified_bool_type() const
+       {
+               return _data() ? unspecified_bool_xml_text : 0;
+       }
+
+       PUGI__FN bool xml_text::operator!() const
+       {
+               return !_data();
+       }
+
+       PUGI__FN bool xml_text::empty() const
+       {
+               return _data() == 0;
+       }
+
+       PUGI__FN const char_t* xml_text::get() const
+       {
+               xml_node_struct* d = _data();
+
+               return (d && d->value) ? d->value + 0 : PUGIXML_TEXT("");
+       }
+
+       PUGI__FN const char_t* xml_text::as_string(const char_t* def) const
+       {
+               xml_node_struct* d = _data();
+
+               return (d && d->value) ? d->value + 0 : def;
+       }
+
+       PUGI__FN int xml_text::as_int(int def) const
+       {
+               xml_node_struct* d = _data();
+
+               return (d && d->value) ? impl::get_value_int(d->value) : def;
+       }
+
+       PUGI__FN unsigned int xml_text::as_uint(unsigned int def) const
+       {
+               xml_node_struct* d = _data();
+
+               return (d && d->value) ? impl::get_value_uint(d->value) : def;
+       }
+
+       PUGI__FN double xml_text::as_double(double def) const
+       {
+               xml_node_struct* d = _data();
+
+               return (d && d->value) ? impl::get_value_double(d->value) : def;
+       }
+
+       PUGI__FN float xml_text::as_float(float def) const
+       {
+               xml_node_struct* d = _data();
+
+               return (d && d->value) ? impl::get_value_float(d->value) : def;
+       }
+
+       PUGI__FN bool xml_text::as_bool(bool def) const
+       {
+               xml_node_struct* d = _data();
+
+               return (d && d->value) ? impl::get_value_bool(d->value) : def;
+       }
+
+#ifdef PUGIXML_HAS_LONG_LONG
+       PUGI__FN long long xml_text::as_llong(long long def) const
+       {
+               xml_node_struct* d = _data();
+
+               return (d && d->value) ? impl::get_value_llong(d->value) : def;
+       }
+
+       PUGI__FN unsigned long long xml_text::as_ullong(unsigned long long def) const
+       {
+               xml_node_struct* d = _data();
+
+               return (d && d->value) ? impl::get_value_ullong(d->value) : def;
+       }
+#endif
+
+       PUGI__FN bool xml_text::set(const char_t* rhs)
+       {
+               xml_node_struct* dn = _data_new();
+
+               return dn ? impl::strcpy_insitu(dn->value, dn->header, impl::xml_memory_page_value_allocated_mask, rhs, impl::strlength(rhs)) : false;
+       }
+
+       PUGI__FN bool xml_text::set(int rhs)
+       {
+               xml_node_struct* dn = _data_new();
+
+               return dn ? impl::set_value_integer<unsigned int>(dn->value, dn->header, impl::xml_memory_page_value_allocated_mask, rhs, rhs < 0) : false;
+       }
+
+       PUGI__FN bool xml_text::set(unsigned int rhs)
+       {
+               xml_node_struct* dn = _data_new();
+
+               return dn ? impl::set_value_integer<unsigned int>(dn->value, dn->header, impl::xml_memory_page_value_allocated_mask, rhs, false) : false;
+       }
+
+       PUGI__FN bool xml_text::set(long rhs)
+       {
+               xml_node_struct* dn = _data_new();
+
+               return dn ? impl::set_value_integer<unsigned long>(dn->value, dn->header, impl::xml_memory_page_value_allocated_mask, rhs, rhs < 0) : false;
+       }
+
+       PUGI__FN bool xml_text::set(unsigned long rhs)
+       {
+               xml_node_struct* dn = _data_new();
+
+               return dn ? impl::set_value_integer<unsigned long>(dn->value, dn->header, impl::xml_memory_page_value_allocated_mask, rhs, false) : false;
+       }
+
+       PUGI__FN bool xml_text::set(float rhs)
+       {
+               xml_node_struct* dn = _data_new();
+
+               return dn ? impl::set_value_convert(dn->value, dn->header, impl::xml_memory_page_value_allocated_mask, rhs) : false;
+       }
+
+       PUGI__FN bool xml_text::set(double rhs)
+       {
+               xml_node_struct* dn = _data_new();
+
+               return dn ? impl::set_value_convert(dn->value, dn->header, impl::xml_memory_page_value_allocated_mask, rhs) : false;
+       }
+
+       PUGI__FN bool xml_text::set(bool rhs)
+       {
+               xml_node_struct* dn = _data_new();
+
+               return dn ? impl::set_value_bool(dn->value, dn->header, impl::xml_memory_page_value_allocated_mask, rhs) : false;
+       }
+
+#ifdef PUGIXML_HAS_LONG_LONG
+       PUGI__FN bool xml_text::set(long long rhs)
+       {
+               xml_node_struct* dn = _data_new();
+
+               return dn ? impl::set_value_integer<unsigned long long>(dn->value, dn->header, impl::xml_memory_page_value_allocated_mask, rhs, rhs < 0) : false;
+       }
+
+       PUGI__FN bool xml_text::set(unsigned long long rhs)
+       {
+               xml_node_struct* dn = _data_new();
+
+               return dn ? impl::set_value_integer<unsigned long long>(dn->value, dn->header, impl::xml_memory_page_value_allocated_mask, rhs, false) : false;
+       }
+#endif
+
+       PUGI__FN xml_text& xml_text::operator=(const char_t* rhs)
+       {
+               set(rhs);
+               return *this;
+       }
+
+       PUGI__FN xml_text& xml_text::operator=(int rhs)
+       {
+               set(rhs);
+               return *this;
+       }
+
+       PUGI__FN xml_text& xml_text::operator=(unsigned int rhs)
+       {
+               set(rhs);
+               return *this;
+       }
+
+       PUGI__FN xml_text& xml_text::operator=(long rhs)
+       {
+               set(rhs);
+               return *this;
+       }
+
+       PUGI__FN xml_text& xml_text::operator=(unsigned long rhs)
+       {
+               set(rhs);
+               return *this;
+       }
+
+       PUGI__FN xml_text& xml_text::operator=(double rhs)
+       {
+               set(rhs);
+               return *this;
+       }
+
+       PUGI__FN xml_text& xml_text::operator=(float rhs)
+       {
+               set(rhs);
+               return *this;
+       }
+
+       PUGI__FN xml_text& xml_text::operator=(bool rhs)
+       {
+               set(rhs);
+               return *this;
+       }
+
+#ifdef PUGIXML_HAS_LONG_LONG
+       PUGI__FN xml_text& xml_text::operator=(long long rhs)
+       {
+               set(rhs);
+               return *this;
+       }
+
+       PUGI__FN xml_text& xml_text::operator=(unsigned long long rhs)
+       {
+               set(rhs);
+               return *this;
+       }
+#endif
+
+       PUGI__FN xml_node xml_text::data() const
+       {
+               return xml_node(_data());
+       }
+
+#ifdef __BORLANDC__
+       PUGI__FN bool operator&&(const xml_text& lhs, bool rhs)
+       {
+               return (bool)lhs && rhs;
+       }
+
+       PUGI__FN bool operator||(const xml_text& lhs, bool rhs)
+       {
+               return (bool)lhs || rhs;
+       }
+#endif
+
+       PUGI__FN xml_node_iterator::xml_node_iterator()
+       {
+       }
+
+       PUGI__FN xml_node_iterator::xml_node_iterator(const xml_node& node): _wrap(node), _parent(node.parent())
+       {
+       }
+
+       PUGI__FN xml_node_iterator::xml_node_iterator(xml_node_struct* ref, xml_node_struct* parent): _wrap(ref), _parent(parent)
+       {
+       }
+
+       PUGI__FN bool xml_node_iterator::operator==(const xml_node_iterator& rhs) const
+       {
+               return _wrap._root == rhs._wrap._root && _parent._root == rhs._parent._root;
+       }
+
+       PUGI__FN bool xml_node_iterator::operator!=(const xml_node_iterator& rhs) const
+       {
+               return _wrap._root != rhs._wrap._root || _parent._root != rhs._parent._root;
+       }
+
+       PUGI__FN xml_node& xml_node_iterator::operator*() const
+       {
+               assert(_wrap._root);
+               return _wrap;
+       }
+
+       PUGI__FN xml_node* xml_node_iterator::operator->() const
+       {
+               assert(_wrap._root);
+               return const_cast<xml_node*>(&_wrap); // BCC5 workaround
+       }
+
+       PUGI__FN const xml_node_iterator& xml_node_iterator::operator++()
+       {
+               assert(_wrap._root);
+               _wrap._root = _wrap._root->next_sibling;
+               return *this;
+       }
+
+       PUGI__FN xml_node_iterator xml_node_iterator::operator++(int)
+       {
+               xml_node_iterator temp = *this;
+               ++*this;
+               return temp;
+       }
+
+       PUGI__FN const xml_node_iterator& xml_node_iterator::operator--()
+       {
+               _wrap = _wrap._root ? _wrap.previous_sibling() : _parent.last_child();
+               return *this;
+       }
+
+       PUGI__FN xml_node_iterator xml_node_iterator::operator--(int)
+       {
+               xml_node_iterator temp = *this;
+               --*this;
+               return temp;
+       }
+
+       PUGI__FN xml_attribute_iterator::xml_attribute_iterator()
+       {
+       }
+
+       PUGI__FN xml_attribute_iterator::xml_attribute_iterator(const xml_attribute& attr, const xml_node& parent): _wrap(attr), _parent(parent)
+       {
+       }
+
+       PUGI__FN xml_attribute_iterator::xml_attribute_iterator(xml_attribute_struct* ref, xml_node_struct* parent): _wrap(ref), _parent(parent)
+       {
+       }
+
+       PUGI__FN bool xml_attribute_iterator::operator==(const xml_attribute_iterator& rhs) const
+       {
+               return _wrap._attr == rhs._wrap._attr && _parent._root == rhs._parent._root;
+       }
+
+       PUGI__FN bool xml_attribute_iterator::operator!=(const xml_attribute_iterator& rhs) const
+       {
+               return _wrap._attr != rhs._wrap._attr || _parent._root != rhs._parent._root;
+       }
+
+       PUGI__FN xml_attribute& xml_attribute_iterator::operator*() const
+       {
+               assert(_wrap._attr);
+               return _wrap;
+       }
+
+       PUGI__FN xml_attribute* xml_attribute_iterator::operator->() const
+       {
+               assert(_wrap._attr);
+               return const_cast<xml_attribute*>(&_wrap); // BCC5 workaround
+       }
+
+       PUGI__FN const xml_attribute_iterator& xml_attribute_iterator::operator++()
+       {
+               assert(_wrap._attr);
+               _wrap._attr = _wrap._attr->next_attribute;
+               return *this;
+       }
+
+       PUGI__FN xml_attribute_iterator xml_attribute_iterator::operator++(int)
+       {
+               xml_attribute_iterator temp = *this;
+               ++*this;
+               return temp;
+       }
+
+       PUGI__FN const xml_attribute_iterator& xml_attribute_iterator::operator--()
+       {
+               _wrap = _wrap._attr ? _wrap.previous_attribute() : _parent.last_attribute();
+               return *this;
+       }
+
+       PUGI__FN xml_attribute_iterator xml_attribute_iterator::operator--(int)
+       {
+               xml_attribute_iterator temp = *this;
+               --*this;
+               return temp;
+       }
+
+       PUGI__FN xml_named_node_iterator::xml_named_node_iterator(): _name(0)
+       {
+       }
+
+       PUGI__FN xml_named_node_iterator::xml_named_node_iterator(const xml_node& node, const char_t* name): _wrap(node), _parent(node.parent()), _name(name)
+       {
+       }
+
+       PUGI__FN xml_named_node_iterator::xml_named_node_iterator(xml_node_struct* ref, xml_node_struct* parent, const char_t* name): _wrap(ref), _parent(parent), _name(name)
+       {
+       }
+
+       PUGI__FN bool xml_named_node_iterator::operator==(const xml_named_node_iterator& rhs) const
+       {
+               return _wrap._root == rhs._wrap._root && _parent._root == rhs._parent._root;
+       }
+
+       PUGI__FN bool xml_named_node_iterator::operator!=(const xml_named_node_iterator& rhs) const
+       {
+               return _wrap._root != rhs._wrap._root || _parent._root != rhs._parent._root;
+       }
+
+       PUGI__FN xml_node& xml_named_node_iterator::operator*() const
+       {
+               assert(_wrap._root);
+               return _wrap;
+       }
+
+       PUGI__FN xml_node* xml_named_node_iterator::operator->() const
+       {
+               assert(_wrap._root);
+               return const_cast<xml_node*>(&_wrap); // BCC5 workaround
+       }
+
+       PUGI__FN const xml_named_node_iterator& xml_named_node_iterator::operator++()
+       {
+               assert(_wrap._root);
+               _wrap = _wrap.next_sibling(_name);
+               return *this;
+       }
+
+       PUGI__FN xml_named_node_iterator xml_named_node_iterator::operator++(int)
+       {
+               xml_named_node_iterator temp = *this;
+               ++*this;
+               return temp;
+       }
+
+       PUGI__FN const xml_named_node_iterator& xml_named_node_iterator::operator--()
+       {
+               if (_wrap._root)
+                       _wrap = _wrap.previous_sibling(_name);
+               else
+               {
+                       _wrap = _parent.last_child();
+
+                       if (!impl::strequal(_wrap.name(), _name))
+                               _wrap = _wrap.previous_sibling(_name);
+               }
+
+               return *this;
+       }
+
+       PUGI__FN xml_named_node_iterator xml_named_node_iterator::operator--(int)
+       {
+               xml_named_node_iterator temp = *this;
+               --*this;
+               return temp;
+       }
+
+       PUGI__FN xml_parse_result::xml_parse_result(): status(status_internal_error), offset(0), encoding(encoding_auto)
+       {
+       }
+
+       PUGI__FN xml_parse_result::operator bool() const
+       {
+               return status == status_ok;
+       }
+
+       PUGI__FN const char* xml_parse_result::description() const
+       {
+               switch (status)
+               {
+               case status_ok: return "No error";
+
+               case status_file_not_found: return "File was not found";
+               case status_io_error: return "Error reading from file/stream";
+               case status_out_of_memory: return "Could not allocate memory";
+               case status_internal_error: return "Internal error occurred";
+
+               case status_unrecognized_tag: return "Could not determine tag type";
+
+               case status_bad_pi: return "Error parsing document declaration/processing instruction";
+               case status_bad_comment: return "Error parsing comment";
+               case status_bad_cdata: return "Error parsing CDATA section";
+               case status_bad_doctype: return "Error parsing document type declaration";
+               case status_bad_pcdata: return "Error parsing PCDATA section";
+               case status_bad_start_element: return "Error parsing start element tag";
+               case status_bad_attribute: return "Error parsing element attribute";
+               case status_bad_end_element: return "Error parsing end element tag";
+               case status_end_element_mismatch: return "Start-end tags mismatch";
+
+               case status_append_invalid_root: return "Unable to append nodes: root is not an element or document";
+
+               case status_no_document_element: return "No document element found";
+
+               default: return "Unknown error";
+               }
+       }
+
+       PUGI__FN xml_document::xml_document(): _buffer(0)
+       {
+               _create();
+       }
+
+       PUGI__FN xml_document::~xml_document()
+       {
+               _destroy();
+       }
+
+#ifdef PUGIXML_HAS_MOVE
+       PUGI__FN xml_document::xml_document(xml_document&& rhs) PUGIXML_NOEXCEPT_IF_NOT_COMPACT: _buffer(0)
+       {
+               _create();
+               _move(rhs);
+       }
+
+       PUGI__FN xml_document& xml_document::operator=(xml_document&& rhs) PUGIXML_NOEXCEPT_IF_NOT_COMPACT
+       {
+               if (this == &rhs) return *this;
+
+               _destroy();
+               _create();
+               _move(rhs);
+
+               return *this;
+       }
+#endif
+
+       PUGI__FN void xml_document::reset()
+       {
+               _destroy();
+               _create();
+       }
+
+       PUGI__FN void xml_document::reset(const xml_document& proto)
+       {
+               reset();
+
+               for (xml_node cur = proto.first_child(); cur; cur = cur.next_sibling())
+                       append_copy(cur);
+       }
+
+       PUGI__FN void xml_document::_create()
+       {
+               assert(!_root);
+
+       #ifdef PUGIXML_COMPACT
+               // space for page marker for the first page (uint32_t), rounded up to pointer size; assumes pointers are at least 32-bit
+               const size_t page_offset = sizeof(void*);
+       #else
+               const size_t page_offset = 0;
+       #endif
+
+               // initialize sentinel page
+               PUGI__STATIC_ASSERT(sizeof(impl::xml_memory_page) + sizeof(impl::xml_document_struct) + page_offset <= sizeof(_memory));
+
+               // prepare page structure
+               impl::xml_memory_page* page = impl::xml_memory_page::construct(_memory);
+               assert(page);
+
+               page->busy_size = impl::xml_memory_page_size;
+
+               // setup first page marker
+       #ifdef PUGIXML_COMPACT
+               // round-trip through void* to avoid 'cast increases required alignment of target type' warning
+               page->compact_page_marker = reinterpret_cast<uint32_t*>(static_cast<void*>(reinterpret_cast<char*>(page) + sizeof(impl::xml_memory_page)));
+               *page->compact_page_marker = sizeof(impl::xml_memory_page);
+       #endif
+
+               // allocate new root
+               _root = new (reinterpret_cast<char*>(page) + sizeof(impl::xml_memory_page) + page_offset) impl::xml_document_struct(page);
+               _root->prev_sibling_c = _root;
+
+               // setup sentinel page
+               page->allocator = static_cast<impl::xml_document_struct*>(_root);
+
+               // setup hash table pointer in allocator
+       #ifdef PUGIXML_COMPACT
+               page->allocator->_hash = &static_cast<impl::xml_document_struct*>(_root)->hash;
+       #endif
+
+               // verify the document allocation
+               assert(reinterpret_cast<char*>(_root) + sizeof(impl::xml_document_struct) <= _memory + sizeof(_memory));
+       }
+
+       PUGI__FN void xml_document::_destroy()
+       {
+               assert(_root);
+
+               // destroy static storage
+               if (_buffer)
+               {
+                       impl::xml_memory::deallocate(_buffer);
+                       _buffer = 0;
+               }
+
+               // destroy extra buffers (note: no need to destroy linked list nodes, they're allocated using document allocator)
+               for (impl::xml_extra_buffer* extra = static_cast<impl::xml_document_struct*>(_root)->extra_buffers; extra; extra = extra->next)
+               {
+                       if (extra->buffer) impl::xml_memory::deallocate(extra->buffer);
+               }
+
+               // destroy dynamic storage, leave sentinel page (it's in static memory)
+               impl::xml_memory_page* root_page = PUGI__GETPAGE(_root);
+               assert(root_page && !root_page->prev);
+               assert(reinterpret_cast<char*>(root_page) >= _memory && reinterpret_cast<char*>(root_page) < _memory + sizeof(_memory));
+
+               for (impl::xml_memory_page* page = root_page->next; page; )
+               {
+                       impl::xml_memory_page* next = page->next;
+
+                       impl::xml_allocator::deallocate_page(page);
+
+                       page = next;
+               }
+
+       #ifdef PUGIXML_COMPACT
+               // destroy hash table
+               static_cast<impl::xml_document_struct*>(_root)->hash.clear();
+       #endif
+
+               _root = 0;
+       }
+
+#ifdef PUGIXML_HAS_MOVE
+       PUGI__FN void xml_document::_move(xml_document& rhs) PUGIXML_NOEXCEPT_IF_NOT_COMPACT
+       {
+               impl::xml_document_struct* doc = static_cast<impl::xml_document_struct*>(_root);
+               impl::xml_document_struct* other = static_cast<impl::xml_document_struct*>(rhs._root);
+
+               // save first child pointer for later; this needs hash access
+               xml_node_struct* other_first_child = other->first_child;
+
+       #ifdef PUGIXML_COMPACT
+               // reserve space for the hash table up front; this is the only operation that can fail
+               // if it does, we have no choice but to throw (if we have exceptions)
+               if (other_first_child)
+               {
+                       size_t other_children = 0;
+                       for (xml_node_struct* node = other_first_child; node; node = node->next_sibling)
+                               other_children++;
+
+                       // in compact mode, each pointer assignment could result in a hash table request
+                       // during move, we have to relocate document first_child and parents of all children
+                       // normally there's just one child and its parent has a pointerless encoding but
+                       // we assume the worst here
+                       if (!other->_hash->reserve(other_children + 1))
+                       {
+                       #ifdef PUGIXML_NO_EXCEPTIONS
+                               return;
+                       #else
+                               throw std::bad_alloc();
+                       #endif
+                       }
+               }
+       #endif
+
+               // move allocation state
+               doc->_root = other->_root;
+               doc->_busy_size = other->_busy_size;
+
+               // move buffer state
+               doc->buffer = other->buffer;
+               doc->extra_buffers = other->extra_buffers;
+               _buffer = rhs._buffer;
+
+       #ifdef PUGIXML_COMPACT
+               // move compact hash; note that the hash table can have pointers to other but they will be "inactive", similarly to nodes removed with remove_child
+               doc->hash = other->hash;
+               doc->_hash = &doc->hash;
+
+               // make sure we don't access other hash up until the end when we reinitialize other document
+               other->_hash = 0;
+       #endif
+
+               // move page structure
+               impl::xml_memory_page* doc_page = PUGI__GETPAGE(doc);
+               assert(doc_page && !doc_page->prev && !doc_page->next);
+
+               impl::xml_memory_page* other_page = PUGI__GETPAGE(other);
+               assert(other_page && !other_page->prev);
+
+               // relink pages since root page is embedded into xml_document
+               if (impl::xml_memory_page* page = other_page->next)
+               {
+                       assert(page->prev == other_page);
+
+                       page->prev = doc_page;
+
+                       doc_page->next = page;
+                       other_page->next = 0;
+               }
+
+               // make sure pages point to the correct document state
+               for (impl::xml_memory_page* page = doc_page->next; page; page = page->next)
+               {
+                       assert(page->allocator == other);
+
+                       page->allocator = doc;
+
+               #ifdef PUGIXML_COMPACT
+                       // this automatically migrates most children between documents and prevents ->parent assignment from allocating
+                       if (page->compact_shared_parent == other)
+                               page->compact_shared_parent = doc;
+               #endif
+               }
+
+               // move tree structure
+               assert(!doc->first_child);
+
+               doc->first_child = other_first_child;
+
+               for (xml_node_struct* node = other_first_child; node; node = node->next_sibling)
+               {
+               #ifdef PUGIXML_COMPACT
+                       // most children will have migrated when we reassigned compact_shared_parent
+                       assert(node->parent == other || node->parent == doc);
+
+                       node->parent = doc;
+               #else
+                       assert(node->parent == other);
+                       node->parent = doc;
+               #endif
+               }
+
+               // reset other document
+               new (other) impl::xml_document_struct(PUGI__GETPAGE(other));
+               rhs._buffer = 0;
+       }
+#endif
+
+#ifndef PUGIXML_NO_STL
+       PUGI__FN xml_parse_result xml_document::load(std::basic_istream<char, std::char_traits<char> >& stream, unsigned int options, xml_encoding encoding)
+       {
+               reset();
+
+               return impl::load_stream_impl(static_cast<impl::xml_document_struct*>(_root), stream, options, encoding, &_buffer);
+       }
+
+       PUGI__FN xml_parse_result xml_document::load(std::basic_istream<wchar_t, std::char_traits<wchar_t> >& stream, unsigned int options)
+       {
+               reset();
+
+               return impl::load_stream_impl(static_cast<impl::xml_document_struct*>(_root), stream, options, encoding_wchar, &_buffer);
+       }
+#endif
+
+       PUGI__FN xml_parse_result xml_document::load_string(const char_t* contents, unsigned int options)
+       {
+               // Force native encoding (skip autodetection)
+       #ifdef PUGIXML_WCHAR_MODE
+               xml_encoding encoding = encoding_wchar;
+       #else
+               xml_encoding encoding = encoding_utf8;
+       #endif
+
+               return load_buffer(contents, impl::strlength(contents) * sizeof(char_t), options, encoding);
+       }
+
+       PUGI__FN xml_parse_result xml_document::load(const char_t* contents, unsigned int options)
+       {
+               return load_string(contents, options);
+       }
+
+       PUGI__FN xml_parse_result xml_document::load_file(const char* path_, unsigned int options, xml_encoding encoding)
+       {
+               reset();
+
+               using impl::auto_deleter; // MSVC7 workaround
+               auto_deleter<FILE> file(fopen(path_, "rb"), impl::close_file);
+
+               return impl::load_file_impl(static_cast<impl::xml_document_struct*>(_root), file.data, options, encoding, &_buffer);
+       }
+
+       PUGI__FN xml_parse_result xml_document::load_file(const wchar_t* path_, unsigned int options, xml_encoding encoding)
+       {
+               reset();
+
+               using impl::auto_deleter; // MSVC7 workaround
+               auto_deleter<FILE> file(impl::open_file_wide(path_, L"rb"), impl::close_file);
+
+               return impl::load_file_impl(static_cast<impl::xml_document_struct*>(_root), file.data, options, encoding, &_buffer);
+       }
+
+       PUGI__FN xml_parse_result xml_document::load_buffer(const void* contents, size_t size, unsigned int options, xml_encoding encoding)
+       {
+               reset();
+
+               return impl::load_buffer_impl(static_cast<impl::xml_document_struct*>(_root), _root, const_cast<void*>(contents), size, options, encoding, false, false, &_buffer);
+       }
+
+       PUGI__FN xml_parse_result xml_document::load_buffer_inplace(void* contents, size_t size, unsigned int options, xml_encoding encoding)
+       {
+               reset();
+
+               return impl::load_buffer_impl(static_cast<impl::xml_document_struct*>(_root), _root, contents, size, options, encoding, true, false, &_buffer);
+       }
+
+       PUGI__FN xml_parse_result xml_document::load_buffer_inplace_own(void* contents, size_t size, unsigned int options, xml_encoding encoding)
+       {
+               reset();
+
+               return impl::load_buffer_impl(static_cast<impl::xml_document_struct*>(_root), _root, contents, size, options, encoding, true, true, &_buffer);
+       }
+
+       PUGI__FN void xml_document::save(xml_writer& writer, const char_t* indent, unsigned int flags, xml_encoding encoding) const
+       {
+               impl::xml_buffered_writer buffered_writer(writer, encoding);
+
+               if ((flags & format_write_bom) && encoding != encoding_latin1)
+               {
+                       // BOM always represents the codepoint U+FEFF, so just write it in native encoding
+               #ifdef PUGIXML_WCHAR_MODE
+                       unsigned int bom = 0xfeff;
+                       buffered_writer.write(static_cast<wchar_t>(bom));
+               #else
+                       buffered_writer.write('\xef', '\xbb', '\xbf');
+               #endif
+               }
+
+               if (!(flags & format_no_declaration) && !impl::has_declaration(_root))
+               {
+                       buffered_writer.write_string(PUGIXML_TEXT("<?xml version=\"1.0\""));
+                       if (encoding == encoding_latin1) buffered_writer.write_string(PUGIXML_TEXT(" encoding=\"ISO-8859-1\""));
+                       buffered_writer.write('?', '>');
+                       if (!(flags & format_raw)) buffered_writer.write('\n');
+               }
+
+               impl::node_output(buffered_writer, _root, indent, flags, 0);
+
+               buffered_writer.flush();
+       }
+
+#ifndef PUGIXML_NO_STL
+       PUGI__FN void xml_document::save(std::basic_ostream<char, std::char_traits<char> >& stream, const char_t* indent, unsigned int flags, xml_encoding encoding) const
+       {
+               xml_writer_stream writer(stream);
+
+               save(writer, indent, flags, encoding);
+       }
+
+       PUGI__FN void xml_document::save(std::basic_ostream<wchar_t, std::char_traits<wchar_t> >& stream, const char_t* indent, unsigned int flags) const
+       {
+               xml_writer_stream writer(stream);
+
+               save(writer, indent, flags, encoding_wchar);
+       }
+#endif
+
+       PUGI__FN bool xml_document::save_file(const char* path_, const char_t* indent, unsigned int flags, xml_encoding encoding) const
+       {
+               using impl::auto_deleter; // MSVC7 workaround
+               auto_deleter<FILE> file(fopen(path_, (flags & format_save_file_text) ? "w" : "wb"), impl::close_file);
+
+               return impl::save_file_impl(*this, file.data, indent, flags, encoding);
+       }
+
+       PUGI__FN bool xml_document::save_file(const wchar_t* path_, const char_t* indent, unsigned int flags, xml_encoding encoding) const
+       {
+               using impl::auto_deleter; // MSVC7 workaround
+               auto_deleter<FILE> file(impl::open_file_wide(path_, (flags & format_save_file_text) ? L"w" : L"wb"), impl::close_file);
+
+               return impl::save_file_impl(*this, file.data, indent, flags, encoding);
+       }
+
+       PUGI__FN xml_node xml_document::document_element() const
+       {
+               assert(_root);
+
+               for (xml_node_struct* i = _root->first_child; i; i = i->next_sibling)
+                       if (PUGI__NODETYPE(i) == node_element)
+                               return xml_node(i);
+
+               return xml_node();
+       }
+
+#ifndef PUGIXML_NO_STL
+       PUGI__FN std::string PUGIXML_FUNCTION as_utf8(const wchar_t* str)
+       {
+               assert(str);
+
+               return impl::as_utf8_impl(str, impl::strlength_wide(str));
+       }
+
+       PUGI__FN std::string PUGIXML_FUNCTION as_utf8(const std::basic_string<wchar_t>& str)
+       {
+               return impl::as_utf8_impl(str.c_str(), str.size());
+       }
+
+       PUGI__FN std::basic_string<wchar_t> PUGIXML_FUNCTION as_wide(const char* str)
+       {
+               assert(str);
+
+               return impl::as_wide_impl(str, strlen(str));
+       }
+
+       PUGI__FN std::basic_string<wchar_t> PUGIXML_FUNCTION as_wide(const std::string& str)
+       {
+               return impl::as_wide_impl(str.c_str(), str.size());
+       }
+#endif
+
+       PUGI__FN void PUGIXML_FUNCTION set_memory_management_functions(allocation_function allocate, deallocation_function deallocate)
+       {
+               impl::xml_memory::allocate = allocate;
+               impl::xml_memory::deallocate = deallocate;
+       }
+
+       PUGI__FN allocation_function PUGIXML_FUNCTION get_memory_allocation_function()
+       {
+               return impl::xml_memory::allocate;
+       }
+
+       PUGI__FN deallocation_function PUGIXML_FUNCTION get_memory_deallocation_function()
+       {
+               return impl::xml_memory::deallocate;
+       }
+}
+
+#if !defined(PUGIXML_NO_STL) && (defined(_MSC_VER) || defined(__ICC))
+namespace std
+{
+       // Workarounds for (non-standard) iterator category detection for older versions (MSVC7/IC8 and earlier)
+       PUGI__FN std::bidirectional_iterator_tag _Iter_cat(const pugi::xml_node_iterator&)
+       {
+               return std::bidirectional_iterator_tag();
+       }
+
+       PUGI__FN std::bidirectional_iterator_tag _Iter_cat(const pugi::xml_attribute_iterator&)
+       {
+               return std::bidirectional_iterator_tag();
+       }
+
+       PUGI__FN std::bidirectional_iterator_tag _Iter_cat(const pugi::xml_named_node_iterator&)
+       {
+               return std::bidirectional_iterator_tag();
+       }
+}
+#endif
+
+#if !defined(PUGIXML_NO_STL) && defined(__SUNPRO_CC)
+namespace std
+{
+       // Workarounds for (non-standard) iterator category detection
+       PUGI__FN std::bidirectional_iterator_tag __iterator_category(const pugi::xml_node_iterator&)
+       {
+               return std::bidirectional_iterator_tag();
+       }
+
+       PUGI__FN std::bidirectional_iterator_tag __iterator_category(const pugi::xml_attribute_iterator&)
+       {
+               return std::bidirectional_iterator_tag();
+       }
+
+       PUGI__FN std::bidirectional_iterator_tag __iterator_category(const pugi::xml_named_node_iterator&)
+       {
+               return std::bidirectional_iterator_tag();
+       }
+}
+#endif
+
+#ifndef PUGIXML_NO_XPATH
+// STL replacements
+PUGI__NS_BEGIN
+       struct equal_to
+       {
+               template <typename T> bool operator()(const T& lhs, const T& rhs) const
+               {
+                       return lhs == rhs;
+               }
+       };
+
+       struct not_equal_to
+       {
+               template <typename T> bool operator()(const T& lhs, const T& rhs) const
+               {
+                       return lhs != rhs;
+               }
+       };
+
+       struct less
+       {
+               template <typename T> bool operator()(const T& lhs, const T& rhs) const
+               {
+                       return lhs < rhs;
+               }
+       };
+
+       struct less_equal
+       {
+               template <typename T> bool operator()(const T& lhs, const T& rhs) const
+               {
+                       return lhs <= rhs;
+               }
+       };
+
+       template <typename T> void swap(T& lhs, T& rhs)
+       {
+               T temp = lhs;
+               lhs = rhs;
+               rhs = temp;
+       }
+
+       template <typename I, typename Pred> I min_element(I begin, I end, const Pred& pred)
+       {
+               I result = begin;
+
+               for (I it = begin + 1; it != end; ++it)
+                       if (pred(*it, *result))
+                               result = it;
+
+               return result;
+       }
+
+       template <typename I> void reverse(I begin, I end)
+       {
+               while (end - begin > 1) swap(*begin++, *--end);
+       }
+
+       template <typename I> I unique(I begin, I end)
+       {
+               // fast skip head
+               while (end - begin > 1 && *begin != *(begin + 1)) begin++;
+
+               if (begin == end) return begin;
+
+               // last written element
+               I write = begin++;
+
+               // merge unique elements
+               while (begin != end)
+               {
+                       if (*begin != *write)
+                               *++write = *begin++;
+                       else
+                               begin++;
+               }
+
+               // past-the-end (write points to live element)
+               return write + 1;
+       }
+
+       template <typename T, typename Pred> void insertion_sort(T* begin, T* end, const Pred& pred)
+       {
+               if (begin == end)
+                       return;
+
+               for (T* it = begin + 1; it != end; ++it)
+               {
+                       T val = *it;
+                       T* hole = it;
+
+                       // move hole backwards
+                       while (hole > begin && pred(val, *(hole - 1)))
+                       {
+                               *hole = *(hole - 1);
+                               hole--;
+                       }
+
+                       // fill hole with element
+                       *hole = val;
+               }
+       }
+
+       template <typename I, typename Pred> I median3(I first, I middle, I last, const Pred& pred)
+       {
+               if (pred(*middle, *first)) swap(middle, first);
+               if (pred(*last, *middle)) swap(last, middle);
+               if (pred(*middle, *first)) swap(middle, first);
+
+               return middle;
+       }
+
+       template <typename T, typename Pred> void partition3(T* begin, T* end, T pivot, const Pred& pred, T** out_eqbeg, T** out_eqend)
+       {
+               // invariant: array is split into 4 groups: = < ? > (each variable denotes the boundary between the groups)
+               T* eq = begin;
+               T* lt = begin;
+               T* gt = end;
+
+               while (lt < gt)
+               {
+                       if (pred(*lt, pivot))
+                               lt++;
+                       else if (*lt == pivot)
+                               swap(*eq++, *lt++);
+                       else
+                               swap(*lt, *--gt);
+               }
+
+               // we now have just 4 groups: = < >; move equal elements to the middle
+               T* eqbeg = gt;
+
+               for (T* it = begin; it != eq; ++it)
+                       swap(*it, *--eqbeg);
+
+               *out_eqbeg = eqbeg;
+               *out_eqend = gt;
+       }
+
+       template <typename I, typename Pred> void sort(I begin, I end, const Pred& pred)
+       {
+               // sort large chunks
+               while (end - begin > 16)
+               {
+                       // find median element
+                       I middle = begin + (end - begin) / 2;
+                       I median = median3(begin, middle, end - 1, pred);
+
+                       // partition in three chunks (< = >)
+                       I eqbeg, eqend;
+                       partition3(begin, end, *median, pred, &eqbeg, &eqend);
+
+                       // loop on larger half
+                       if (eqbeg - begin > end - eqend)
+                       {
+                               sort(eqend, end, pred);
+                               end = eqbeg;
+                       }
+                       else
+                       {
+                               sort(begin, eqbeg, pred);
+                               begin = eqend;
+                       }
+               }
+
+               // insertion sort small chunk
+               insertion_sort(begin, end, pred);
+       }
+PUGI__NS_END
+
+// Allocator used for AST and evaluation stacks
+PUGI__NS_BEGIN
+       static const size_t xpath_memory_page_size =
+       #ifdef PUGIXML_MEMORY_XPATH_PAGE_SIZE
+               PUGIXML_MEMORY_XPATH_PAGE_SIZE
+       #else
+               4096
+       #endif
+               ;
+
+       static const uintptr_t xpath_memory_block_alignment = sizeof(double) > sizeof(void*) ? sizeof(double) : sizeof(void*);
+
+       struct xpath_memory_block
+       {
+               xpath_memory_block* next;
+               size_t capacity;
+
+               union
+               {
+                       char data[xpath_memory_page_size];
+                       double alignment;
+               };
+       };
+
+       struct xpath_allocator
+       {
+               xpath_memory_block* _root;
+               size_t _root_size;
+               bool* _error;
+
+               xpath_allocator(xpath_memory_block* root, bool* error = 0): _root(root), _root_size(0), _error(error)
+               {
+               }
+
+               void* allocate(size_t size)
+               {
+                       // round size up to block alignment boundary
+                       size = (size + xpath_memory_block_alignment - 1) & ~(xpath_memory_block_alignment - 1);
+
+                       if (_root_size + size <= _root->capacity)
+                       {
+                               void* buf = &_root->data[0] + _root_size;
+                               _root_size += size;
+                               return buf;
+                       }
+                       else
+                       {
+                               // make sure we have at least 1/4th of the page free after allocation to satisfy subsequent allocation requests
+                               size_t block_capacity_base = sizeof(_root->data);
+                               size_t block_capacity_req = size + block_capacity_base / 4;
+                               size_t block_capacity = (block_capacity_base > block_capacity_req) ? block_capacity_base : block_capacity_req;
+
+                               size_t block_size = block_capacity + offsetof(xpath_memory_block, data);
+
+                               xpath_memory_block* block = static_cast<xpath_memory_block*>(xml_memory::allocate(block_size));
+                               if (!block)
+                               {
+                                       if (_error) *_error = true;
+                                       return 0;
+                               }
+
+                               block->next = _root;
+                               block->capacity = block_capacity;
+
+                               _root = block;
+                               _root_size = size;
+
+                               return block->data;
+                       }
+               }
+
+               void* reallocate(void* ptr, size_t old_size, size_t new_size)
+               {
+                       // round size up to block alignment boundary
+                       old_size = (old_size + xpath_memory_block_alignment - 1) & ~(xpath_memory_block_alignment - 1);
+                       new_size = (new_size + xpath_memory_block_alignment - 1) & ~(xpath_memory_block_alignment - 1);
+
+                       // we can only reallocate the last object
+                       assert(ptr == 0 || static_cast<char*>(ptr) + old_size == &_root->data[0] + _root_size);
+
+                       // try to reallocate the object inplace
+                       if (ptr && _root_size - old_size + new_size <= _root->capacity)
+                       {
+                               _root_size = _root_size - old_size + new_size;
+                               return ptr;
+                       }
+
+                       // allocate a new block
+                       void* result = allocate(new_size);
+                       if (!result) return 0;
+
+                       // we have a new block
+                       if (ptr)
+                       {
+                               // copy old data (we only support growing)
+                               assert(new_size >= old_size);
+                               memcpy(result, ptr, old_size);
+
+                               // free the previous page if it had no other objects
+                               assert(_root->data == result);
+                               assert(_root->next);
+
+                               if (_root->next->data == ptr)
+                               {
+                                       // deallocate the whole page, unless it was the first one
+                                       xpath_memory_block* next = _root->next->next;
+
+                                       if (next)
+                                       {
+                                               xml_memory::deallocate(_root->next);
+                                               _root->next = next;
+                                       }
+                               }
+                       }
+
+                       return result;
+               }
+
+               void revert(const xpath_allocator& state)
+               {
+                       // free all new pages
+                       xpath_memory_block* cur = _root;
+
+                       while (cur != state._root)
+                       {
+                               xpath_memory_block* next = cur->next;
+
+                               xml_memory::deallocate(cur);
+
+                               cur = next;
+                       }
+
+                       // restore state
+                       _root = state._root;
+                       _root_size = state._root_size;
+               }
+
+               void release()
+               {
+                       xpath_memory_block* cur = _root;
+                       assert(cur);
+
+                       while (cur->next)
+                       {
+                               xpath_memory_block* next = cur->next;
+
+                               xml_memory::deallocate(cur);
+
+                               cur = next;
+                       }
+               }
+       };
+
+       struct xpath_allocator_capture
+       {
+               xpath_allocator_capture(xpath_allocator* alloc): _target(alloc), _state(*alloc)
+               {
+               }
+
+               ~xpath_allocator_capture()
+               {
+                       _target->revert(_state);
+               }
+
+               xpath_allocator* _target;
+               xpath_allocator _state;
+       };
+
+       struct xpath_stack
+       {
+               xpath_allocator* result;
+               xpath_allocator* temp;
+       };
+
+       struct xpath_stack_data
+       {
+               xpath_memory_block blocks[2];
+               xpath_allocator result;
+               xpath_allocator temp;
+               xpath_stack stack;
+               bool oom;
+
+               xpath_stack_data(): result(blocks + 0, &oom), temp(blocks + 1, &oom), oom(false)
+               {
+                       blocks[0].next = blocks[1].next = 0;
+                       blocks[0].capacity = blocks[1].capacity = sizeof(blocks[0].data);
+
+                       stack.result = &result;
+                       stack.temp = &temp;
+               }
+
+               ~xpath_stack_data()
+               {
+                       result.release();
+                       temp.release();
+               }
+       };
+PUGI__NS_END
+
+// String class
+PUGI__NS_BEGIN
+       class xpath_string
+       {
+               const char_t* _buffer;
+               bool _uses_heap;
+               size_t _length_heap;
+
+               static char_t* duplicate_string(const char_t* string, size_t length, xpath_allocator* alloc)
+               {
+                       char_t* result = static_cast<char_t*>(alloc->allocate((length + 1) * sizeof(char_t)));
+                       if (!result) return 0;
+
+                       memcpy(result, string, length * sizeof(char_t));
+                       result[length] = 0;
+
+                       return result;
+               }
+
+               xpath_string(const char_t* buffer, bool uses_heap_, size_t length_heap): _buffer(buffer), _uses_heap(uses_heap_), _length_heap(length_heap)
+               {
+               }
+
+       public:
+               static xpath_string from_const(const char_t* str)
+               {
+                       return xpath_string(str, false, 0);
+               }
+
+               static xpath_string from_heap_preallocated(const char_t* begin, const char_t* end)
+               {
+                       assert(begin <= end && *end == 0);
+
+                       return xpath_string(begin, true, static_cast<size_t>(end - begin));
+               }
+
+               static xpath_string from_heap(const char_t* begin, const char_t* end, xpath_allocator* alloc)
+               {
+                       assert(begin <= end);
+
+                       if (begin == end)
+                               return xpath_string();
+
+                       size_t length = static_cast<size_t>(end - begin);
+                       const char_t* data = duplicate_string(begin, length, alloc);
+
+                       return data ? xpath_string(data, true, length) : xpath_string();
+               }
+
+               xpath_string(): _buffer(PUGIXML_TEXT("")), _uses_heap(false), _length_heap(0)
+               {
+               }
+
+               void append(const xpath_string& o, xpath_allocator* alloc)
+               {
+                       // skip empty sources
+                       if (!*o._buffer) return;
+
+                       // fast append for constant empty target and constant source
+                       if (!*_buffer && !_uses_heap && !o._uses_heap)
+                       {
+                               _buffer = o._buffer;
+                       }
+                       else
+                       {
+                               // need to make heap copy
+                               size_t target_length = length();
+                               size_t source_length = o.length();
+                               size_t result_length = target_length + source_length;
+
+                               // allocate new buffer
+                               char_t* result = static_cast<char_t*>(alloc->reallocate(_uses_heap ? const_cast<char_t*>(_buffer) : 0, (target_length + 1) * sizeof(char_t), (result_length + 1) * sizeof(char_t)));
+                               if (!result) return;
+
+                               // append first string to the new buffer in case there was no reallocation
+                               if (!_uses_heap) memcpy(result, _buffer, target_length * sizeof(char_t));
+
+                               // append second string to the new buffer
+                               memcpy(result + target_length, o._buffer, source_length * sizeof(char_t));
+                               result[result_length] = 0;
+
+                               // finalize
+                               _buffer = result;
+                               _uses_heap = true;
+                               _length_heap = result_length;
+                       }
+               }
+
+               const char_t* c_str() const
+               {
+                       return _buffer;
+               }
+
+               size_t length() const
+               {
+                       return _uses_heap ? _length_heap : strlength(_buffer);
+               }
+
+               char_t* data(xpath_allocator* alloc)
+               {
+                       // make private heap copy
+                       if (!_uses_heap)
+                       {
+                               size_t length_ = strlength(_buffer);
+                               const char_t* data_ = duplicate_string(_buffer, length_, alloc);
+
+                               if (!data_) return 0;
+
+                               _buffer = data_;
+                               _uses_heap = true;
+                               _length_heap = length_;
+                       }
+
+                       return const_cast<char_t*>(_buffer);
+               }
+
+               bool empty() const
+               {
+                       return *_buffer == 0;
+               }
+
+               bool operator==(const xpath_string& o) const
+               {
+                       return strequal(_buffer, o._buffer);
+               }
+
+               bool operator!=(const xpath_string& o) const
+               {
+                       return !strequal(_buffer, o._buffer);
+               }
+
+               bool uses_heap() const
+               {
+                       return _uses_heap;
+               }
+       };
+PUGI__NS_END
+
+PUGI__NS_BEGIN
+       PUGI__FN bool starts_with(const char_t* string, const char_t* pattern)
+       {
+               while (*pattern && *string == *pattern)
+               {
+                       string++;
+                       pattern++;
+               }
+
+               return *pattern == 0;
+       }
+
+       PUGI__FN const char_t* find_char(const char_t* s, char_t c)
+       {
+       #ifdef PUGIXML_WCHAR_MODE
+               return wcschr(s, c);
+       #else
+               return strchr(s, c);
+       #endif
+       }
+
+       PUGI__FN const char_t* find_substring(const char_t* s, const char_t* p)
+       {
+       #ifdef PUGIXML_WCHAR_MODE
+               // MSVC6 wcsstr bug workaround (if s is empty it always returns 0)
+               return (*p == 0) ? s : wcsstr(s, p);
+       #else
+               return strstr(s, p);
+       #endif
+       }
+
+       // Converts symbol to lower case, if it is an ASCII one
+       PUGI__FN char_t tolower_ascii(char_t ch)
+       {
+               return static_cast<unsigned int>(ch - 'A') < 26 ? static_cast<char_t>(ch | ' ') : ch;
+       }
+
+       PUGI__FN xpath_string string_value(const xpath_node& na, xpath_allocator* alloc)
+       {
+               if (na.attribute())
+                       return xpath_string::from_const(na.attribute().value());
+               else
+               {
+                       xml_node n = na.node();
+
+                       switch (n.type())
+                       {
+                       case node_pcdata:
+                       case node_cdata:
+                       case node_comment:
+                       case node_pi:
+                               return xpath_string::from_const(n.value());
+
+                       case node_document:
+                       case node_element:
+                       {
+                               xpath_string result;
+
+                               // element nodes can have value if parse_embed_pcdata was used
+                               if (n.value()[0])
+                                       result.append(xpath_string::from_const(n.value()), alloc);
+
+                               xml_node cur = n.first_child();
+
+                               while (cur && cur != n)
+                               {
+                                       if (cur.type() == node_pcdata || cur.type() == node_cdata)
+                                               result.append(xpath_string::from_const(cur.value()), alloc);
+
+                                       if (cur.first_child())
+                                               cur = cur.first_child();
+                                       else if (cur.next_sibling())
+                                               cur = cur.next_sibling();
+                                       else
+                                       {
+                                               while (!cur.next_sibling() && cur != n)
+                                                       cur = cur.parent();
+
+                                               if (cur != n) cur = cur.next_sibling();
+                                       }
+                               }
+
+                               return result;
+                       }
+
+                       default:
+                               return xpath_string();
+                       }
+               }
+       }
+
+       PUGI__FN bool node_is_before_sibling(xml_node_struct* ln, xml_node_struct* rn)
+       {
+               assert(ln->parent == rn->parent);
+
+               // there is no common ancestor (the shared parent is null), nodes are from different documents
+               if (!ln->parent) return ln < rn;
+
+               // determine sibling order
+               xml_node_struct* ls = ln;
+               xml_node_struct* rs = rn;
+
+               while (ls && rs)
+               {
+                       if (ls == rn) return true;
+                       if (rs == ln) return false;
+
+                       ls = ls->next_sibling;
+                       rs = rs->next_sibling;
+               }
+
+               // if rn sibling chain ended ln must be before rn
+               return !rs;
+       }
+
+       PUGI__FN bool node_is_before(xml_node_struct* ln, xml_node_struct* rn)
+       {
+               // find common ancestor at the same depth, if any
+               xml_node_struct* lp = ln;
+               xml_node_struct* rp = rn;
+
+               while (lp && rp && lp->parent != rp->parent)
+               {
+                       lp = lp->parent;
+                       rp = rp->parent;
+               }
+
+               // parents are the same!
+               if (lp && rp) return node_is_before_sibling(lp, rp);
+
+               // nodes are at different depths, need to normalize heights
+               bool left_higher = !lp;
+
+               while (lp)
+               {
+                       lp = lp->parent;
+                       ln = ln->parent;
+               }
+
+               while (rp)
+               {
+                       rp = rp->parent;
+                       rn = rn->parent;
+               }
+
+               // one node is the ancestor of the other
+               if (ln == rn) return left_higher;
+
+               // find common ancestor... again
+               while (ln->parent != rn->parent)
+               {
+                       ln = ln->parent;
+                       rn = rn->parent;
+               }
+
+               return node_is_before_sibling(ln, rn);
+       }
+
+       PUGI__FN bool node_is_ancestor(xml_node_struct* parent, xml_node_struct* node)
+       {
+               while (node && node != parent) node = node->parent;
+
+               return parent && node == parent;
+       }
+
+       PUGI__FN const void* document_buffer_order(const xpath_node& xnode)
+       {
+               xml_node_struct* node = xnode.node().internal_object();
+
+               if (node)
+               {
+                       if ((get_document(node).header & xml_memory_page_contents_shared_mask) == 0)
+                       {
+                               if (node->name && (node->header & impl::xml_memory_page_name_allocated_or_shared_mask) == 0) return node->name;
+                               if (node->value && (node->header & impl::xml_memory_page_value_allocated_or_shared_mask) == 0) return node->value;
+                       }
+
+                       return 0;
+               }
+
+               xml_attribute_struct* attr = xnode.attribute().internal_object();
+
+               if (attr)
+               {
+                       if ((get_document(attr).header & xml_memory_page_contents_shared_mask) == 0)
+                       {
+                               if ((attr->header & impl::xml_memory_page_name_allocated_or_shared_mask) == 0) return attr->name;
+                               if ((attr->header & impl::xml_memory_page_value_allocated_or_shared_mask) == 0) return attr->value;
+                       }
+
+                       return 0;
+               }
+
+               return 0;
+       }
+
+       struct document_order_comparator
+       {
+               bool operator()(const xpath_node& lhs, const xpath_node& rhs) const
+               {
+                       // optimized document order based check
+                       const void* lo = document_buffer_order(lhs);
+                       const void* ro = document_buffer_order(rhs);
+
+                       if (lo && ro) return lo < ro;
+
+                       // slow comparison
+                       xml_node ln = lhs.node(), rn = rhs.node();
+
+                       // compare attributes
+                       if (lhs.attribute() && rhs.attribute())
+                       {
+                               // shared parent
+                               if (lhs.parent() == rhs.parent())
+                               {
+                                       // determine sibling order
+                                       for (xml_attribute a = lhs.attribute(); a; a = a.next_attribute())
+                                               if (a == rhs.attribute())
+                                                       return true;
+
+                                       return false;
+                               }
+
+                               // compare attribute parents
+                               ln = lhs.parent();
+                               rn = rhs.parent();
+                       }
+                       else if (lhs.attribute())
+                       {
+                               // attributes go after the parent element
+                               if (lhs.parent() == rhs.node()) return false;
+
+                               ln = lhs.parent();
+                       }
+                       else if (rhs.attribute())
+                       {
+                               // attributes go after the parent element
+                               if (rhs.parent() == lhs.node()) return true;
+
+                               rn = rhs.parent();
+                       }
+
+                       if (ln == rn) return false;
+
+                       if (!ln || !rn) return ln < rn;
+
+                       return node_is_before(ln.internal_object(), rn.internal_object());
+               }
+       };
+
+       struct duplicate_comparator
+       {
+               bool operator()(const xpath_node& lhs, const xpath_node& rhs) const
+               {
+                       if (lhs.attribute()) return rhs.attribute() ? lhs.attribute() < rhs.attribute() : true;
+                       else return rhs.attribute() ? false : lhs.node() < rhs.node();
+               }
+       };
+
+       PUGI__FN double gen_nan()
+       {
+       #if defined(__STDC_IEC_559__) || ((FLT_RADIX - 0 == 2) && (FLT_MAX_EXP - 0 == 128) && (FLT_MANT_DIG - 0 == 24))
+               PUGI__STATIC_ASSERT(sizeof(float) == sizeof(uint32_t));
+               typedef uint32_t UI; // BCC5 workaround
+               union { float f; UI i; } u;
+               u.i = 0x7fc00000;
+               return u.f;
+       #else
+               // fallback
+               const volatile double zero = 0.0;
+               return zero / zero;
+       #endif
+       }
+
+       PUGI__FN bool is_nan(double value)
+       {
+       #if defined(PUGI__MSVC_CRT_VERSION) || defined(__BORLANDC__)
+               return !!_isnan(value);
+       #elif defined(fpclassify) && defined(FP_NAN)
+               return fpclassify(value) == FP_NAN;
+       #else
+               // fallback
+               const volatile double v = value;
+               return v != v;
+       #endif
+       }
+
+       PUGI__FN const char_t* convert_number_to_string_special(double value)
+       {
+       #if defined(PUGI__MSVC_CRT_VERSION) || defined(__BORLANDC__)
+               if (_finite(value)) return (value == 0) ? PUGIXML_TEXT("0") : 0;
+               if (_isnan(value)) return PUGIXML_TEXT("NaN");
+               return value > 0 ? PUGIXML_TEXT("Infinity") : PUGIXML_TEXT("-Infinity");
+       #elif defined(fpclassify) && defined(FP_NAN) && defined(FP_INFINITE) && defined(FP_ZERO)
+               switch (fpclassify(value))
+               {
+               case FP_NAN:
+                       return PUGIXML_TEXT("NaN");
+
+               case FP_INFINITE:
+                       return value > 0 ? PUGIXML_TEXT("Infinity") : PUGIXML_TEXT("-Infinity");
+
+               case FP_ZERO:
+                       return PUGIXML_TEXT("0");
+
+               default:
+                       return 0;
+               }
+       #else
+               // fallback
+               const volatile double v = value;
+
+               if (v == 0) return PUGIXML_TEXT("0");
+               if (v != v) return PUGIXML_TEXT("NaN");
+               if (v * 2 == v) return value > 0 ? PUGIXML_TEXT("Infinity") : PUGIXML_TEXT("-Infinity");
+               return 0;
+       #endif
+       }
+
+       PUGI__FN bool convert_number_to_boolean(double value)
+       {
+               return (value != 0 && !is_nan(value));
+       }
+
+       PUGI__FN void truncate_zeros(char* begin, char* end)
+       {
+               while (begin != end && end[-1] == '0') end--;
+
+               *end = 0;
+       }
+
+       // gets mantissa digits in the form of 0.xxxxx with 0. implied and the exponent
+#if defined(PUGI__MSVC_CRT_VERSION) && PUGI__MSVC_CRT_VERSION >= 1400 && !defined(_WIN32_WCE)
+       PUGI__FN void convert_number_to_mantissa_exponent(double value, char (&buffer)[32], char** out_mantissa, int* out_exponent)
+       {
+               // get base values
+               int sign, exponent;
+               _ecvt_s(buffer, sizeof(buffer), value, DBL_DIG + 1, &exponent, &sign);
+
+               // truncate redundant zeros
+               truncate_zeros(buffer, buffer + strlen(buffer));
+
+               // fill results
+               *out_mantissa = buffer;
+               *out_exponent = exponent;
+       }
+#else
+       PUGI__FN void convert_number_to_mantissa_exponent(double value, char (&buffer)[32], char** out_mantissa, int* out_exponent)
+       {
+               // get a scientific notation value with IEEE DBL_DIG decimals
+               PUGI__SNPRINTF(buffer, "%.*e", DBL_DIG, value);
+
+               // get the exponent (possibly negative)
+               char* exponent_string = strchr(buffer, 'e');
+               assert(exponent_string);
+
+               int exponent = atoi(exponent_string + 1);
+
+               // extract mantissa string: skip sign
+               char* mantissa = buffer[0] == '-' ? buffer + 1 : buffer;
+               assert(mantissa[0] != '0' && mantissa[1] == '.');
+
+               // divide mantissa by 10 to eliminate integer part
+               mantissa[1] = mantissa[0];
+               mantissa++;
+               exponent++;
+
+               // remove extra mantissa digits and zero-terminate mantissa
+               truncate_zeros(mantissa, exponent_string);
+
+               // fill results
+               *out_mantissa = mantissa;
+               *out_exponent = exponent;
+       }
+#endif
+
+       PUGI__FN xpath_string convert_number_to_string(double value, xpath_allocator* alloc)
+       {
+               // try special number conversion
+               const char_t* special = convert_number_to_string_special(value);
+               if (special) return xpath_string::from_const(special);
+
+               // get mantissa + exponent form
+               char mantissa_buffer[32];
+
+               char* mantissa;
+               int exponent;
+               convert_number_to_mantissa_exponent(value, mantissa_buffer, &mantissa, &exponent);
+
+               // allocate a buffer of suitable length for the number
+               size_t result_size = strlen(mantissa_buffer) + (exponent > 0 ? exponent : -exponent) + 4;
+               char_t* result = static_cast<char_t*>(alloc->allocate(sizeof(char_t) * result_size));
+               if (!result) return xpath_string();
+
+               // make the number!
+               char_t* s = result;
+
+               // sign
+               if (value < 0) *s++ = '-';
+
+               // integer part
+               if (exponent <= 0)
+               {
+                       *s++ = '0';
+               }
+               else
+               {
+                       while (exponent > 0)
+                       {
+                               assert(*mantissa == 0 || static_cast<unsigned int>(*mantissa - '0') <= 9);
+                               *s++ = *mantissa ? *mantissa++ : '0';
+                               exponent--;
+                       }
+               }
+
+               // fractional part
+               if (*mantissa)
+               {
+                       // decimal point
+                       *s++ = '.';
+
+                       // extra zeroes from negative exponent
+                       while (exponent < 0)
+                       {
+                               *s++ = '0';
+                               exponent++;
+                       }
+
+                       // extra mantissa digits
+                       while (*mantissa)
+                       {
+                               assert(static_cast<unsigned int>(*mantissa - '0') <= 9);
+                               *s++ = *mantissa++;
+                       }
+               }
+
+               // zero-terminate
+               assert(s < result + result_size);
+               *s = 0;
+
+               return xpath_string::from_heap_preallocated(result, s);
+       }
+
+       PUGI__FN bool check_string_to_number_format(const char_t* string)
+       {
+               // parse leading whitespace
+               while (PUGI__IS_CHARTYPE(*string, ct_space)) ++string;
+
+               // parse sign
+               if (*string == '-') ++string;
+
+               if (!*string) return false;
+
+               // if there is no integer part, there should be a decimal part with at least one digit
+               if (!PUGI__IS_CHARTYPEX(string[0], ctx_digit) && (string[0] != '.' || !PUGI__IS_CHARTYPEX(string[1], ctx_digit))) return false;
+
+               // parse integer part
+               while (PUGI__IS_CHARTYPEX(*string, ctx_digit)) ++string;
+
+               // parse decimal part
+               if (*string == '.')
+               {
+                       ++string;
+
+                       while (PUGI__IS_CHARTYPEX(*string, ctx_digit)) ++string;
+               }
+
+               // parse trailing whitespace
+               while (PUGI__IS_CHARTYPE(*string, ct_space)) ++string;
+
+               return *string == 0;
+       }
+
+       PUGI__FN double convert_string_to_number(const char_t* string)
+       {
+               // check string format
+               if (!check_string_to_number_format(string)) return gen_nan();
+
+               // parse string
+       #ifdef PUGIXML_WCHAR_MODE
+               return wcstod(string, 0);
+       #else
+               return strtod(string, 0);
+       #endif
+       }
+
+       PUGI__FN bool convert_string_to_number_scratch(char_t (&buffer)[32], const char_t* begin, const char_t* end, double* out_result)
+       {
+               size_t length = static_cast<size_t>(end - begin);
+               char_t* scratch = buffer;
+
+               if (length >= sizeof(buffer) / sizeof(buffer[0]))
+               {
+                       // need to make dummy on-heap copy
+                       scratch = static_cast<char_t*>(xml_memory::allocate((length + 1) * sizeof(char_t)));
+                       if (!scratch) return false;
+               }
+
+               // copy string to zero-terminated buffer and perform conversion
+               memcpy(scratch, begin, length * sizeof(char_t));
+               scratch[length] = 0;
+
+               *out_result = convert_string_to_number(scratch);
+
+               // free dummy buffer
+               if (scratch != buffer) xml_memory::deallocate(scratch);
+
+               return true;
+       }
+
+       PUGI__FN double round_nearest(double value)
+       {
+               return floor(value + 0.5);
+       }
+
+       PUGI__FN double round_nearest_nzero(double value)
+       {
+               // same as round_nearest, but returns -0 for [-0.5, -0]
+               // ceil is used to differentiate between +0 and -0 (we return -0 for [-0.5, -0] and +0 for +0)
+               return (value >= -0.5 && value <= 0) ? ceil(value) : floor(value + 0.5);
+       }
+
+       PUGI__FN const char_t* qualified_name(const xpath_node& node)
+       {
+               return node.attribute() ? node.attribute().name() : node.node().name();
+       }
+
+       PUGI__FN const char_t* local_name(const xpath_node& node)
+       {
+               const char_t* name = qualified_name(node);
+               const char_t* p = find_char(name, ':');
+
+               return p ? p + 1 : name;
+       }
+
+       struct namespace_uri_predicate
+       {
+               const char_t* prefix;
+               size_t prefix_length;
+
+               namespace_uri_predicate(const char_t* name)
+               {
+                       const char_t* pos = find_char(name, ':');
+
+                       prefix = pos ? name : 0;
+                       prefix_length = pos ? static_cast<size_t>(pos - name) : 0;
+               }
+
+               bool operator()(xml_attribute a) const
+               {
+                       const char_t* name = a.name();
+
+                       if (!starts_with(name, PUGIXML_TEXT("xmlns"))) return false;
+
+                       return prefix ? name[5] == ':' && strequalrange(name + 6, prefix, prefix_length) : name[5] == 0;
+               }
+       };
+
+       PUGI__FN const char_t* namespace_uri(xml_node node)
+       {
+               namespace_uri_predicate pred = node.name();
+
+               xml_node p = node;
+
+               while (p)
+               {
+                       xml_attribute a = p.find_attribute(pred);
+
+                       if (a) return a.value();
+
+                       p = p.parent();
+               }
+
+               return PUGIXML_TEXT("");
+       }
+
+       PUGI__FN const char_t* namespace_uri(xml_attribute attr, xml_node parent)
+       {
+               namespace_uri_predicate pred = attr.name();
+
+               // Default namespace does not apply to attributes
+               if (!pred.prefix) return PUGIXML_TEXT("");
+
+               xml_node p = parent;
+
+               while (p)
+               {
+                       xml_attribute a = p.find_attribute(pred);
+
+                       if (a) return a.value();
+
+                       p = p.parent();
+               }
+
+               return PUGIXML_TEXT("");
+       }
+
+       PUGI__FN const char_t* namespace_uri(const xpath_node& node)
+       {
+               return node.attribute() ? namespace_uri(node.attribute(), node.parent()) : namespace_uri(node.node());
+       }
+
+       PUGI__FN char_t* normalize_space(char_t* buffer)
+       {
+               char_t* write = buffer;
+
+               for (char_t* it = buffer; *it; )
+               {
+                       char_t ch = *it++;
+
+                       if (PUGI__IS_CHARTYPE(ch, ct_space))
+                       {
+                               // replace whitespace sequence with single space
+                               while (PUGI__IS_CHARTYPE(*it, ct_space)) it++;
+
+                               // avoid leading spaces
+                               if (write != buffer) *write++ = ' ';
+                       }
+                       else *write++ = ch;
+               }
+
+               // remove trailing space
+               if (write != buffer && PUGI__IS_CHARTYPE(write[-1], ct_space)) write--;
+
+               // zero-terminate
+               *write = 0;
+
+               return write;
+       }
+
+       PUGI__FN char_t* translate(char_t* buffer, const char_t* from, const char_t* to, size_t to_length)
+       {
+               char_t* write = buffer;
+
+               while (*buffer)
+               {
+                       PUGI__DMC_VOLATILE char_t ch = *buffer++;
+
+                       const char_t* pos = find_char(from, ch);
+
+                       if (!pos)
+                               *write++ = ch; // do not process
+                       else if (static_cast<size_t>(pos - from) < to_length)
+                               *write++ = to[pos - from]; // replace
+               }
+
+               // zero-terminate
+               *write = 0;
+
+               return write;
+       }
+
+       PUGI__FN unsigned char* translate_table_generate(xpath_allocator* alloc, const char_t* from, const char_t* to)
+       {
+               unsigned char table[128] = {0};
+
+               while (*from)
+               {
+                       unsigned int fc = static_cast<unsigned int>(*from);
+                       unsigned int tc = static_cast<unsigned int>(*to);
+
+                       if (fc >= 128 || tc >= 128)
+                               return 0;
+
+                       // code=128 means "skip character"
+                       if (!table[fc])
+                               table[fc] = static_cast<unsigned char>(tc ? tc : 128);
+
+                       from++;
+                       if (tc) to++;
+               }
+
+               for (int i = 0; i < 128; ++i)
+                       if (!table[i])
+                               table[i] = static_cast<unsigned char>(i);
+
+               void* result = alloc->allocate(sizeof(table));
+               if (!result) return 0;
+
+               memcpy(result, table, sizeof(table));
+
+               return static_cast<unsigned char*>(result);
+       }
+
+       PUGI__FN char_t* translate_table(char_t* buffer, const unsigned char* table)
+       {
+               char_t* write = buffer;
+
+               while (*buffer)
+               {
+                       char_t ch = *buffer++;
+                       unsigned int index = static_cast<unsigned int>(ch);
+
+                       if (index < 128)
+                       {
+                               unsigned char code = table[index];
+
+                               // code=128 means "skip character" (table size is 128 so 128 can be a special value)
+                               // this code skips these characters without extra branches
+                               *write = static_cast<char_t>(code);
+                               write += 1 - (code >> 7);
+                       }
+                       else
+                       {
+                               *write++ = ch;
+                       }
+               }
+
+               // zero-terminate
+               *write = 0;
+
+               return write;
+       }
+
+       inline bool is_xpath_attribute(const char_t* name)
+       {
+               return !(starts_with(name, PUGIXML_TEXT("xmlns")) && (name[5] == 0 || name[5] == ':'));
+       }
+
+       struct xpath_variable_boolean: xpath_variable
+       {
+               xpath_variable_boolean(): xpath_variable(xpath_type_boolean), value(false)
+               {
+               }
+
+               bool value;
+               char_t name[1];
+       };
+
+       struct xpath_variable_number: xpath_variable
+       {
+               xpath_variable_number(): xpath_variable(xpath_type_number), value(0)
+               {
+               }
+
+               double value;
+               char_t name[1];
+       };
+
+       struct xpath_variable_string: xpath_variable
+       {
+               xpath_variable_string(): xpath_variable(xpath_type_string), value(0)
+               {
+               }
+
+               ~xpath_variable_string()
+               {
+                       if (value) xml_memory::deallocate(value);
+               }
+
+               char_t* value;
+               char_t name[1];
+       };
+
+       struct xpath_variable_node_set: xpath_variable
+       {
+               xpath_variable_node_set(): xpath_variable(xpath_type_node_set)
+               {
+               }
+
+               xpath_node_set value;
+               char_t name[1];
+       };
+
+       static const xpath_node_set dummy_node_set;
+
+       PUGI__FN PUGI__UNSIGNED_OVERFLOW unsigned int hash_string(const char_t* str)
+       {
+               // Jenkins one-at-a-time hash (http://en.wikipedia.org/wiki/Jenkins_hash_function#one-at-a-time)
+               unsigned int result = 0;
+
+               while (*str)
+               {
+                       result += static_cast<unsigned int>(*str++);
+                       result += result << 10;
+                       result ^= result >> 6;
+               }
+
+               result += result << 3;
+               result ^= result >> 11;
+               result += result << 15;
+
+               return result;
+       }
+
+       template <typename T> PUGI__FN T* new_xpath_variable(const char_t* name)
+       {
+               size_t length = strlength(name);
+               if (length == 0) return 0; // empty variable names are invalid
+
+               // $$ we can't use offsetof(T, name) because T is non-POD, so we just allocate additional length characters
+               void* memory = xml_memory::allocate(sizeof(T) + length * sizeof(char_t));
+               if (!memory) return 0;
+
+               T* result = new (memory) T();
+
+               memcpy(result->name, name, (length + 1) * sizeof(char_t));
+
+               return result;
+       }
+
+       PUGI__FN xpath_variable* new_xpath_variable(xpath_value_type type, const char_t* name)
+       {
+               switch (type)
+               {
+               case xpath_type_node_set:
+                       return new_xpath_variable<xpath_variable_node_set>(name);
+
+               case xpath_type_number:
+                       return new_xpath_variable<xpath_variable_number>(name);
+
+               case xpath_type_string:
+                       return new_xpath_variable<xpath_variable_string>(name);
+
+               case xpath_type_boolean:
+                       return new_xpath_variable<xpath_variable_boolean>(name);
+
+               default:
+                       return 0;
+               }
+       }
+
+       template <typename T> PUGI__FN void delete_xpath_variable(T* var)
+       {
+               var->~T();
+               xml_memory::deallocate(var);
+       }
+
+       PUGI__FN void delete_xpath_variable(xpath_value_type type, xpath_variable* var)
+       {
+               switch (type)
+               {
+               case xpath_type_node_set:
+                       delete_xpath_variable(static_cast<xpath_variable_node_set*>(var));
+                       break;
+
+               case xpath_type_number:
+                       delete_xpath_variable(static_cast<xpath_variable_number*>(var));
+                       break;
+
+               case xpath_type_string:
+                       delete_xpath_variable(static_cast<xpath_variable_string*>(var));
+                       break;
+
+               case xpath_type_boolean:
+                       delete_xpath_variable(static_cast<xpath_variable_boolean*>(var));
+                       break;
+
+               default:
+                       assert(false && "Invalid variable type"); // unreachable
+               }
+       }
+
+       PUGI__FN bool copy_xpath_variable(xpath_variable* lhs, const xpath_variable* rhs)
+       {
+               switch (rhs->type())
+               {
+               case xpath_type_node_set:
+                       return lhs->set(static_cast<const xpath_variable_node_set*>(rhs)->value);
+
+               case xpath_type_number:
+                       return lhs->set(static_cast<const xpath_variable_number*>(rhs)->value);
+
+               case xpath_type_string:
+                       return lhs->set(static_cast<const xpath_variable_string*>(rhs)->value);
+
+               case xpath_type_boolean:
+                       return lhs->set(static_cast<const xpath_variable_boolean*>(rhs)->value);
+
+               default:
+                       assert(false && "Invalid variable type"); // unreachable
+                       return false;
+               }
+       }
+
+       PUGI__FN bool get_variable_scratch(char_t (&buffer)[32], xpath_variable_set* set, const char_t* begin, const char_t* end, xpath_variable** out_result)
+       {
+               size_t length = static_cast<size_t>(end - begin);
+               char_t* scratch = buffer;
+
+               if (length >= sizeof(buffer) / sizeof(buffer[0]))
+               {
+                       // need to make dummy on-heap copy
+                       scratch = static_cast<char_t*>(xml_memory::allocate((length + 1) * sizeof(char_t)));
+                       if (!scratch) return false;
+               }
+
+               // copy string to zero-terminated buffer and perform lookup
+               memcpy(scratch, begin, length * sizeof(char_t));
+               scratch[length] = 0;
+
+               *out_result = set->get(scratch);
+
+               // free dummy buffer
+               if (scratch != buffer) xml_memory::deallocate(scratch);
+
+               return true;
+       }
+PUGI__NS_END
+
+// Internal node set class
+PUGI__NS_BEGIN
+       PUGI__FN xpath_node_set::type_t xpath_get_order(const xpath_node* begin, const xpath_node* end)
+       {
+               if (end - begin < 2)
+                       return xpath_node_set::type_sorted;
+
+               document_order_comparator cmp;
+
+               bool first = cmp(begin[0], begin[1]);
+
+               for (const xpath_node* it = begin + 1; it + 1 < end; ++it)
+                       if (cmp(it[0], it[1]) != first)
+                               return xpath_node_set::type_unsorted;
+
+               return first ? xpath_node_set::type_sorted : xpath_node_set::type_sorted_reverse;
+       }
+
+       PUGI__FN xpath_node_set::type_t xpath_sort(xpath_node* begin, xpath_node* end, xpath_node_set::type_t type, bool rev)
+       {
+               xpath_node_set::type_t order = rev ? xpath_node_set::type_sorted_reverse : xpath_node_set::type_sorted;
+
+               if (type == xpath_node_set::type_unsorted)
+               {
+                       xpath_node_set::type_t sorted = xpath_get_order(begin, end);
+
+                       if (sorted == xpath_node_set::type_unsorted)
+                       {
+                               sort(begin, end, document_order_comparator());
+
+                               type = xpath_node_set::type_sorted;
+                       }
+                       else
+                               type = sorted;
+               }
+
+               if (type != order) reverse(begin, end);
+
+               return order;
+       }
+
+       PUGI__FN xpath_node xpath_first(const xpath_node* begin, const xpath_node* end, xpath_node_set::type_t type)
+       {
+               if (begin == end) return xpath_node();
+
+               switch (type)
+               {
+               case xpath_node_set::type_sorted:
+                       return *begin;
+
+               case xpath_node_set::type_sorted_reverse:
+                       return *(end - 1);
+
+               case xpath_node_set::type_unsorted:
+                       return *min_element(begin, end, document_order_comparator());
+
+               default:
+                       assert(false && "Invalid node set type"); // unreachable
+                       return xpath_node();
+               }
+       }
+
+       class xpath_node_set_raw
+       {
+               xpath_node_set::type_t _type;
+
+               xpath_node* _begin;
+               xpath_node* _end;
+               xpath_node* _eos;
+
+       public:
+               xpath_node_set_raw(): _type(xpath_node_set::type_unsorted), _begin(0), _end(0), _eos(0)
+               {
+               }
+
+               xpath_node* begin() const
+               {
+                       return _begin;
+               }
+
+               xpath_node* end() const
+               {
+                       return _end;
+               }
+
+               bool empty() const
+               {
+                       return _begin == _end;
+               }
+
+               size_t size() const
+               {
+                       return static_cast<size_t>(_end - _begin);
+               }
+
+               xpath_node first() const
+               {
+                       return xpath_first(_begin, _end, _type);
+               }
+
+               void push_back_grow(const xpath_node& node, xpath_allocator* alloc);
+
+               void push_back(const xpath_node& node, xpath_allocator* alloc)
+               {
+                       if (_end != _eos)
+                               *_end++ = node;
+                       else
+                               push_back_grow(node, alloc);
+               }
+
+               void append(const xpath_node* begin_, const xpath_node* end_, xpath_allocator* alloc)
+               {
+                       if (begin_ == end_) return;
+
+                       size_t size_ = static_cast<size_t>(_end - _begin);
+                       size_t capacity = static_cast<size_t>(_eos - _begin);
+                       size_t count = static_cast<size_t>(end_ - begin_);
+
+                       if (size_ + count > capacity)
+                       {
+                               // reallocate the old array or allocate a new one
+                               xpath_node* data = static_cast<xpath_node*>(alloc->reallocate(_begin, capacity * sizeof(xpath_node), (size_ + count) * sizeof(xpath_node)));
+                               if (!data) return;
+
+                               // finalize
+                               _begin = data;
+                               _end = data + size_;
+                               _eos = data + size_ + count;
+                       }
+
+                       memcpy(_end, begin_, count * sizeof(xpath_node));
+                       _end += count;
+               }
+
+               void sort_do()
+               {
+                       _type = xpath_sort(_begin, _end, _type, false);
+               }
+
+               void truncate(xpath_node* pos)
+               {
+                       assert(_begin <= pos && pos <= _end);
+
+                       _end = pos;
+               }
+
+               void remove_duplicates()
+               {
+                       if (_type == xpath_node_set::type_unsorted)
+                               sort(_begin, _end, duplicate_comparator());
+
+                       _end = unique(_begin, _end);
+               }
+
+               xpath_node_set::type_t type() const
+               {
+                       return _type;
+               }
+
+               void set_type(xpath_node_set::type_t value)
+               {
+                       _type = value;
+               }
+       };
+
+       PUGI__FN_NO_INLINE void xpath_node_set_raw::push_back_grow(const xpath_node& node, xpath_allocator* alloc)
+       {
+               size_t capacity = static_cast<size_t>(_eos - _begin);
+
+               // get new capacity (1.5x rule)
+               size_t new_capacity = capacity + capacity / 2 + 1;
+
+               // reallocate the old array or allocate a new one
+               xpath_node* data = static_cast<xpath_node*>(alloc->reallocate(_begin, capacity * sizeof(xpath_node), new_capacity * sizeof(xpath_node)));
+               if (!data) return;
+
+               // finalize
+               _begin = data;
+               _end = data + capacity;
+               _eos = data + new_capacity;
+
+               // push
+               *_end++ = node;
+       }
+PUGI__NS_END
+
+PUGI__NS_BEGIN
+       struct xpath_context
+       {
+               xpath_node n;
+               size_t position, size;
+
+               xpath_context(const xpath_node& n_, size_t position_, size_t size_): n(n_), position(position_), size(size_)
+               {
+               }
+       };
+
+       enum lexeme_t
+       {
+               lex_none = 0,
+               lex_equal,
+               lex_not_equal,
+               lex_less,
+               lex_greater,
+               lex_less_or_equal,
+               lex_greater_or_equal,
+               lex_plus,
+               lex_minus,
+               lex_multiply,
+               lex_union,
+               lex_var_ref,
+               lex_open_brace,
+               lex_close_brace,
+               lex_quoted_string,
+               lex_number,
+               lex_slash,
+               lex_double_slash,
+               lex_open_square_brace,
+               lex_close_square_brace,
+               lex_string,
+               lex_comma,
+               lex_axis_attribute,
+               lex_dot,
+               lex_double_dot,
+               lex_double_colon,
+               lex_eof
+       };
+
+       struct xpath_lexer_string
+       {
+               const char_t* begin;
+               const char_t* end;
+
+               xpath_lexer_string(): begin(0), end(0)
+               {
+               }
+
+               bool operator==(const char_t* other) const
+               {
+                       size_t length = static_cast<size_t>(end - begin);
+
+                       return strequalrange(other, begin, length);
+               }
+       };
+
+       class xpath_lexer
+       {
+               const char_t* _cur;
+               const char_t* _cur_lexeme_pos;
+               xpath_lexer_string _cur_lexeme_contents;
+
+               lexeme_t _cur_lexeme;
+
+       public:
+               explicit xpath_lexer(const char_t* query): _cur(query)
+               {
+                       next();
+               }
+
+               const char_t* state() const
+               {
+                       return _cur;
+               }
+
+               void next()
+               {
+                       const char_t* cur = _cur;
+
+                       while (PUGI__IS_CHARTYPE(*cur, ct_space)) ++cur;
+
+                       // save lexeme position for error reporting
+                       _cur_lexeme_pos = cur;
+
+                       switch (*cur)
+                       {
+                       case 0:
+                               _cur_lexeme = lex_eof;
+                               break;
+
+                       case '>':
+                               if (*(cur+1) == '=')
+                               {
+                                       cur += 2;
+                                       _cur_lexeme = lex_greater_or_equal;
+                               }
+                               else
+                               {
+                                       cur += 1;
+                                       _cur_lexeme = lex_greater;
+                               }
+                               break;
+
+                       case '<':
+                               if (*(cur+1) == '=')
+                               {
+                                       cur += 2;
+                                       _cur_lexeme = lex_less_or_equal;
+                               }
+                               else
+                               {
+                                       cur += 1;
+                                       _cur_lexeme = lex_less;
+                               }
+                               break;
+
+                       case '!':
+                               if (*(cur+1) == '=')
+                               {
+                                       cur += 2;
+                                       _cur_lexeme = lex_not_equal;
+                               }
+                               else
+                               {
+                                       _cur_lexeme = lex_none;
+                               }
+                               break;
+
+                       case '=':
+                               cur += 1;
+                               _cur_lexeme = lex_equal;
+
+                               break;
+
+                       case '+':
+                               cur += 1;
+                               _cur_lexeme = lex_plus;
+
+                               break;
+
+                       case '-':
+                               cur += 1;
+                               _cur_lexeme = lex_minus;
+
+                               break;
+
+                       case '*':
+                               cur += 1;
+                               _cur_lexeme = lex_multiply;
+
+                               break;
+
+                       case '|':
+                               cur += 1;
+                               _cur_lexeme = lex_union;
+
+                               break;
+
+                       case '$':
+                               cur += 1;
+
+                               if (PUGI__IS_CHARTYPEX(*cur, ctx_start_symbol))
+                               {
+                                       _cur_lexeme_contents.begin = cur;
+
+                                       while (PUGI__IS_CHARTYPEX(*cur, ctx_symbol)) cur++;
+
+                                       if (cur[0] == ':' && PUGI__IS_CHARTYPEX(cur[1], ctx_symbol)) // qname
+                                       {
+                                               cur++; // :
+
+                                               while (PUGI__IS_CHARTYPEX(*cur, ctx_symbol)) cur++;
+                                       }
+
+                                       _cur_lexeme_contents.end = cur;
+
+                                       _cur_lexeme = lex_var_ref;
+                               }
+                               else
+                               {
+                                       _cur_lexeme = lex_none;
+                               }
+
+                               break;
+
+                       case '(':
+                               cur += 1;
+                               _cur_lexeme = lex_open_brace;
+
+                               break;
+
+                       case ')':
+                               cur += 1;
+                               _cur_lexeme = lex_close_brace;
+
+                               break;
+
+                       case '[':
+                               cur += 1;
+                               _cur_lexeme = lex_open_square_brace;
+
+                               break;
+
+                       case ']':
+                               cur += 1;
+                               _cur_lexeme = lex_close_square_brace;
+
+                               break;
+
+                       case ',':
+                               cur += 1;
+                               _cur_lexeme = lex_comma;
+
+                               break;
+
+                       case '/':
+                               if (*(cur+1) == '/')
+                               {
+                                       cur += 2;
+                                       _cur_lexeme = lex_double_slash;
+                               }
+                               else
+                               {
+                                       cur += 1;
+                                       _cur_lexeme = lex_slash;
+                               }
+                               break;
+
+                       case '.':
+                               if (*(cur+1) == '.')
+                               {
+                                       cur += 2;
+                                       _cur_lexeme = lex_double_dot;
+                               }
+                               else if (PUGI__IS_CHARTYPEX(*(cur+1), ctx_digit))
+                               {
+                                       _cur_lexeme_contents.begin = cur; // .
+
+                                       ++cur;
+
+                                       while (PUGI__IS_CHARTYPEX(*cur, ctx_digit)) cur++;
+
+                                       _cur_lexeme_contents.end = cur;
+
+                                       _cur_lexeme = lex_number;
+                               }
+                               else
+                               {
+                                       cur += 1;
+                                       _cur_lexeme = lex_dot;
+                               }
+                               break;
+
+                       case '@':
+                               cur += 1;
+                               _cur_lexeme = lex_axis_attribute;
+
+                               break;
+
+                       case '"':
+                       case '\'':
+                       {
+                               char_t terminator = *cur;
+
+                               ++cur;
+
+                               _cur_lexeme_contents.begin = cur;
+                               while (*cur && *cur != terminator) cur++;
+                               _cur_lexeme_contents.end = cur;
+
+                               if (!*cur)
+                                       _cur_lexeme = lex_none;
+                               else
+                               {
+                                       cur += 1;
+                                       _cur_lexeme = lex_quoted_string;
+                               }
+
+                               break;
+                       }
+
+                       case ':':
+                               if (*(cur+1) == ':')
+                               {
+                                       cur += 2;
+                                       _cur_lexeme = lex_double_colon;
+                               }
+                               else
+                               {
+                                       _cur_lexeme = lex_none;
+                               }
+                               break;
+
+                       default:
+                               if (PUGI__IS_CHARTYPEX(*cur, ctx_digit))
+                               {
+                                       _cur_lexeme_contents.begin = cur;
+
+                                       while (PUGI__IS_CHARTYPEX(*cur, ctx_digit)) cur++;
+
+                                       if (*cur == '.')
+                                       {
+                                               cur++;
+
+                                               while (PUGI__IS_CHARTYPEX(*cur, ctx_digit)) cur++;
+                                       }
+
+                                       _cur_lexeme_contents.end = cur;
+
+                                       _cur_lexeme = lex_number;
+                               }
+                               else if (PUGI__IS_CHARTYPEX(*cur, ctx_start_symbol))
+                               {
+                                       _cur_lexeme_contents.begin = cur;
+
+                                       while (PUGI__IS_CHARTYPEX(*cur, ctx_symbol)) cur++;
+
+                                       if (cur[0] == ':')
+                                       {
+                                               if (cur[1] == '*') // namespace test ncname:*
+                                               {
+                                                       cur += 2; // :*
+                                               }
+                                               else if (PUGI__IS_CHARTYPEX(cur[1], ctx_symbol)) // namespace test qname
+                                               {
+                                                       cur++; // :
+
+                                                       while (PUGI__IS_CHARTYPEX(*cur, ctx_symbol)) cur++;
+                                               }
+                                       }
+
+                                       _cur_lexeme_contents.end = cur;
+
+                                       _cur_lexeme = lex_string;
+                               }
+                               else
+                               {
+                                       _cur_lexeme = lex_none;
+                               }
+                       }
+
+                       _cur = cur;
+               }
+
+               lexeme_t current() const
+               {
+                       return _cur_lexeme;
+               }
+
+               const char_t* current_pos() const
+               {
+                       return _cur_lexeme_pos;
+               }
+
+               const xpath_lexer_string& contents() const
+               {
+                       assert(_cur_lexeme == lex_var_ref || _cur_lexeme == lex_number || _cur_lexeme == lex_string || _cur_lexeme == lex_quoted_string);
+
+                       return _cur_lexeme_contents;
+               }
+       };
+
+       enum ast_type_t
+       {
+               ast_unknown,
+               ast_op_or,                                              // left or right
+               ast_op_and,                                             // left and right
+               ast_op_equal,                                   // left = right
+               ast_op_not_equal,                               // left != right
+               ast_op_less,                                    // left < right
+               ast_op_greater,                                 // left > right
+               ast_op_less_or_equal,                   // left <= right
+               ast_op_greater_or_equal,                // left >= right
+               ast_op_add,                                             // left + right
+               ast_op_subtract,                                // left - right
+               ast_op_multiply,                                // left * right
+               ast_op_divide,                                  // left / right
+               ast_op_mod,                                             // left % right
+               ast_op_negate,                                  // left - right
+               ast_op_union,                                   // left | right
+               ast_predicate,                                  // apply predicate to set; next points to next predicate
+               ast_filter,                                             // select * from left where right
+               ast_string_constant,                    // string constant
+               ast_number_constant,                    // number constant
+               ast_variable,                                   // variable
+               ast_func_last,                                  // last()
+               ast_func_position,                              // position()
+               ast_func_count,                                 // count(left)
+               ast_func_id,                                    // id(left)
+               ast_func_local_name_0,                  // local-name()
+               ast_func_local_name_1,                  // local-name(left)
+               ast_func_namespace_uri_0,               // namespace-uri()
+               ast_func_namespace_uri_1,               // namespace-uri(left)
+               ast_func_name_0,                                // name()
+               ast_func_name_1,                                // name(left)
+               ast_func_string_0,                              // string()
+               ast_func_string_1,                              // string(left)
+               ast_func_concat,                                // concat(left, right, siblings)
+               ast_func_starts_with,                   // starts_with(left, right)
+               ast_func_contains,                              // contains(left, right)
+               ast_func_substring_before,              // substring-before(left, right)
+               ast_func_substring_after,               // substring-after(left, right)
+               ast_func_substring_2,                   // substring(left, right)
+               ast_func_substring_3,                   // substring(left, right, third)
+               ast_func_string_length_0,               // string-length()
+               ast_func_string_length_1,               // string-length(left)
+               ast_func_normalize_space_0,             // normalize-space()
+               ast_func_normalize_space_1,             // normalize-space(left)
+               ast_func_translate,                             // translate(left, right, third)
+               ast_func_boolean,                               // boolean(left)
+               ast_func_not,                                   // not(left)
+               ast_func_true,                                  // true()
+               ast_func_false,                                 // false()
+               ast_func_lang,                                  // lang(left)
+               ast_func_number_0,                              // number()
+               ast_func_number_1,                              // number(left)
+               ast_func_sum,                                   // sum(left)
+               ast_func_floor,                                 // floor(left)
+               ast_func_ceiling,                               // ceiling(left)
+               ast_func_round,                                 // round(left)
+               ast_step,                                               // process set left with step
+               ast_step_root,                                  // select root node
+
+               ast_opt_translate_table,                // translate(left, right, third) where right/third are constants
+               ast_opt_compare_attribute               // @name = 'string'
+       };
+
+       enum axis_t
+       {
+               axis_ancestor,
+               axis_ancestor_or_self,
+               axis_attribute,
+               axis_child,
+               axis_descendant,
+               axis_descendant_or_self,
+               axis_following,
+               axis_following_sibling,
+               axis_namespace,
+               axis_parent,
+               axis_preceding,
+               axis_preceding_sibling,
+               axis_self
+       };
+
+       enum nodetest_t
+       {
+               nodetest_none,
+               nodetest_name,
+               nodetest_type_node,
+               nodetest_type_comment,
+               nodetest_type_pi,
+               nodetest_type_text,
+               nodetest_pi,
+               nodetest_all,
+               nodetest_all_in_namespace
+       };
+
+       enum predicate_t
+       {
+               predicate_default,
+               predicate_posinv,
+               predicate_constant,
+               predicate_constant_one
+       };
+
+       enum nodeset_eval_t
+       {
+               nodeset_eval_all,
+               nodeset_eval_any,
+               nodeset_eval_first
+       };
+
+       template <axis_t N> struct axis_to_type
+       {
+               static const axis_t axis;
+       };
+
+       template <axis_t N> const axis_t axis_to_type<N>::axis = N;
+
+       class xpath_ast_node
+       {
+       private:
+               // node type
+               char _type;
+               char _rettype;
+
+               // for ast_step
+               char _axis;
+
+               // for ast_step/ast_predicate/ast_filter
+               char _test;
+
+               // tree node structure
+               xpath_ast_node* _left;
+               xpath_ast_node* _right;
+               xpath_ast_node* _next;
+
+               union
+               {
+                       // value for ast_string_constant
+                       const char_t* string;
+                       // value for ast_number_constant
+                       double number;
+                       // variable for ast_variable
+                       xpath_variable* variable;
+                       // node test for ast_step (node name/namespace/node type/pi target)
+                       const char_t* nodetest;
+                       // table for ast_opt_translate_table
+                       const unsigned char* table;
+               } _data;
+
+               xpath_ast_node(const xpath_ast_node&);
+               xpath_ast_node& operator=(const xpath_ast_node&);
+
+               template <class Comp> static bool compare_eq(xpath_ast_node* lhs, xpath_ast_node* rhs, const xpath_context& c, const xpath_stack& stack, const Comp& comp)
+               {
+                       xpath_value_type lt = lhs->rettype(), rt = rhs->rettype();
+
+                       if (lt != xpath_type_node_set && rt != xpath_type_node_set)
+                       {
+                               if (lt == xpath_type_boolean || rt == xpath_type_boolean)
+                                       return comp(lhs->eval_boolean(c, stack), rhs->eval_boolean(c, stack));
+                               else if (lt == xpath_type_number || rt == xpath_type_number)
+                                       return comp(lhs->eval_number(c, stack), rhs->eval_number(c, stack));
+                               else if (lt == xpath_type_string || rt == xpath_type_string)
+                               {
+                                       xpath_allocator_capture cr(stack.result);
+
+                                       xpath_string ls = lhs->eval_string(c, stack);
+                                       xpath_string rs = rhs->eval_string(c, stack);
+
+                                       return comp(ls, rs);
+                               }
+                       }
+                       else if (lt == xpath_type_node_set && rt == xpath_type_node_set)
+                       {
+                               xpath_allocator_capture cr(stack.result);
+
+                               xpath_node_set_raw ls = lhs->eval_node_set(c, stack, nodeset_eval_all);
+                               xpath_node_set_raw rs = rhs->eval_node_set(c, stack, nodeset_eval_all);
+
+                               for (const xpath_node* li = ls.begin(); li != ls.end(); ++li)
+                                       for (const xpath_node* ri = rs.begin(); ri != rs.end(); ++ri)
+                                       {
+                                               xpath_allocator_capture cri(stack.result);
+
+                                               if (comp(string_value(*li, stack.result), string_value(*ri, stack.result)))
+                                                       return true;
+                                       }
+
+                               return false;
+                       }
+                       else
+                       {
+                               if (lt == xpath_type_node_set)
+                               {
+                                       swap(lhs, rhs);
+                                       swap(lt, rt);
+                               }
+
+                               if (lt == xpath_type_boolean)
+                                       return comp(lhs->eval_boolean(c, stack), rhs->eval_boolean(c, stack));
+                               else if (lt == xpath_type_number)
+                               {
+                                       xpath_allocator_capture cr(stack.result);
+
+                                       double l = lhs->eval_number(c, stack);
+                                       xpath_node_set_raw rs = rhs->eval_node_set(c, stack, nodeset_eval_all);
+
+                                       for (const xpath_node* ri = rs.begin(); ri != rs.end(); ++ri)
+                                       {
+                                               xpath_allocator_capture cri(stack.result);
+
+                                               if (comp(l, convert_string_to_number(string_value(*ri, stack.result).c_str())))
+                                                       return true;
+                                       }
+
+                                       return false;
+                               }
+                               else if (lt == xpath_type_string)
+                               {
+                                       xpath_allocator_capture cr(stack.result);
+
+                                       xpath_string l = lhs->eval_string(c, stack);
+                                       xpath_node_set_raw rs = rhs->eval_node_set(c, stack, nodeset_eval_all);
+
+                                       for (const xpath_node* ri = rs.begin(); ri != rs.end(); ++ri)
+                                       {
+                                               xpath_allocator_capture cri(stack.result);
+
+                                               if (comp(l, string_value(*ri, stack.result)))
+                                                       return true;
+                                       }
+
+                                       return false;
+                               }
+                       }
+
+                       assert(false && "Wrong types"); // unreachable
+                       return false;
+               }
+
+               static bool eval_once(xpath_node_set::type_t type, nodeset_eval_t eval)
+               {
+                       return type == xpath_node_set::type_sorted ? eval != nodeset_eval_all : eval == nodeset_eval_any;
+               }
+
+               template <class Comp> static bool compare_rel(xpath_ast_node* lhs, xpath_ast_node* rhs, const xpath_context& c, const xpath_stack& stack, const Comp& comp)
+               {
+                       xpath_value_type lt = lhs->rettype(), rt = rhs->rettype();
+
+                       if (lt != xpath_type_node_set && rt != xpath_type_node_set)
+                               return comp(lhs->eval_number(c, stack), rhs->eval_number(c, stack));
+                       else if (lt == xpath_type_node_set && rt == xpath_type_node_set)
+                       {
+                               xpath_allocator_capture cr(stack.result);
+
+                               xpath_node_set_raw ls = lhs->eval_node_set(c, stack, nodeset_eval_all);
+                               xpath_node_set_raw rs = rhs->eval_node_set(c, stack, nodeset_eval_all);
+
+                               for (const xpath_node* li = ls.begin(); li != ls.end(); ++li)
+                               {
+                                       xpath_allocator_capture cri(stack.result);
+
+                                       double l = convert_string_to_number(string_value(*li, stack.result).c_str());
+
+                                       for (const xpath_node* ri = rs.begin(); ri != rs.end(); ++ri)
+                                       {
+                                               xpath_allocator_capture crii(stack.result);
+
+                                               if (comp(l, convert_string_to_number(string_value(*ri, stack.result).c_str())))
+                                                       return true;
+                                       }
+                               }
+
+                               return false;
+                       }
+                       else if (lt != xpath_type_node_set && rt == xpath_type_node_set)
+                       {
+                               xpath_allocator_capture cr(stack.result);
+
+                               double l = lhs->eval_number(c, stack);
+                               xpath_node_set_raw rs = rhs->eval_node_set(c, stack, nodeset_eval_all);
+
+                               for (const xpath_node* ri = rs.begin(); ri != rs.end(); ++ri)
+                               {
+                                       xpath_allocator_capture cri(stack.result);
+
+                                       if (comp(l, convert_string_to_number(string_value(*ri, stack.result).c_str())))
+                                               return true;
+                               }
+
+                               return false;
+                       }
+                       else if (lt == xpath_type_node_set && rt != xpath_type_node_set)
+                       {
+                               xpath_allocator_capture cr(stack.result);
+
+                               xpath_node_set_raw ls = lhs->eval_node_set(c, stack, nodeset_eval_all);
+                               double r = rhs->eval_number(c, stack);
+
+                               for (const xpath_node* li = ls.begin(); li != ls.end(); ++li)
+                               {
+                                       xpath_allocator_capture cri(stack.result);
+
+                                       if (comp(convert_string_to_number(string_value(*li, stack.result).c_str()), r))
+                                               return true;
+                               }
+
+                               return false;
+                       }
+                       else
+                       {
+                               assert(false && "Wrong types"); // unreachable
+                               return false;
+                       }
+               }
+
+               static void apply_predicate_boolean(xpath_node_set_raw& ns, size_t first, xpath_ast_node* expr, const xpath_stack& stack, bool once)
+               {
+                       assert(ns.size() >= first);
+                       assert(expr->rettype() != xpath_type_number);
+
+                       size_t i = 1;
+                       size_t size = ns.size() - first;
+
+                       xpath_node* last = ns.begin() + first;
+
+                       // remove_if... or well, sort of
+                       for (xpath_node* it = last; it != ns.end(); ++it, ++i)
+                       {
+                               xpath_context c(*it, i, size);
+
+                               if (expr->eval_boolean(c, stack))
+                               {
+                                       *last++ = *it;
+
+                                       if (once) break;
+                               }
+                       }
+
+                       ns.truncate(last);
+               }
+
+               static void apply_predicate_number(xpath_node_set_raw& ns, size_t first, xpath_ast_node* expr, const xpath_stack& stack, bool once)
+               {
+                       assert(ns.size() >= first);
+                       assert(expr->rettype() == xpath_type_number);
+
+                       size_t i = 1;
+                       size_t size = ns.size() - first;
+
+                       xpath_node* last = ns.begin() + first;
+
+                       // remove_if... or well, sort of
+                       for (xpath_node* it = last; it != ns.end(); ++it, ++i)
+                       {
+                               xpath_context c(*it, i, size);
+
+                               if (expr->eval_number(c, stack) == i)
+                               {
+                                       *last++ = *it;
+
+                                       if (once) break;
+                               }
+                       }
+
+                       ns.truncate(last);
+               }
+
+               static void apply_predicate_number_const(xpath_node_set_raw& ns, size_t first, xpath_ast_node* expr, const xpath_stack& stack)
+               {
+                       assert(ns.size() >= first);
+                       assert(expr->rettype() == xpath_type_number);
+
+                       size_t size = ns.size() - first;
+
+                       xpath_node* last = ns.begin() + first;
+
+                       xpath_context c(xpath_node(), 1, size);
+
+                       double er = expr->eval_number(c, stack);
+
+                       if (er >= 1.0 && er <= size)
+                       {
+                               size_t eri = static_cast<size_t>(er);
+
+                               if (er == eri)
+                               {
+                                       xpath_node r = last[eri - 1];
+
+                                       *last++ = r;
+                               }
+                       }
+
+                       ns.truncate(last);
+               }
+
+               void apply_predicate(xpath_node_set_raw& ns, size_t first, const xpath_stack& stack, bool once)
+               {
+                       if (ns.size() == first) return;
+
+                       assert(_type == ast_filter || _type == ast_predicate);
+
+                       if (_test == predicate_constant || _test == predicate_constant_one)
+                               apply_predicate_number_const(ns, first, _right, stack);
+                       else if (_right->rettype() == xpath_type_number)
+                               apply_predicate_number(ns, first, _right, stack, once);
+                       else
+                               apply_predicate_boolean(ns, first, _right, stack, once);
+               }
+
+               void apply_predicates(xpath_node_set_raw& ns, size_t first, const xpath_stack& stack, nodeset_eval_t eval)
+               {
+                       if (ns.size() == first) return;
+
+                       bool last_once = eval_once(ns.type(), eval);
+
+                       for (xpath_ast_node* pred = _right; pred; pred = pred->_next)
+                               pred->apply_predicate(ns, first, stack, !pred->_next && last_once);
+               }
+
+               bool step_push(xpath_node_set_raw& ns, xml_attribute_struct* a, xml_node_struct* parent, xpath_allocator* alloc)
+               {
+                       assert(a);
+
+                       const char_t* name = a->name ? a->name + 0 : PUGIXML_TEXT("");
+
+                       switch (_test)
+                       {
+                       case nodetest_name:
+                               if (strequal(name, _data.nodetest) && is_xpath_attribute(name))
+                               {
+                                       ns.push_back(xpath_node(xml_attribute(a), xml_node(parent)), alloc);
+                                       return true;
+                               }
+                               break;
+
+                       case nodetest_type_node:
+                       case nodetest_all:
+                               if (is_xpath_attribute(name))
+                               {
+                                       ns.push_back(xpath_node(xml_attribute(a), xml_node(parent)), alloc);
+                                       return true;
+                               }
+                               break;
+
+                       case nodetest_all_in_namespace:
+                               if (starts_with(name, _data.nodetest) && is_xpath_attribute(name))
+                               {
+                                       ns.push_back(xpath_node(xml_attribute(a), xml_node(parent)), alloc);
+                                       return true;
+                               }
+                               break;
+
+                       default:
+                               ;
+                       }
+
+                       return false;
+               }
+
+               bool step_push(xpath_node_set_raw& ns, xml_node_struct* n, xpath_allocator* alloc)
+               {
+                       assert(n);
+
+                       xml_node_type type = PUGI__NODETYPE(n);
+
+                       switch (_test)
+                       {
+                       case nodetest_name:
+                               if (type == node_element && n->name && strequal(n->name, _data.nodetest))
+                               {
+                                       ns.push_back(xml_node(n), alloc);
+                                       return true;
+                               }
+                               break;
+
+                       case nodetest_type_node:
+                               ns.push_back(xml_node(n), alloc);
+                               return true;
+
+                       case nodetest_type_comment:
+                               if (type == node_comment)
+                               {
+                                       ns.push_back(xml_node(n), alloc);
+                                       return true;
+                               }
+                               break;
+
+                       case nodetest_type_text:
+                               if (type == node_pcdata || type == node_cdata)
+                               {
+                                       ns.push_back(xml_node(n), alloc);
+                                       return true;
+                               }
+                               break;
+
+                       case nodetest_type_pi:
+                               if (type == node_pi)
+                               {
+                                       ns.push_back(xml_node(n), alloc);
+                                       return true;
+                               }
+                               break;
+
+                       case nodetest_pi:
+                               if (type == node_pi && n->name && strequal(n->name, _data.nodetest))
+                               {
+                                       ns.push_back(xml_node(n), alloc);
+                                       return true;
+                               }
+                               break;
+
+                       case nodetest_all:
+                               if (type == node_element)
+                               {
+                                       ns.push_back(xml_node(n), alloc);
+                                       return true;
+                               }
+                               break;
+
+                       case nodetest_all_in_namespace:
+                               if (type == node_element && n->name && starts_with(n->name, _data.nodetest))
+                               {
+                                       ns.push_back(xml_node(n), alloc);
+                                       return true;
+                               }
+                               break;
+
+                       default:
+                               assert(false && "Unknown axis"); // unreachable
+                       }
+
+                       return false;
+               }
+
+               template <class T> void step_fill(xpath_node_set_raw& ns, xml_node_struct* n, xpath_allocator* alloc, bool once, T)
+               {
+                       const axis_t axis = T::axis;
+
+                       switch (axis)
+                       {
+                       case axis_attribute:
+                       {
+                               for (xml_attribute_struct* a = n->first_attribute; a; a = a->next_attribute)
+                                       if (step_push(ns, a, n, alloc) & once)
+                                               return;
+
+                               break;
+                       }
+
+                       case axis_child:
+                       {
+                               for (xml_node_struct* c = n->first_child; c; c = c->next_sibling)
+                                       if (step_push(ns, c, alloc) & once)
+                                               return;
+
+                               break;
+                       }
+
+                       case axis_descendant:
+                       case axis_descendant_or_self:
+                       {
+                               if (axis == axis_descendant_or_self)
+                                       if (step_push(ns, n, alloc) & once)
+                                               return;
+
+                               xml_node_struct* cur = n->first_child;
+
+                               while (cur)
+                               {
+                                       if (step_push(ns, cur, alloc) & once)
+                                               return;
+
+                                       if (cur->first_child)
+                                               cur = cur->first_child;
+                                       else
+                                       {
+                                               while (!cur->next_sibling)
+                                               {
+                                                       cur = cur->parent;
+
+                                                       if (cur == n) return;
+                                               }
+
+                                               cur = cur->next_sibling;
+                                       }
+                               }
+
+                               break;
+                       }
+
+                       case axis_following_sibling:
+                       {
+                               for (xml_node_struct* c = n->next_sibling; c; c = c->next_sibling)
+                                       if (step_push(ns, c, alloc) & once)
+                                               return;
+
+                               break;
+                       }
+
+                       case axis_preceding_sibling:
+                       {
+                               for (xml_node_struct* c = n->prev_sibling_c; c->next_sibling; c = c->prev_sibling_c)
+                                       if (step_push(ns, c, alloc) & once)
+                                               return;
+
+                               break;
+                       }
+
+                       case axis_following:
+                       {
+                               xml_node_struct* cur = n;
+
+                               // exit from this node so that we don't include descendants
+                               while (!cur->next_sibling)
+                               {
+                                       cur = cur->parent;
+
+                                       if (!cur) return;
+                               }
+
+                               cur = cur->next_sibling;
+
+                               while (cur)
+                               {
+                                       if (step_push(ns, cur, alloc) & once)
+                                               return;
+
+                                       if (cur->first_child)
+                                               cur = cur->first_child;
+                                       else
+                                       {
+                                               while (!cur->next_sibling)
+                                               {
+                                                       cur = cur->parent;
+
+                                                       if (!cur) return;
+                                               }
+
+                                               cur = cur->next_sibling;
+                                       }
+                               }
+
+                               break;
+                       }
+
+                       case axis_preceding:
+                       {
+                               xml_node_struct* cur = n;
+
+                               // exit from this node so that we don't include descendants
+                               while (!cur->prev_sibling_c->next_sibling)
+                               {
+                                       cur = cur->parent;
+
+                                       if (!cur) return;
+                               }
+
+                               cur = cur->prev_sibling_c;
+
+                               while (cur)
+                               {
+                                       if (cur->first_child)
+                                               cur = cur->first_child->prev_sibling_c;
+                                       else
+                                       {
+                                               // leaf node, can't be ancestor
+                                               if (step_push(ns, cur, alloc) & once)
+                                                       return;
+
+                                               while (!cur->prev_sibling_c->next_sibling)
+                                               {
+                                                       cur = cur->parent;
+
+                                                       if (!cur) return;
+
+                                                       if (!node_is_ancestor(cur, n))
+                                                               if (step_push(ns, cur, alloc) & once)
+                                                                       return;
+                                               }
+
+                                               cur = cur->prev_sibling_c;
+                                       }
+                               }
+
+                               break;
+                       }
+
+                       case axis_ancestor:
+                       case axis_ancestor_or_self:
+                       {
+                               if (axis == axis_ancestor_or_self)
+                                       if (step_push(ns, n, alloc) & once)
+                                               return;
+
+                               xml_node_struct* cur = n->parent;
+
+                               while (cur)
+                               {
+                                       if (step_push(ns, cur, alloc) & once)
+                                               return;
+
+                                       cur = cur->parent;
+                               }
+
+                               break;
+                       }
+
+                       case axis_self:
+                       {
+                               step_push(ns, n, alloc);
+
+                               break;
+                       }
+
+                       case axis_parent:
+                       {
+                               if (n->parent)
+                                       step_push(ns, n->parent, alloc);
+
+                               break;
+                       }
+
+                       default:
+                               assert(false && "Unimplemented axis"); // unreachable
+                       }
+               }
+
+               template <class T> void step_fill(xpath_node_set_raw& ns, xml_attribute_struct* a, xml_node_struct* p, xpath_allocator* alloc, bool once, T v)
+               {
+                       const axis_t axis = T::axis;
+
+                       switch (axis)
+                       {
+                       case axis_ancestor:
+                       case axis_ancestor_or_self:
+                       {
+                               if (axis == axis_ancestor_or_self && _test == nodetest_type_node) // reject attributes based on principal node type test
+                                       if (step_push(ns, a, p, alloc) & once)
+                                               return;
+
+                               xml_node_struct* cur = p;
+
+                               while (cur)
+                               {
+                                       if (step_push(ns, cur, alloc) & once)
+                                               return;
+
+                                       cur = cur->parent;
+                               }
+
+                               break;
+                       }
+
+                       case axis_descendant_or_self:
+                       case axis_self:
+                       {
+                               if (_test == nodetest_type_node) // reject attributes based on principal node type test
+                                       step_push(ns, a, p, alloc);
+
+                               break;
+                       }
+
+                       case axis_following:
+                       {
+                               xml_node_struct* cur = p;
+
+                               while (cur)
+                               {
+                                       if (cur->first_child)
+                                               cur = cur->first_child;
+                                       else
+                                       {
+                                               while (!cur->next_sibling)
+                                               {
+                                                       cur = cur->parent;
+
+                                                       if (!cur) return;
+                                               }
+
+                                               cur = cur->next_sibling;
+                                       }
+
+                                       if (step_push(ns, cur, alloc) & once)
+                                               return;
+                               }
+
+                               break;
+                       }
+
+                       case axis_parent:
+                       {
+                               step_push(ns, p, alloc);
+
+                               break;
+                       }
+
+                       case axis_preceding:
+                       {
+                               // preceding:: axis does not include attribute nodes and attribute ancestors (they are the same as parent's ancestors), so we can reuse node preceding
+                               step_fill(ns, p, alloc, once, v);
+                               break;
+                       }
+
+                       default:
+                               assert(false && "Unimplemented axis"); // unreachable
+                       }
+               }
+
+               template <class T> void step_fill(xpath_node_set_raw& ns, const xpath_node& xn, xpath_allocator* alloc, bool once, T v)
+               {
+                       const axis_t axis = T::axis;
+                       const bool axis_has_attributes = (axis == axis_ancestor || axis == axis_ancestor_or_self || axis == axis_descendant_or_self || axis == axis_following || axis == axis_parent || axis == axis_preceding || axis == axis_self);
+
+                       if (xn.node())
+                               step_fill(ns, xn.node().internal_object(), alloc, once, v);
+                       else if (axis_has_attributes && xn.attribute() && xn.parent())
+                               step_fill(ns, xn.attribute().internal_object(), xn.parent().internal_object(), alloc, once, v);
+               }
+
+               template <class T> xpath_node_set_raw step_do(const xpath_context& c, const xpath_stack& stack, nodeset_eval_t eval, T v)
+               {
+                       const axis_t axis = T::axis;
+                       const bool axis_reverse = (axis == axis_ancestor || axis == axis_ancestor_or_self || axis == axis_preceding || axis == axis_preceding_sibling);
+                       const xpath_node_set::type_t axis_type = axis_reverse ? xpath_node_set::type_sorted_reverse : xpath_node_set::type_sorted;
+
+                       bool once =
+                               (axis == axis_attribute && _test == nodetest_name) ||
+                               (!_right && eval_once(axis_type, eval)) ||
+                               (_right && !_right->_next && _right->_test == predicate_constant_one);
+
+                       xpath_node_set_raw ns;
+                       ns.set_type(axis_type);
+
+                       if (_left)
+                       {
+                               xpath_node_set_raw s = _left->eval_node_set(c, stack, nodeset_eval_all);
+
+                               // self axis preserves the original order
+                               if (axis == axis_self) ns.set_type(s.type());
+
+                               for (const xpath_node* it = s.begin(); it != s.end(); ++it)
+                               {
+                                       size_t size = ns.size();
+
+                                       // in general, all axes generate elements in a particular order, but there is no order guarantee if axis is applied to two nodes
+                                       if (axis != axis_self && size != 0) ns.set_type(xpath_node_set::type_unsorted);
+
+                                       step_fill(ns, *it, stack.result, once, v);
+                                       if (_right) apply_predicates(ns, size, stack, eval);
+                               }
+                       }
+                       else
+                       {
+                               step_fill(ns, c.n, stack.result, once, v);
+                               if (_right) apply_predicates(ns, 0, stack, eval);
+                       }
+
+                       // child, attribute and self axes always generate unique set of nodes
+                       // for other axis, if the set stayed sorted, it stayed unique because the traversal algorithms do not visit the same node twice
+                       if (axis != axis_child && axis != axis_attribute && axis != axis_self && ns.type() == xpath_node_set::type_unsorted)
+                               ns.remove_duplicates();
+
+                       return ns;
+               }
+
+       public:
+               xpath_ast_node(ast_type_t type, xpath_value_type rettype_, const char_t* value):
+                       _type(static_cast<char>(type)), _rettype(static_cast<char>(rettype_)), _axis(0), _test(0), _left(0), _right(0), _next(0)
+               {
+                       assert(type == ast_string_constant);
+                       _data.string = value;
+               }
+
+               xpath_ast_node(ast_type_t type, xpath_value_type rettype_, double value):
+                       _type(static_cast<char>(type)), _rettype(static_cast<char>(rettype_)), _axis(0), _test(0), _left(0), _right(0), _next(0)
+               {
+                       assert(type == ast_number_constant);
+                       _data.number = value;
+               }
+
+               xpath_ast_node(ast_type_t type, xpath_value_type rettype_, xpath_variable* value):
+                       _type(static_cast<char>(type)), _rettype(static_cast<char>(rettype_)), _axis(0), _test(0), _left(0), _right(0), _next(0)
+               {
+                       assert(type == ast_variable);
+                       _data.variable = value;
+               }
+
+               xpath_ast_node(ast_type_t type, xpath_value_type rettype_, xpath_ast_node* left = 0, xpath_ast_node* right = 0):
+                       _type(static_cast<char>(type)), _rettype(static_cast<char>(rettype_)), _axis(0), _test(0), _left(left), _right(right), _next(0)
+               {
+               }
+
+               xpath_ast_node(ast_type_t type, xpath_ast_node* left, axis_t axis, nodetest_t test, const char_t* contents):
+                       _type(static_cast<char>(type)), _rettype(xpath_type_node_set), _axis(static_cast<char>(axis)), _test(static_cast<char>(test)), _left(left), _right(0), _next(0)
+               {
+                       assert(type == ast_step);
+                       _data.nodetest = contents;
+               }
+
+               xpath_ast_node(ast_type_t type, xpath_ast_node* left, xpath_ast_node* right, predicate_t test):
+                       _type(static_cast<char>(type)), _rettype(xpath_type_node_set), _axis(0), _test(static_cast<char>(test)), _left(left), _right(right), _next(0)
+               {
+                       assert(type == ast_filter || type == ast_predicate);
+               }
+
+               void set_next(xpath_ast_node* value)
+               {
+                       _next = value;
+               }
+
+               void set_right(xpath_ast_node* value)
+               {
+                       _right = value;
+               }
+
+               bool eval_boolean(const xpath_context& c, const xpath_stack& stack)
+               {
+                       switch (_type)
+                       {
+                       case ast_op_or:
+                               return _left->eval_boolean(c, stack) || _right->eval_boolean(c, stack);
+
+                       case ast_op_and:
+                               return _left->eval_boolean(c, stack) && _right->eval_boolean(c, stack);
+
+                       case ast_op_equal:
+                               return compare_eq(_left, _right, c, stack, equal_to());
+
+                       case ast_op_not_equal:
+                               return compare_eq(_left, _right, c, stack, not_equal_to());
+
+                       case ast_op_less:
+                               return compare_rel(_left, _right, c, stack, less());
+
+                       case ast_op_greater:
+                               return compare_rel(_right, _left, c, stack, less());
+
+                       case ast_op_less_or_equal:
+                               return compare_rel(_left, _right, c, stack, less_equal());
+
+                       case ast_op_greater_or_equal:
+                               return compare_rel(_right, _left, c, stack, less_equal());
+
+                       case ast_func_starts_with:
+                       {
+                               xpath_allocator_capture cr(stack.result);
+
+                               xpath_string lr = _left->eval_string(c, stack);
+                               xpath_string rr = _right->eval_string(c, stack);
+
+                               return starts_with(lr.c_str(), rr.c_str());
+                       }
+
+                       case ast_func_contains:
+                       {
+                               xpath_allocator_capture cr(stack.result);
+
+                               xpath_string lr = _left->eval_string(c, stack);
+                               xpath_string rr = _right->eval_string(c, stack);
+
+                               return find_substring(lr.c_str(), rr.c_str()) != 0;
+                       }
+
+                       case ast_func_boolean:
+                               return _left->eval_boolean(c, stack);
+
+                       case ast_func_not:
+                               return !_left->eval_boolean(c, stack);
+
+                       case ast_func_true:
+                               return true;
+
+                       case ast_func_false:
+                               return false;
+
+                       case ast_func_lang:
+                       {
+                               if (c.n.attribute()) return false;
+
+                               xpath_allocator_capture cr(stack.result);
+
+                               xpath_string lang = _left->eval_string(c, stack);
+
+                               for (xml_node n = c.n.node(); n; n = n.parent())
+                               {
+                                       xml_attribute a = n.attribute(PUGIXML_TEXT("xml:lang"));
+
+                                       if (a)
+                                       {
+                                               const char_t* value = a.value();
+
+                                               // strnicmp / strncasecmp is not portable
+                                               for (const char_t* lit = lang.c_str(); *lit; ++lit)
+                                               {
+                                                       if (tolower_ascii(*lit) != tolower_ascii(*value)) return false;
+                                                       ++value;
+                                               }
+
+                                               return *value == 0 || *value == '-';
+                                       }
+                               }
+
+                               return false;
+                       }
+
+                       case ast_opt_compare_attribute:
+                       {
+                               const char_t* value = (_right->_type == ast_string_constant) ? _right->_data.string : _right->_data.variable->get_string();
+
+                               xml_attribute attr = c.n.node().attribute(_left->_data.nodetest);
+
+                               return attr && strequal(attr.value(), value) && is_xpath_attribute(attr.name());
+                       }
+
+                       case ast_variable:
+                       {
+                               assert(_rettype == _data.variable->type());
+
+                               if (_rettype == xpath_type_boolean)
+                                       return _data.variable->get_boolean();
+                       }
+
+                       // fallthrough
+                       default:
+                       {
+                               switch (_rettype)
+                               {
+                               case xpath_type_number:
+                                       return convert_number_to_boolean(eval_number(c, stack));
+
+                               case xpath_type_string:
+                               {
+                                       xpath_allocator_capture cr(stack.result);
+
+                                       return !eval_string(c, stack).empty();
+                               }
+
+                               case xpath_type_node_set:
+                               {
+                                       xpath_allocator_capture cr(stack.result);
+
+                                       return !eval_node_set(c, stack, nodeset_eval_any).empty();
+                               }
+
+                               default:
+                                       assert(false && "Wrong expression for return type boolean"); // unreachable
+                                       return false;
+                               }
+                       }
+                       }
+               }
+
+               double eval_number(const xpath_context& c, const xpath_stack& stack)
+               {
+                       switch (_type)
+                       {
+                       case ast_op_add:
+                               return _left->eval_number(c, stack) + _right->eval_number(c, stack);
+
+                       case ast_op_subtract:
+                               return _left->eval_number(c, stack) - _right->eval_number(c, stack);
+
+                       case ast_op_multiply:
+                               return _left->eval_number(c, stack) * _right->eval_number(c, stack);
+
+                       case ast_op_divide:
+                               return _left->eval_number(c, stack) / _right->eval_number(c, stack);
+
+                       case ast_op_mod:
+                               return fmod(_left->eval_number(c, stack), _right->eval_number(c, stack));
+
+                       case ast_op_negate:
+                               return -_left->eval_number(c, stack);
+
+                       case ast_number_constant:
+                               return _data.number;
+
+                       case ast_func_last:
+                               return static_cast<double>(c.size);
+
+                       case ast_func_position:
+                               return static_cast<double>(c.position);
+
+                       case ast_func_count:
+                       {
+                               xpath_allocator_capture cr(stack.result);
+
+                               return static_cast<double>(_left->eval_node_set(c, stack, nodeset_eval_all).size());
+                       }
+
+                       case ast_func_string_length_0:
+                       {
+                               xpath_allocator_capture cr(stack.result);
+
+                               return static_cast<double>(string_value(c.n, stack.result).length());
+                       }
+
+                       case ast_func_string_length_1:
+                       {
+                               xpath_allocator_capture cr(stack.result);
+
+                               return static_cast<double>(_left->eval_string(c, stack).length());
+                       }
+
+                       case ast_func_number_0:
+                       {
+                               xpath_allocator_capture cr(stack.result);
+
+                               return convert_string_to_number(string_value(c.n, stack.result).c_str());
+                       }
+
+                       case ast_func_number_1:
+                               return _left->eval_number(c, stack);
+
+                       case ast_func_sum:
+                       {
+                               xpath_allocator_capture cr(stack.result);
+
+                               double r = 0;
+
+                               xpath_node_set_raw ns = _left->eval_node_set(c, stack, nodeset_eval_all);
+
+                               for (const xpath_node* it = ns.begin(); it != ns.end(); ++it)
+                               {
+                                       xpath_allocator_capture cri(stack.result);
+
+                                       r += convert_string_to_number(string_value(*it, stack.result).c_str());
+                               }
+
+                               return r;
+                       }
+
+                       case ast_func_floor:
+                       {
+                               double r = _left->eval_number(c, stack);
+
+                               return r == r ? floor(r) : r;
+                       }
+
+                       case ast_func_ceiling:
+                       {
+                               double r = _left->eval_number(c, stack);
+
+                               return r == r ? ceil(r) : r;
+                       }
+
+                       case ast_func_round:
+                               return round_nearest_nzero(_left->eval_number(c, stack));
+
+                       case ast_variable:
+                       {
+                               assert(_rettype == _data.variable->type());
+
+                               if (_rettype == xpath_type_number)
+                                       return _data.variable->get_number();
+                       }
+
+                       // fallthrough
+                       default:
+                       {
+                               switch (_rettype)
+                               {
+                               case xpath_type_boolean:
+                                       return eval_boolean(c, stack) ? 1 : 0;
+
+                               case xpath_type_string:
+                               {
+                                       xpath_allocator_capture cr(stack.result);
+
+                                       return convert_string_to_number(eval_string(c, stack).c_str());
+                               }
+
+                               case xpath_type_node_set:
+                               {
+                                       xpath_allocator_capture cr(stack.result);
+
+                                       return convert_string_to_number(eval_string(c, stack).c_str());
+                               }
+
+                               default:
+                                       assert(false && "Wrong expression for return type number"); // unreachable
+                                       return 0;
+                               }
+
+                       }
+                       }
+               }
+
+               xpath_string eval_string_concat(const xpath_context& c, const xpath_stack& stack)
+               {
+                       assert(_type == ast_func_concat);
+
+                       xpath_allocator_capture ct(stack.temp);
+
+                       // count the string number
+                       size_t count = 1;
+                       for (xpath_ast_node* nc = _right; nc; nc = nc->_next) count++;
+
+                       // allocate a buffer for temporary string objects
+                       xpath_string* buffer = static_cast<xpath_string*>(stack.temp->allocate(count * sizeof(xpath_string)));
+                       if (!buffer) return xpath_string();
+
+                       // evaluate all strings to temporary stack
+                       xpath_stack swapped_stack = {stack.temp, stack.result};
+
+                       buffer[0] = _left->eval_string(c, swapped_stack);
+
+                       size_t pos = 1;
+                       for (xpath_ast_node* n = _right; n; n = n->_next, ++pos) buffer[pos] = n->eval_string(c, swapped_stack);
+                       assert(pos == count);
+
+                       // get total length
+                       size_t length = 0;
+                       for (size_t i = 0; i < count; ++i) length += buffer[i].length();
+
+                       // create final string
+                       char_t* result = static_cast<char_t*>(stack.result->allocate((length + 1) * sizeof(char_t)));
+                       if (!result) return xpath_string();
+
+                       char_t* ri = result;
+
+                       for (size_t j = 0; j < count; ++j)
+                               for (const char_t* bi = buffer[j].c_str(); *bi; ++bi)
+                                       *ri++ = *bi;
+
+                       *ri = 0;
+
+                       return xpath_string::from_heap_preallocated(result, ri);
+               }
+
+               xpath_string eval_string(const xpath_context& c, const xpath_stack& stack)
+               {
+                       switch (_type)
+                       {
+                       case ast_string_constant:
+                               return xpath_string::from_const(_data.string);
+
+                       case ast_func_local_name_0:
+                       {
+                               xpath_node na = c.n;
+
+                               return xpath_string::from_const(local_name(na));
+                       }
+
+                       case ast_func_local_name_1:
+                       {
+                               xpath_allocator_capture cr(stack.result);
+
+                               xpath_node_set_raw ns = _left->eval_node_set(c, stack, nodeset_eval_first);
+                               xpath_node na = ns.first();
+
+                               return xpath_string::from_const(local_name(na));
+                       }
+
+                       case ast_func_name_0:
+                       {
+                               xpath_node na = c.n;
+
+                               return xpath_string::from_const(qualified_name(na));
+                       }
+
+                       case ast_func_name_1:
+                       {
+                               xpath_allocator_capture cr(stack.result);
+
+                               xpath_node_set_raw ns = _left->eval_node_set(c, stack, nodeset_eval_first);
+                               xpath_node na = ns.first();
+
+                               return xpath_string::from_const(qualified_name(na));
+                       }
+
+                       case ast_func_namespace_uri_0:
+                       {
+                               xpath_node na = c.n;
+
+                               return xpath_string::from_const(namespace_uri(na));
+                       }
+
+                       case ast_func_namespace_uri_1:
+                       {
+                               xpath_allocator_capture cr(stack.result);
+
+                               xpath_node_set_raw ns = _left->eval_node_set(c, stack, nodeset_eval_first);
+                               xpath_node na = ns.first();
+
+                               return xpath_string::from_const(namespace_uri(na));
+                       }
+
+                       case ast_func_string_0:
+                               return string_value(c.n, stack.result);
+
+                       case ast_func_string_1:
+                               return _left->eval_string(c, stack);
+
+                       case ast_func_concat:
+                               return eval_string_concat(c, stack);
+
+                       case ast_func_substring_before:
+                       {
+                               xpath_allocator_capture cr(stack.temp);
+
+                               xpath_stack swapped_stack = {stack.temp, stack.result};
+
+                               xpath_string s = _left->eval_string(c, swapped_stack);
+                               xpath_string p = _right->eval_string(c, swapped_stack);
+
+                               const char_t* pos = find_substring(s.c_str(), p.c_str());
+
+                               return pos ? xpath_string::from_heap(s.c_str(), pos, stack.result) : xpath_string();
+                       }
+
+                       case ast_func_substring_after:
+                       {
+                               xpath_allocator_capture cr(stack.temp);
+
+                               xpath_stack swapped_stack = {stack.temp, stack.result};
+
+                               xpath_string s = _left->eval_string(c, swapped_stack);
+                               xpath_string p = _right->eval_string(c, swapped_stack);
+
+                               const char_t* pos = find_substring(s.c_str(), p.c_str());
+                               if (!pos) return xpath_string();
+
+                               const char_t* rbegin = pos + p.length();
+                               const char_t* rend = s.c_str() + s.length();
+
+                               return s.uses_heap() ? xpath_string::from_heap(rbegin, rend, stack.result) : xpath_string::from_const(rbegin);
+                       }
+
+                       case ast_func_substring_2:
+                       {
+                               xpath_allocator_capture cr(stack.temp);
+
+                               xpath_stack swapped_stack = {stack.temp, stack.result};
+
+                               xpath_string s = _left->eval_string(c, swapped_stack);
+                               size_t s_length = s.length();
+
+                               double first = round_nearest(_right->eval_number(c, stack));
+
+                               if (is_nan(first)) return xpath_string(); // NaN
+                               else if (first >= s_length + 1) return xpath_string();
+
+                               size_t pos = first < 1 ? 1 : static_cast<size_t>(first);
+                               assert(1 <= pos && pos <= s_length + 1);
+
+                               const char_t* rbegin = s.c_str() + (pos - 1);
+                               const char_t* rend = s.c_str() + s.length();
+
+                               return s.uses_heap() ? xpath_string::from_heap(rbegin, rend, stack.result) : xpath_string::from_const(rbegin);
+                       }
+
+                       case ast_func_substring_3:
+                       {
+                               xpath_allocator_capture cr(stack.temp);
+
+                               xpath_stack swapped_stack = {stack.temp, stack.result};
+
+                               xpath_string s = _left->eval_string(c, swapped_stack);
+                               size_t s_length = s.length();
+
+                               double first = round_nearest(_right->eval_number(c, stack));
+                               double last = first + round_nearest(_right->_next->eval_number(c, stack));
+
+                               if (is_nan(first) || is_nan(last)) return xpath_string();
+                               else if (first >= s_length + 1) return xpath_string();
+                               else if (first >= last) return xpath_string();
+                               else if (last < 1) return xpath_string();
+
+                               size_t pos = first < 1 ? 1 : static_cast<size_t>(first);
+                               size_t end = last >= s_length + 1 ? s_length + 1 : static_cast<size_t>(last);
+
+                               assert(1 <= pos && pos <= end && end <= s_length + 1);
+                               const char_t* rbegin = s.c_str() + (pos - 1);
+                               const char_t* rend = s.c_str() + (end - 1);
+
+                               return (end == s_length + 1 && !s.uses_heap()) ? xpath_string::from_const(rbegin) : xpath_string::from_heap(rbegin, rend, stack.result);
+                       }
+
+                       case ast_func_normalize_space_0:
+                       {
+                               xpath_string s = string_value(c.n, stack.result);
+
+                               char_t* begin = s.data(stack.result);
+                               if (!begin) return xpath_string();
+
+                               char_t* end = normalize_space(begin);
+
+                               return xpath_string::from_heap_preallocated(begin, end);
+                       }
+
+                       case ast_func_normalize_space_1:
+                       {
+                               xpath_string s = _left->eval_string(c, stack);
+
+                               char_t* begin = s.data(stack.result);
+                               if (!begin) return xpath_string();
+
+                               char_t* end = normalize_space(begin);
+
+                               return xpath_string::from_heap_preallocated(begin, end);
+                       }
+
+                       case ast_func_translate:
+                       {
+                               xpath_allocator_capture cr(stack.temp);
+
+                               xpath_stack swapped_stack = {stack.temp, stack.result};
+
+                               xpath_string s = _left->eval_string(c, stack);
+                               xpath_string from = _right->eval_string(c, swapped_stack);
+                               xpath_string to = _right->_next->eval_string(c, swapped_stack);
+
+                               char_t* begin = s.data(stack.result);
+                               if (!begin) return xpath_string();
+
+                               char_t* end = translate(begin, from.c_str(), to.c_str(), to.length());
+
+                               return xpath_string::from_heap_preallocated(begin, end);
+                       }
+
+                       case ast_opt_translate_table:
+                       {
+                               xpath_string s = _left->eval_string(c, stack);
+
+                               char_t* begin = s.data(stack.result);
+                               if (!begin) return xpath_string();
+
+                               char_t* end = translate_table(begin, _data.table);
+
+                               return xpath_string::from_heap_preallocated(begin, end);
+                       }
+
+                       case ast_variable:
+                       {
+                               assert(_rettype == _data.variable->type());
+
+                               if (_rettype == xpath_type_string)
+                                       return xpath_string::from_const(_data.variable->get_string());
+                       }
+
+                       // fallthrough
+                       default:
+                       {
+                               switch (_rettype)
+                               {
+                               case xpath_type_boolean:
+                                       return xpath_string::from_const(eval_boolean(c, stack) ? PUGIXML_TEXT("true") : PUGIXML_TEXT("false"));
+
+                               case xpath_type_number:
+                                       return convert_number_to_string(eval_number(c, stack), stack.result);
+
+                               case xpath_type_node_set:
+                               {
+                                       xpath_allocator_capture cr(stack.temp);
+
+                                       xpath_stack swapped_stack = {stack.temp, stack.result};
+
+                                       xpath_node_set_raw ns = eval_node_set(c, swapped_stack, nodeset_eval_first);
+                                       return ns.empty() ? xpath_string() : string_value(ns.first(), stack.result);
+                               }
+
+                               default:
+                                       assert(false && "Wrong expression for return type string"); // unreachable
+                                       return xpath_string();
+                               }
+                       }
+                       }
+               }
+
+               xpath_node_set_raw eval_node_set(const xpath_context& c, const xpath_stack& stack, nodeset_eval_t eval)
+               {
+                       switch (_type)
+                       {
+                       case ast_op_union:
+                       {
+                               xpath_allocator_capture cr(stack.temp);
+
+                               xpath_stack swapped_stack = {stack.temp, stack.result};
+
+                               xpath_node_set_raw ls = _left->eval_node_set(c, swapped_stack, eval);
+                               xpath_node_set_raw rs = _right->eval_node_set(c, stack, eval);
+
+                               // we can optimize merging two sorted sets, but this is a very rare operation, so don't bother
+                               rs.set_type(xpath_node_set::type_unsorted);
+
+                               rs.append(ls.begin(), ls.end(), stack.result);
+                               rs.remove_duplicates();
+
+                               return rs;
+                       }
+
+                       case ast_filter:
+                       {
+                               xpath_node_set_raw set = _left->eval_node_set(c, stack, _test == predicate_constant_one ? nodeset_eval_first : nodeset_eval_all);
+
+                               // either expression is a number or it contains position() call; sort by document order
+                               if (_test != predicate_posinv) set.sort_do();
+
+                               bool once = eval_once(set.type(), eval);
+
+                               apply_predicate(set, 0, stack, once);
+
+                               return set;
+                       }
+
+                       case ast_func_id:
+                               return xpath_node_set_raw();
+
+                       case ast_step:
+                       {
+                               switch (_axis)
+                               {
+                               case axis_ancestor:
+                                       return step_do(c, stack, eval, axis_to_type<axis_ancestor>());
+
+                               case axis_ancestor_or_self:
+                                       return step_do(c, stack, eval, axis_to_type<axis_ancestor_or_self>());
+
+                               case axis_attribute:
+                                       return step_do(c, stack, eval, axis_to_type<axis_attribute>());
+
+                               case axis_child:
+                                       return step_do(c, stack, eval, axis_to_type<axis_child>());
+
+                               case axis_descendant:
+                                       return step_do(c, stack, eval, axis_to_type<axis_descendant>());
+
+                               case axis_descendant_or_self:
+                                       return step_do(c, stack, eval, axis_to_type<axis_descendant_or_self>());
+
+                               case axis_following:
+                                       return step_do(c, stack, eval, axis_to_type<axis_following>());
+
+                               case axis_following_sibling:
+                                       return step_do(c, stack, eval, axis_to_type<axis_following_sibling>());
+
+                               case axis_namespace:
+                                       // namespaced axis is not supported
+                                       return xpath_node_set_raw();
+
+                               case axis_parent:
+                                       return step_do(c, stack, eval, axis_to_type<axis_parent>());
+
+                               case axis_preceding:
+                                       return step_do(c, stack, eval, axis_to_type<axis_preceding>());
+
+                               case axis_preceding_sibling:
+                                       return step_do(c, stack, eval, axis_to_type<axis_preceding_sibling>());
+
+                               case axis_self:
+                                       return step_do(c, stack, eval, axis_to_type<axis_self>());
+
+                               default:
+                                       assert(false && "Unknown axis"); // unreachable
+                                       return xpath_node_set_raw();
+                               }
+                       }
+
+                       case ast_step_root:
+                       {
+                               assert(!_right); // root step can't have any predicates
+
+                               xpath_node_set_raw ns;
+
+                               ns.set_type(xpath_node_set::type_sorted);
+
+                               if (c.n.node()) ns.push_back(c.n.node().root(), stack.result);
+                               else if (c.n.attribute()) ns.push_back(c.n.parent().root(), stack.result);
+
+                               return ns;
+                       }
+
+                       case ast_variable:
+                       {
+                               assert(_rettype == _data.variable->type());
+
+                               if (_rettype == xpath_type_node_set)
+                               {
+                                       const xpath_node_set& s = _data.variable->get_node_set();
+
+                                       xpath_node_set_raw ns;
+
+                                       ns.set_type(s.type());
+                                       ns.append(s.begin(), s.end(), stack.result);
+
+                                       return ns;
+                               }
+                       }
+
+                       // fallthrough
+                       default:
+                               assert(false && "Wrong expression for return type node set"); // unreachable
+                               return xpath_node_set_raw();
+                       }
+               }
+
+               void optimize(xpath_allocator* alloc)
+               {
+                       if (_left)
+                               _left->optimize(alloc);
+
+                       if (_right)
+                               _right->optimize(alloc);
+
+                       if (_next)
+                               _next->optimize(alloc);
+
+                       optimize_self(alloc);
+               }
+
+               void optimize_self(xpath_allocator* alloc)
+               {
+                       // Rewrite [position()=expr] with [expr]
+                       // Note that this step has to go before classification to recognize [position()=1]
+                       if ((_type == ast_filter || _type == ast_predicate) &&
+                               _right->_type == ast_op_equal && _right->_left->_type == ast_func_position && _right->_right->_rettype == xpath_type_number)
+                       {
+                               _right = _right->_right;
+                       }
+
+                       // Classify filter/predicate ops to perform various optimizations during evaluation
+                       if (_type == ast_filter || _type == ast_predicate)
+                       {
+                               assert(_test == predicate_default);
+
+                               if (_right->_type == ast_number_constant && _right->_data.number == 1.0)
+                                       _test = predicate_constant_one;
+                               else if (_right->_rettype == xpath_type_number && (_right->_type == ast_number_constant || _right->_type == ast_variable || _right->_type == ast_func_last))
+                                       _test = predicate_constant;
+                               else if (_right->_rettype != xpath_type_number && _right->is_posinv_expr())
+                                       _test = predicate_posinv;
+                       }
+
+                       // Rewrite descendant-or-self::node()/child::foo with descendant::foo
+                       // The former is a full form of //foo, the latter is much faster since it executes the node test immediately
+                       // Do a similar kind of rewrite for self/descendant/descendant-or-self axes
+                       // Note that we only rewrite positionally invariant steps (//foo[1] != /descendant::foo[1])
+                       if (_type == ast_step && (_axis == axis_child || _axis == axis_self || _axis == axis_descendant || _axis == axis_descendant_or_self) && _left &&
+                               _left->_type == ast_step && _left->_axis == axis_descendant_or_self && _left->_test == nodetest_type_node && !_left->_right &&
+                               is_posinv_step())
+                       {
+                               if (_axis == axis_child || _axis == axis_descendant)
+                                       _axis = axis_descendant;
+                               else
+                                       _axis = axis_descendant_or_self;
+
+                               _left = _left->_left;
+                       }
+
+                       // Use optimized lookup table implementation for translate() with constant arguments
+                       if (_type == ast_func_translate && _right->_type == ast_string_constant && _right->_next->_type == ast_string_constant)
+                       {
+                               unsigned char* table = translate_table_generate(alloc, _right->_data.string, _right->_next->_data.string);
+
+                               if (table)
+                               {
+                                       _type = ast_opt_translate_table;
+                                       _data.table = table;
+                               }
+                       }
+
+                       // Use optimized path for @attr = 'value' or @attr = $value
+                       if (_type == ast_op_equal &&
+                               _left->_type == ast_step && _left->_axis == axis_attribute && _left->_test == nodetest_name && !_left->_left && !_left->_right &&
+                               (_right->_type == ast_string_constant || (_right->_type == ast_variable && _right->_rettype == xpath_type_string)))
+                       {
+                               _type = ast_opt_compare_attribute;
+                       }
+               }
+
+               bool is_posinv_expr() const
+               {
+                       switch (_type)
+                       {
+                       case ast_func_position:
+                       case ast_func_last:
+                               return false;
+
+                       case ast_string_constant:
+                       case ast_number_constant:
+                       case ast_variable:
+                               return true;
+
+                       case ast_step:
+                       case ast_step_root:
+                               return true;
+
+                       case ast_predicate:
+                       case ast_filter:
+                               return true;
+
+                       default:
+                               if (_left && !_left->is_posinv_expr()) return false;
+
+                               for (xpath_ast_node* n = _right; n; n = n->_next)
+                                       if (!n->is_posinv_expr()) return false;
+
+                               return true;
+                       }
+               }
+
+               bool is_posinv_step() const
+               {
+                       assert(_type == ast_step);
+
+                       for (xpath_ast_node* n = _right; n; n = n->_next)
+                       {
+                               assert(n->_type == ast_predicate);
+
+                               if (n->_test != predicate_posinv)
+                                       return false;
+                       }
+
+                       return true;
+               }
+
+               xpath_value_type rettype() const
+               {
+                       return static_cast<xpath_value_type>(_rettype);
+               }
+       };
+
+       struct xpath_parser
+       {
+               xpath_allocator* _alloc;
+               xpath_lexer _lexer;
+
+               const char_t* _query;
+               xpath_variable_set* _variables;
+
+               xpath_parse_result* _result;
+
+               char_t _scratch[32];
+
+               xpath_ast_node* error(const char* message)
+               {
+                       _result->error = message;
+                       _result->offset = _lexer.current_pos() - _query;
+
+                       return 0;
+               }
+
+               xpath_ast_node* error_oom()
+               {
+                       assert(_alloc->_error);
+                       *_alloc->_error = true;
+
+                       return 0;
+               }
+
+               void* alloc_node()
+               {
+                       return _alloc->allocate(sizeof(xpath_ast_node));
+               }
+
+               xpath_ast_node* alloc_node(ast_type_t type, xpath_value_type rettype, const char_t* value)
+               {
+                       void* memory = alloc_node();
+                       return memory ? new (memory) xpath_ast_node(type, rettype, value) : 0;
+               }
+
+               xpath_ast_node* alloc_node(ast_type_t type, xpath_value_type rettype, double value)
+               {
+                       void* memory = alloc_node();
+                       return memory ? new (memory) xpath_ast_node(type, rettype, value) : 0;
+               }
+
+               xpath_ast_node* alloc_node(ast_type_t type, xpath_value_type rettype, xpath_variable* value)
+               {
+                       void* memory = alloc_node();
+                       return memory ? new (memory) xpath_ast_node(type, rettype, value) : 0;
+               }
+
+               xpath_ast_node* alloc_node(ast_type_t type, xpath_value_type rettype, xpath_ast_node* left = 0, xpath_ast_node* right = 0)
+               {
+                       void* memory = alloc_node();
+                       return memory ? new (memory) xpath_ast_node(type, rettype, left, right) : 0;
+               }
+
+               xpath_ast_node* alloc_node(ast_type_t type, xpath_ast_node* left, axis_t axis, nodetest_t test, const char_t* contents)
+               {
+                       void* memory = alloc_node();
+                       return memory ? new (memory) xpath_ast_node(type, left, axis, test, contents) : 0;
+               }
+
+               xpath_ast_node* alloc_node(ast_type_t type, xpath_ast_node* left, xpath_ast_node* right, predicate_t test)
+               {
+                       void* memory = alloc_node();
+                       return memory ? new (memory) xpath_ast_node(type, left, right, test) : 0;
+               }
+
+               const char_t* alloc_string(const xpath_lexer_string& value)
+               {
+                       if (!value.begin)
+                               return PUGIXML_TEXT("");
+
+                       size_t length = static_cast<size_t>(value.end - value.begin);
+
+                       char_t* c = static_cast<char_t*>(_alloc->allocate((length + 1) * sizeof(char_t)));
+                       if (!c) return 0;
+
+                       memcpy(c, value.begin, length * sizeof(char_t));
+                       c[length] = 0;
+
+                       return c;
+               }
+
+               xpath_ast_node* parse_function(const xpath_lexer_string& name, size_t argc, xpath_ast_node* args[2])
+               {
+                       switch (name.begin[0])
+                       {
+                       case 'b':
+                               if (name == PUGIXML_TEXT("boolean") && argc == 1)
+                                       return alloc_node(ast_func_boolean, xpath_type_boolean, args[0]);
+
+                               break;
+
+                       case 'c':
+                               if (name == PUGIXML_TEXT("count") && argc == 1)
+                               {
+                                       if (args[0]->rettype() != xpath_type_node_set) return error("Function has to be applied to node set");
+                                       return alloc_node(ast_func_count, xpath_type_number, args[0]);
+                               }
+                               else if (name == PUGIXML_TEXT("contains") && argc == 2)
+                                       return alloc_node(ast_func_contains, xpath_type_boolean, args[0], args[1]);
+                               else if (name == PUGIXML_TEXT("concat") && argc >= 2)
+                                       return alloc_node(ast_func_concat, xpath_type_string, args[0], args[1]);
+                               else if (name == PUGIXML_TEXT("ceiling") && argc == 1)
+                                       return alloc_node(ast_func_ceiling, xpath_type_number, args[0]);
+
+                               break;
+
+                       case 'f':
+                               if (name == PUGIXML_TEXT("false") && argc == 0)
+                                       return alloc_node(ast_func_false, xpath_type_boolean);
+                               else if (name == PUGIXML_TEXT("floor") && argc == 1)
+                                       return alloc_node(ast_func_floor, xpath_type_number, args[0]);
+
+                               break;
+
+                       case 'i':
+                               if (name == PUGIXML_TEXT("id") && argc == 1)
+                                       return alloc_node(ast_func_id, xpath_type_node_set, args[0]);
+
+                               break;
+
+                       case 'l':
+                               if (name == PUGIXML_TEXT("last") && argc == 0)
+                                       return alloc_node(ast_func_last, xpath_type_number);
+                               else if (name == PUGIXML_TEXT("lang") && argc == 1)
+                                       return alloc_node(ast_func_lang, xpath_type_boolean, args[0]);
+                               else if (name == PUGIXML_TEXT("local-name") && argc <= 1)
+                               {
+                                       if (argc == 1 && args[0]->rettype() != xpath_type_node_set) return error("Function has to be applied to node set");
+                                       return alloc_node(argc == 0 ? ast_func_local_name_0 : ast_func_local_name_1, xpath_type_string, args[0]);
+                               }
+
+                               break;
+
+                       case 'n':
+                               if (name == PUGIXML_TEXT("name") && argc <= 1)
+                               {
+                                       if (argc == 1 && args[0]->rettype() != xpath_type_node_set) return error("Function has to be applied to node set");
+                                       return alloc_node(argc == 0 ? ast_func_name_0 : ast_func_name_1, xpath_type_string, args[0]);
+                               }
+                               else if (name == PUGIXML_TEXT("namespace-uri") && argc <= 1)
+                               {
+                                       if (argc == 1 && args[0]->rettype() != xpath_type_node_set) return error("Function has to be applied to node set");
+                                       return alloc_node(argc == 0 ? ast_func_namespace_uri_0 : ast_func_namespace_uri_1, xpath_type_string, args[0]);
+                               }
+                               else if (name == PUGIXML_TEXT("normalize-space") && argc <= 1)
+                                       return alloc_node(argc == 0 ? ast_func_normalize_space_0 : ast_func_normalize_space_1, xpath_type_string, args[0], args[1]);
+                               else if (name == PUGIXML_TEXT("not") && argc == 1)
+                                       return alloc_node(ast_func_not, xpath_type_boolean, args[0]);
+                               else if (name == PUGIXML_TEXT("number") && argc <= 1)
+                                       return alloc_node(argc == 0 ? ast_func_number_0 : ast_func_number_1, xpath_type_number, args[0]);
+
+                               break;
+
+                       case 'p':
+                               if (name == PUGIXML_TEXT("position") && argc == 0)
+                                       return alloc_node(ast_func_position, xpath_type_number);
+
+                               break;
+
+                       case 'r':
+                               if (name == PUGIXML_TEXT("round") && argc == 1)
+                                       return alloc_node(ast_func_round, xpath_type_number, args[0]);
+
+                               break;
+
+                       case 's':
+                               if (name == PUGIXML_TEXT("string") && argc <= 1)
+                                       return alloc_node(argc == 0 ? ast_func_string_0 : ast_func_string_1, xpath_type_string, args[0]);
+                               else if (name == PUGIXML_TEXT("string-length") && argc <= 1)
+                                       return alloc_node(argc == 0 ? ast_func_string_length_0 : ast_func_string_length_1, xpath_type_number, args[0]);
+                               else if (name == PUGIXML_TEXT("starts-with") && argc == 2)
+                                       return alloc_node(ast_func_starts_with, xpath_type_boolean, args[0], args[1]);
+                               else if (name == PUGIXML_TEXT("substring-before") && argc == 2)
+                                       return alloc_node(ast_func_substring_before, xpath_type_string, args[0], args[1]);
+                               else if (name == PUGIXML_TEXT("substring-after") && argc == 2)
+                                       return alloc_node(ast_func_substring_after, xpath_type_string, args[0], args[1]);
+                               else if (name == PUGIXML_TEXT("substring") && (argc == 2 || argc == 3))
+                                       return alloc_node(argc == 2 ? ast_func_substring_2 : ast_func_substring_3, xpath_type_string, args[0], args[1]);
+                               else if (name == PUGIXML_TEXT("sum") && argc == 1)
+                               {
+                                       if (args[0]->rettype() != xpath_type_node_set) return error("Function has to be applied to node set");
+                                       return alloc_node(ast_func_sum, xpath_type_number, args[0]);
+                               }
+
+                               break;
+
+                       case 't':
+                               if (name == PUGIXML_TEXT("translate") && argc == 3)
+                                       return alloc_node(ast_func_translate, xpath_type_string, args[0], args[1]);
+                               else if (name == PUGIXML_TEXT("true") && argc == 0)
+                                       return alloc_node(ast_func_true, xpath_type_boolean);
+
+                               break;
+
+                       default:
+                               break;
+                       }
+
+                       return error("Unrecognized function or wrong parameter count");
+               }
+
+               axis_t parse_axis_name(const xpath_lexer_string& name, bool& specified)
+               {
+                       specified = true;
+
+                       switch (name.begin[0])
+                       {
+                       case 'a':
+                               if (name == PUGIXML_TEXT("ancestor"))
+                                       return axis_ancestor;
+                               else if (name == PUGIXML_TEXT("ancestor-or-self"))
+                                       return axis_ancestor_or_self;
+                               else if (name == PUGIXML_TEXT("attribute"))
+                                       return axis_attribute;
+
+                               break;
+
+                       case 'c':
+                               if (name == PUGIXML_TEXT("child"))
+                                       return axis_child;
+
+                               break;
+
+                       case 'd':
+                               if (name == PUGIXML_TEXT("descendant"))
+                                       return axis_descendant;
+                               else if (name == PUGIXML_TEXT("descendant-or-self"))
+                                       return axis_descendant_or_self;
+
+                               break;
+
+                       case 'f':
+                               if (name == PUGIXML_TEXT("following"))
+                                       return axis_following;
+                               else if (name == PUGIXML_TEXT("following-sibling"))
+                                       return axis_following_sibling;
+
+                               break;
+
+                       case 'n':
+                               if (name == PUGIXML_TEXT("namespace"))
+                                       return axis_namespace;
+
+                               break;
+
+                       case 'p':
+                               if (name == PUGIXML_TEXT("parent"))
+                                       return axis_parent;
+                               else if (name == PUGIXML_TEXT("preceding"))
+                                       return axis_preceding;
+                               else if (name == PUGIXML_TEXT("preceding-sibling"))
+                                       return axis_preceding_sibling;
+
+                               break;
+
+                       case 's':
+                               if (name == PUGIXML_TEXT("self"))
+                                       return axis_self;
+
+                               break;
+
+                       default:
+                               break;
+                       }
+
+                       specified = false;
+                       return axis_child;
+               }
+
+               nodetest_t parse_node_test_type(const xpath_lexer_string& name)
+               {
+                       switch (name.begin[0])
+                       {
+                       case 'c':
+                               if (name == PUGIXML_TEXT("comment"))
+                                       return nodetest_type_comment;
+
+                               break;
+
+                       case 'n':
+                               if (name == PUGIXML_TEXT("node"))
+                                       return nodetest_type_node;
+
+                               break;
+
+                       case 'p':
+                               if (name == PUGIXML_TEXT("processing-instruction"))
+                                       return nodetest_type_pi;
+
+                               break;
+
+                       case 't':
+                               if (name == PUGIXML_TEXT("text"))
+                                       return nodetest_type_text;
+
+                               break;
+
+                       default:
+                               break;
+                       }
+
+                       return nodetest_none;
+               }
+
+               // PrimaryExpr ::= VariableReference | '(' Expr ')' | Literal | Number | FunctionCall
+               xpath_ast_node* parse_primary_expression()
+               {
+                       switch (_lexer.current())
+                       {
+                       case lex_var_ref:
+                       {
+                               xpath_lexer_string name = _lexer.contents();
+
+                               if (!_variables)
+                                       return error("Unknown variable: variable set is not provided");
+
+                               xpath_variable* var = 0;
+                               if (!get_variable_scratch(_scratch, _variables, name.begin, name.end, &var))
+                                       return error_oom();
+
+                               if (!var)
+                                       return error("Unknown variable: variable set does not contain the given name");
+
+                               _lexer.next();
+
+                               return alloc_node(ast_variable, var->type(), var);
+                       }
+
+                       case lex_open_brace:
+                       {
+                               _lexer.next();
+
+                               xpath_ast_node* n = parse_expression();
+                               if (!n) return 0;
+
+                               if (_lexer.current() != lex_close_brace)
+                                       return error("Expected ')' to match an opening '('");
+
+                               _lexer.next();
+
+                               return n;
+                       }
+
+                       case lex_quoted_string:
+                       {
+                               const char_t* value = alloc_string(_lexer.contents());
+                               if (!value) return 0;
+
+                               _lexer.next();
+
+                               return alloc_node(ast_string_constant, xpath_type_string, value);
+                       }
+
+                       case lex_number:
+                       {
+                               double value = 0;
+
+                               if (!convert_string_to_number_scratch(_scratch, _lexer.contents().begin, _lexer.contents().end, &value))
+                                       return error_oom();
+
+                               _lexer.next();
+
+                               return alloc_node(ast_number_constant, xpath_type_number, value);
+                       }
+
+                       case lex_string:
+                       {
+                               xpath_ast_node* args[2] = {0};
+                               size_t argc = 0;
+
+                               xpath_lexer_string function = _lexer.contents();
+                               _lexer.next();
+
+                               xpath_ast_node* last_arg = 0;
+
+                               if (_lexer.current() != lex_open_brace)
+                                       return error("Unrecognized function call");
+                               _lexer.next();
+
+                               while (_lexer.current() != lex_close_brace)
+                               {
+                                       if (argc > 0)
+                                       {
+                                               if (_lexer.current() != lex_comma)
+                                                       return error("No comma between function arguments");
+                                               _lexer.next();
+                                       }
+
+                                       xpath_ast_node* n = parse_expression();
+                                       if (!n) return 0;
+
+                                       if (argc < 2) args[argc] = n;
+                                       else last_arg->set_next(n);
+
+                                       argc++;
+                                       last_arg = n;
+                               }
+
+                               _lexer.next();
+
+                               return parse_function(function, argc, args);
+                       }
+
+                       default:
+                               return error("Unrecognizable primary expression");
+                       }
+               }
+
+               // FilterExpr ::= PrimaryExpr | FilterExpr Predicate
+               // Predicate ::= '[' PredicateExpr ']'
+               // PredicateExpr ::= Expr
+               xpath_ast_node* parse_filter_expression()
+               {
+                       xpath_ast_node* n = parse_primary_expression();
+                       if (!n) return 0;
+
+                       while (_lexer.current() == lex_open_square_brace)
+                       {
+                               _lexer.next();
+
+                               if (n->rettype() != xpath_type_node_set)
+                                       return error("Predicate has to be applied to node set");
+
+                               xpath_ast_node* expr = parse_expression();
+                               if (!expr) return 0;
+
+                               n = alloc_node(ast_filter, n, expr, predicate_default);
+                               if (!n) return 0;
+
+                               if (_lexer.current() != lex_close_square_brace)
+                                       return error("Expected ']' to match an opening '['");
+
+                               _lexer.next();
+                       }
+
+                       return n;
+               }
+
+               // Step ::= AxisSpecifier NodeTest Predicate* | AbbreviatedStep
+               // AxisSpecifier ::= AxisName '::' | '@'?
+               // NodeTest ::= NameTest | NodeType '(' ')' | 'processing-instruction' '(' Literal ')'
+               // NameTest ::= '*' | NCName ':' '*' | QName
+               // AbbreviatedStep ::= '.' | '..'
+               xpath_ast_node* parse_step(xpath_ast_node* set)
+               {
+                       if (set && set->rettype() != xpath_type_node_set)
+                               return error("Step has to be applied to node set");
+
+                       bool axis_specified = false;
+                       axis_t axis = axis_child; // implied child axis
+
+                       if (_lexer.current() == lex_axis_attribute)
+                       {
+                               axis = axis_attribute;
+                               axis_specified = true;
+
+                               _lexer.next();
+                       }
+                       else if (_lexer.current() == lex_dot)
+                       {
+                               _lexer.next();
+
+                               if (_lexer.current() == lex_open_square_brace)
+                                       return error("Predicates are not allowed after an abbreviated step");
+
+                               return alloc_node(ast_step, set, axis_self, nodetest_type_node, 0);
+                       }
+                       else if (_lexer.current() == lex_double_dot)
+                       {
+                               _lexer.next();
+
+                               if (_lexer.current() == lex_open_square_brace)
+                                       return error("Predicates are not allowed after an abbreviated step");
+
+                               return alloc_node(ast_step, set, axis_parent, nodetest_type_node, 0);
+                       }
+
+                       nodetest_t nt_type = nodetest_none;
+                       xpath_lexer_string nt_name;
+
+                       if (_lexer.current() == lex_string)
+                       {
+                               // node name test
+                               nt_name = _lexer.contents();
+                               _lexer.next();
+
+                               // was it an axis name?
+                               if (_lexer.current() == lex_double_colon)
+                               {
+                                       // parse axis name
+                                       if (axis_specified)
+                                               return error("Two axis specifiers in one step");
+
+                                       axis = parse_axis_name(nt_name, axis_specified);
+
+                                       if (!axis_specified)
+                                               return error("Unknown axis");
+
+                                       // read actual node test
+                                       _lexer.next();
+
+                                       if (_lexer.current() == lex_multiply)
+                                       {
+                                               nt_type = nodetest_all;
+                                               nt_name = xpath_lexer_string();
+                                               _lexer.next();
+                                       }
+                                       else if (_lexer.current() == lex_string)
+                                       {
+                                               nt_name = _lexer.contents();
+                                               _lexer.next();
+                                       }
+                                       else
+                                       {
+                                               return error("Unrecognized node test");
+                                       }
+                               }
+
+                               if (nt_type == nodetest_none)
+                               {
+                                       // node type test or processing-instruction
+                                       if (_lexer.current() == lex_open_brace)
+                                       {
+                                               _lexer.next();
+
+                                               if (_lexer.current() == lex_close_brace)
+                                               {
+                                                       _lexer.next();
+
+                                                       nt_type = parse_node_test_type(nt_name);
+
+                                                       if (nt_type == nodetest_none)
+                                                               return error("Unrecognized node type");
+
+                                                       nt_name = xpath_lexer_string();
+                                               }
+                                               else if (nt_name == PUGIXML_TEXT("processing-instruction"))
+                                               {
+                                                       if (_lexer.current() != lex_quoted_string)
+                                                               return error("Only literals are allowed as arguments to processing-instruction()");
+
+                                                       nt_type = nodetest_pi;
+                                                       nt_name = _lexer.contents();
+                                                       _lexer.next();
+
+                                                       if (_lexer.current() != lex_close_brace)
+                                                               return error("Unmatched brace near processing-instruction()");
+                                                       _lexer.next();
+                                               }
+                                               else
+                                               {
+                                                       return error("Unmatched brace near node type test");
+                                               }
+                                       }
+                                       // QName or NCName:*
+                                       else
+                                       {
+                                               if (nt_name.end - nt_name.begin > 2 && nt_name.end[-2] == ':' && nt_name.end[-1] == '*') // NCName:*
+                                               {
+                                                       nt_name.end--; // erase *
+
+                                                       nt_type = nodetest_all_in_namespace;
+                                               }
+                                               else
+                                               {
+                                                       nt_type = nodetest_name;
+                                               }
+                                       }
+                               }
+                       }
+                       else if (_lexer.current() == lex_multiply)
+                       {
+                               nt_type = nodetest_all;
+                               _lexer.next();
+                       }
+                       else
+                       {
+                               return error("Unrecognized node test");
+                       }
+
+                       const char_t* nt_name_copy = alloc_string(nt_name);
+                       if (!nt_name_copy) return 0;
+
+                       xpath_ast_node* n = alloc_node(ast_step, set, axis, nt_type, nt_name_copy);
+                       if (!n) return 0;
+
+                       xpath_ast_node* last = 0;
+
+                       while (_lexer.current() == lex_open_square_brace)
+                       {
+                               _lexer.next();
+
+                               xpath_ast_node* expr = parse_expression();
+                               if (!expr) return 0;
+
+                               xpath_ast_node* pred = alloc_node(ast_predicate, 0, expr, predicate_default);
+                               if (!pred) return 0;
+
+                               if (_lexer.current() != lex_close_square_brace)
+                                       return error("Expected ']' to match an opening '['");
+                               _lexer.next();
+
+                               if (last) last->set_next(pred);
+                               else n->set_right(pred);
+
+                               last = pred;
+                       }
+
+                       return n;
+               }
+
+               // RelativeLocationPath ::= Step | RelativeLocationPath '/' Step | RelativeLocationPath '//' Step
+               xpath_ast_node* parse_relative_location_path(xpath_ast_node* set)
+               {
+                       xpath_ast_node* n = parse_step(set);
+                       if (!n) return 0;
+
+                       while (_lexer.current() == lex_slash || _lexer.current() == lex_double_slash)
+                       {
+                               lexeme_t l = _lexer.current();
+                               _lexer.next();
+
+                               if (l == lex_double_slash)
+                               {
+                                       n = alloc_node(ast_step, n, axis_descendant_or_self, nodetest_type_node, 0);
+                                       if (!n) return 0;
+                               }
+
+                               n = parse_step(n);
+                               if (!n) return 0;
+                       }
+
+                       return n;
+               }
+
+               // LocationPath ::= RelativeLocationPath | AbsoluteLocationPath
+               // AbsoluteLocationPath ::= '/' RelativeLocationPath? | '//' RelativeLocationPath
+               xpath_ast_node* parse_location_path()
+               {
+                       if (_lexer.current() == lex_slash)
+                       {
+                               _lexer.next();
+
+                               xpath_ast_node* n = alloc_node(ast_step_root, xpath_type_node_set);
+                               if (!n) return 0;
+
+                               // relative location path can start from axis_attribute, dot, double_dot, multiply and string lexemes; any other lexeme means standalone root path
+                               lexeme_t l = _lexer.current();
+
+                               if (l == lex_string || l == lex_axis_attribute || l == lex_dot || l == lex_double_dot || l == lex_multiply)
+                                       return parse_relative_location_path(n);
+                               else
+                                       return n;
+                       }
+                       else if (_lexer.current() == lex_double_slash)
+                       {
+                               _lexer.next();
+
+                               xpath_ast_node* n = alloc_node(ast_step_root, xpath_type_node_set);
+                               if (!n) return 0;
+
+                               n = alloc_node(ast_step, n, axis_descendant_or_self, nodetest_type_node, 0);
+                               if (!n) return 0;
+
+                               return parse_relative_location_path(n);
+                       }
+
+                       // else clause moved outside of if because of bogus warning 'control may reach end of non-void function being inlined' in gcc 4.0.1
+                       return parse_relative_location_path(0);
+               }
+
+               // PathExpr ::= LocationPath
+               //                              | FilterExpr
+               //                              | FilterExpr '/' RelativeLocationPath
+               //                              | FilterExpr '//' RelativeLocationPath
+               // UnionExpr ::= PathExpr | UnionExpr '|' PathExpr
+               // UnaryExpr ::= UnionExpr | '-' UnaryExpr
+               xpath_ast_node* parse_path_or_unary_expression()
+               {
+                       // Clarification.
+                       // PathExpr begins with either LocationPath or FilterExpr.
+                       // FilterExpr begins with PrimaryExpr
+                       // PrimaryExpr begins with '$' in case of it being a variable reference,
+                       // '(' in case of it being an expression, string literal, number constant or
+                       // function call.
+                       if (_lexer.current() == lex_var_ref || _lexer.current() == lex_open_brace ||
+                               _lexer.current() == lex_quoted_string || _lexer.current() == lex_number ||
+                               _lexer.current() == lex_string)
+                       {
+                               if (_lexer.current() == lex_string)
+                               {
+                                       // This is either a function call, or not - if not, we shall proceed with location path
+                                       const char_t* state = _lexer.state();
+
+                                       while (PUGI__IS_CHARTYPE(*state, ct_space)) ++state;
+
+                                       if (*state != '(')
+                                               return parse_location_path();
+
+                                       // This looks like a function call; however this still can be a node-test. Check it.
+                                       if (parse_node_test_type(_lexer.contents()) != nodetest_none)
+                                               return parse_location_path();
+                               }
+
+                               xpath_ast_node* n = parse_filter_expression();
+                               if (!n) return 0;
+
+                               if (_lexer.current() == lex_slash || _lexer.current() == lex_double_slash)
+                               {
+                                       lexeme_t l = _lexer.current();
+                                       _lexer.next();
+
+                                       if (l == lex_double_slash)
+                                       {
+                                               if (n->rettype() != xpath_type_node_set)
+                                                       return error("Step has to be applied to node set");
+
+                                               n = alloc_node(ast_step, n, axis_descendant_or_self, nodetest_type_node, 0);
+                                               if (!n) return 0;
+                                       }
+
+                                       // select from location path
+                                       return parse_relative_location_path(n);
+                               }
+
+                               return n;
+                       }
+                       else if (_lexer.current() == lex_minus)
+                       {
+                               _lexer.next();
+
+                               // precedence 7+ - only parses union expressions
+                               xpath_ast_node* n = parse_expression(7);
+                               if (!n) return 0;
+
+                               return alloc_node(ast_op_negate, xpath_type_number, n);
+                       }
+                       else
+                       {
+                               return parse_location_path();
+                       }
+               }
+
+               struct binary_op_t
+               {
+                       ast_type_t asttype;
+                       xpath_value_type rettype;
+                       int precedence;
+
+                       binary_op_t(): asttype(ast_unknown), rettype(xpath_type_none), precedence(0)
+                       {
+                       }
+
+                       binary_op_t(ast_type_t asttype_, xpath_value_type rettype_, int precedence_): asttype(asttype_), rettype(rettype_), precedence(precedence_)
+                       {
+                       }
+
+                       static binary_op_t parse(xpath_lexer& lexer)
+                       {
+                               switch (lexer.current())
+                               {
+                               case lex_string:
+                                       if (lexer.contents() == PUGIXML_TEXT("or"))
+                                               return binary_op_t(ast_op_or, xpath_type_boolean, 1);
+                                       else if (lexer.contents() == PUGIXML_TEXT("and"))
+                                               return binary_op_t(ast_op_and, xpath_type_boolean, 2);
+                                       else if (lexer.contents() == PUGIXML_TEXT("div"))
+                                               return binary_op_t(ast_op_divide, xpath_type_number, 6);
+                                       else if (lexer.contents() == PUGIXML_TEXT("mod"))
+                                               return binary_op_t(ast_op_mod, xpath_type_number, 6);
+                                       else
+                                               return binary_op_t();
+
+                               case lex_equal:
+                                       return binary_op_t(ast_op_equal, xpath_type_boolean, 3);
+
+                               case lex_not_equal:
+                                       return binary_op_t(ast_op_not_equal, xpath_type_boolean, 3);
+
+                               case lex_less:
+                                       return binary_op_t(ast_op_less, xpath_type_boolean, 4);
+
+                               case lex_greater:
+                                       return binary_op_t(ast_op_greater, xpath_type_boolean, 4);
+
+                               case lex_less_or_equal:
+                                       return binary_op_t(ast_op_less_or_equal, xpath_type_boolean, 4);
+
+                               case lex_greater_or_equal:
+                                       return binary_op_t(ast_op_greater_or_equal, xpath_type_boolean, 4);
+
+                               case lex_plus:
+                                       return binary_op_t(ast_op_add, xpath_type_number, 5);
+
+                               case lex_minus:
+                                       return binary_op_t(ast_op_subtract, xpath_type_number, 5);
+
+                               case lex_multiply:
+                                       return binary_op_t(ast_op_multiply, xpath_type_number, 6);
+
+                               case lex_union:
+                                       return binary_op_t(ast_op_union, xpath_type_node_set, 7);
+
+                               default:
+                                       return binary_op_t();
+                               }
+                       }
+               };
+
+               xpath_ast_node* parse_expression_rec(xpath_ast_node* lhs, int limit)
+               {
+                       binary_op_t op = binary_op_t::parse(_lexer);
+
+                       while (op.asttype != ast_unknown && op.precedence >= limit)
+                       {
+                               _lexer.next();
+
+                               xpath_ast_node* rhs = parse_path_or_unary_expression();
+                               if (!rhs) return 0;
+
+                               binary_op_t nextop = binary_op_t::parse(_lexer);
+
+                               while (nextop.asttype != ast_unknown && nextop.precedence > op.precedence)
+                               {
+                                       rhs = parse_expression_rec(rhs, nextop.precedence);
+                                       if (!rhs) return 0;
+
+                                       nextop = binary_op_t::parse(_lexer);
+                               }
+
+                               if (op.asttype == ast_op_union && (lhs->rettype() != xpath_type_node_set || rhs->rettype() != xpath_type_node_set))
+                                       return error("Union operator has to be applied to node sets");
+
+                               lhs = alloc_node(op.asttype, op.rettype, lhs, rhs);
+                               if (!lhs) return 0;
+
+                               op = binary_op_t::parse(_lexer);
+                       }
+
+                       return lhs;
+               }
+
+               // Expr ::= OrExpr
+               // OrExpr ::= AndExpr | OrExpr 'or' AndExpr
+               // AndExpr ::= EqualityExpr | AndExpr 'and' EqualityExpr
+               // EqualityExpr ::= RelationalExpr
+               //                                      | EqualityExpr '=' RelationalExpr
+               //                                      | EqualityExpr '!=' RelationalExpr
+               // RelationalExpr ::= AdditiveExpr
+               //                                        | RelationalExpr '<' AdditiveExpr
+               //                                        | RelationalExpr '>' AdditiveExpr
+               //                                        | RelationalExpr '<=' AdditiveExpr
+               //                                        | RelationalExpr '>=' AdditiveExpr
+               // AdditiveExpr ::= MultiplicativeExpr
+               //                                      | AdditiveExpr '+' MultiplicativeExpr
+               //                                      | AdditiveExpr '-' MultiplicativeExpr
+               // MultiplicativeExpr ::= UnaryExpr
+               //                                                | MultiplicativeExpr '*' UnaryExpr
+               //                                                | MultiplicativeExpr 'div' UnaryExpr
+               //                                                | MultiplicativeExpr 'mod' UnaryExpr
+               xpath_ast_node* parse_expression(int limit = 0)
+               {
+                       xpath_ast_node* n = parse_path_or_unary_expression();
+                       if (!n) return 0;
+
+                       return parse_expression_rec(n, limit);
+               }
+
+               xpath_parser(const char_t* query, xpath_variable_set* variables, xpath_allocator* alloc, xpath_parse_result* result): _alloc(alloc), _lexer(query), _query(query), _variables(variables), _result(result)
+               {
+               }
+
+               xpath_ast_node* parse()
+               {
+                       xpath_ast_node* n = parse_expression();
+                       if (!n) return 0;
+
+                       // check if there are unparsed tokens left
+                       if (_lexer.current() != lex_eof)
+                               return error("Incorrect query");
+
+                       return n;
+               }
+
+               static xpath_ast_node* parse(const char_t* query, xpath_variable_set* variables, xpath_allocator* alloc, xpath_parse_result* result)
+               {
+                       xpath_parser parser(query, variables, alloc, result);
+
+                       return parser.parse();
+               }
+       };
+
+       struct xpath_query_impl
+       {
+               static xpath_query_impl* create()
+               {
+                       void* memory = xml_memory::allocate(sizeof(xpath_query_impl));
+                       if (!memory) return 0;
+
+                       return new (memory) xpath_query_impl();
+               }
+
+               static void destroy(xpath_query_impl* impl)
+               {
+                       // free all allocated pages
+                       impl->alloc.release();
+
+                       // free allocator memory (with the first page)
+                       xml_memory::deallocate(impl);
+               }
+
+               xpath_query_impl(): root(0), alloc(&block, &oom), oom(false)
+               {
+                       block.next = 0;
+                       block.capacity = sizeof(block.data);
+               }
+
+               xpath_ast_node* root;
+               xpath_allocator alloc;
+               xpath_memory_block block;
+               bool oom;
+       };
+
+       PUGI__FN impl::xpath_ast_node* evaluate_node_set_prepare(xpath_query_impl* impl)
+       {
+               if (!impl) return 0;
+
+               if (impl->root->rettype() != xpath_type_node_set)
+               {
+               #ifdef PUGIXML_NO_EXCEPTIONS
+                       return 0;
+               #else
+                       xpath_parse_result res;
+                       res.error = "Expression does not evaluate to node set";
+
+                       throw xpath_exception(res);
+               #endif
+               }
+
+               return impl->root;
+       }
+PUGI__NS_END
+
+namespace pugi
+{
+#ifndef PUGIXML_NO_EXCEPTIONS
+       PUGI__FN xpath_exception::xpath_exception(const xpath_parse_result& result_): _result(result_)
+       {
+               assert(_result.error);
+       }
+
+       PUGI__FN const char* xpath_exception::what() const throw()
+       {
+               return _result.error;
+       }
+
+       PUGI__FN const xpath_parse_result& xpath_exception::result() const
+       {
+               return _result;
+       }
+#endif
+
+       PUGI__FN xpath_node::xpath_node()
+       {
+       }
+
+       PUGI__FN xpath_node::xpath_node(const xml_node& node_): _node(node_)
+       {
+       }
+
+       PUGI__FN xpath_node::xpath_node(const xml_attribute& attribute_, const xml_node& parent_): _node(attribute_ ? parent_ : xml_node()), _attribute(attribute_)
+       {
+       }
+
+       PUGI__FN xml_node xpath_node::node() const
+       {
+               return _attribute ? xml_node() : _node;
+       }
+
+       PUGI__FN xml_attribute xpath_node::attribute() const
+       {
+               return _attribute;
+       }
+
+       PUGI__FN xml_node xpath_node::parent() const
+       {
+               return _attribute ? _node : _node.parent();
+       }
+
+       PUGI__FN static void unspecified_bool_xpath_node(xpath_node***)
+       {
+       }
+
+       PUGI__FN xpath_node::operator xpath_node::unspecified_bool_type() const
+       {
+               return (_node || _attribute) ? unspecified_bool_xpath_node : 0;
+       }
+
+       PUGI__FN bool xpath_node::operator!() const
+       {
+               return !(_node || _attribute);
+       }
+
+       PUGI__FN bool xpath_node::operator==(const xpath_node& n) const
+       {
+               return _node == n._node && _attribute == n._attribute;
+       }
+
+       PUGI__FN bool xpath_node::operator!=(const xpath_node& n) const
+       {
+               return _node != n._node || _attribute != n._attribute;
+       }
+
+#ifdef __BORLANDC__
+       PUGI__FN bool operator&&(const xpath_node& lhs, bool rhs)
+       {
+               return (bool)lhs && rhs;
+       }
+
+       PUGI__FN bool operator||(const xpath_node& lhs, bool rhs)
+       {
+               return (bool)lhs || rhs;
+       }
+#endif
+
+       PUGI__FN void xpath_node_set::_assign(const_iterator begin_, const_iterator end_, type_t type_)
+       {
+               assert(begin_ <= end_);
+
+               size_t size_ = static_cast<size_t>(end_ - begin_);
+
+               if (size_ <= 1)
+               {
+                       // deallocate old buffer
+                       if (_begin != &_storage) impl::xml_memory::deallocate(_begin);
+
+                       // use internal buffer
+                       if (begin_ != end_) _storage = *begin_;
+
+                       _begin = &_storage;
+                       _end = &_storage + size_;
+                       _type = type_;
+               }
+               else
+               {
+                       // make heap copy
+                       xpath_node* storage = static_cast<xpath_node*>(impl::xml_memory::allocate(size_ * sizeof(xpath_node)));
+
+                       if (!storage)
+                       {
+                       #ifdef PUGIXML_NO_EXCEPTIONS
+                               return;
+                       #else
+                               throw std::bad_alloc();
+                       #endif
+                       }
+
+                       memcpy(storage, begin_, size_ * sizeof(xpath_node));
+
+                       // deallocate old buffer
+                       if (_begin != &_storage) impl::xml_memory::deallocate(_begin);
+
+                       // finalize
+                       _begin = storage;
+                       _end = storage + size_;
+                       _type = type_;
+               }
+       }
+
+#ifdef PUGIXML_HAS_MOVE
+       PUGI__FN void xpath_node_set::_move(xpath_node_set& rhs) PUGIXML_NOEXCEPT
+       {
+               _type = rhs._type;
+               _storage = rhs._storage;
+               _begin = (rhs._begin == &rhs._storage) ? &_storage : rhs._begin;
+               _end = _begin + (rhs._end - rhs._begin);
+
+               rhs._type = type_unsorted;
+               rhs._begin = &rhs._storage;
+               rhs._end = rhs._begin;
+       }
+#endif
+
+       PUGI__FN xpath_node_set::xpath_node_set(): _type(type_unsorted), _begin(&_storage), _end(&_storage)
+       {
+       }
+
+       PUGI__FN xpath_node_set::xpath_node_set(const_iterator begin_, const_iterator end_, type_t type_): _type(type_unsorted), _begin(&_storage), _end(&_storage)
+       {
+               _assign(begin_, end_, type_);
+       }
+
+       PUGI__FN xpath_node_set::~xpath_node_set()
+       {
+               if (_begin != &_storage)
+                       impl::xml_memory::deallocate(_begin);
+       }
+
+       PUGI__FN xpath_node_set::xpath_node_set(const xpath_node_set& ns): _type(type_unsorted), _begin(&_storage), _end(&_storage)
+       {
+               _assign(ns._begin, ns._end, ns._type);
+       }
+
+       PUGI__FN xpath_node_set& xpath_node_set::operator=(const xpath_node_set& ns)
+       {
+               if (this == &ns) return *this;
+
+               _assign(ns._begin, ns._end, ns._type);
+
+               return *this;
+       }
+
+#ifdef PUGIXML_HAS_MOVE
+       PUGI__FN xpath_node_set::xpath_node_set(xpath_node_set&& rhs) PUGIXML_NOEXCEPT: _type(type_unsorted), _begin(&_storage), _end(&_storage)
+       {
+               _move(rhs);
+       }
+
+       PUGI__FN xpath_node_set& xpath_node_set::operator=(xpath_node_set&& rhs) PUGIXML_NOEXCEPT
+       {
+               if (this == &rhs) return *this;
+
+               if (_begin != &_storage)
+                       impl::xml_memory::deallocate(_begin);
+
+               _move(rhs);
+
+               return *this;
+       }
+#endif
+
+       PUGI__FN xpath_node_set::type_t xpath_node_set::type() const
+       {
+               return _type;
+       }
+
+       PUGI__FN size_t xpath_node_set::size() const
+       {
+               return _end - _begin;
+       }
+
+       PUGI__FN bool xpath_node_set::empty() const
+       {
+               return _begin == _end;
+       }
+
+       PUGI__FN const xpath_node& xpath_node_set::operator[](size_t index) const
+       {
+               assert(index < size());
+               return _begin[index];
+       }
+
+       PUGI__FN xpath_node_set::const_iterator xpath_node_set::begin() const
+       {
+               return _begin;
+       }
+
+       PUGI__FN xpath_node_set::const_iterator xpath_node_set::end() const
+       {
+               return _end;
+       }
+
+       PUGI__FN void xpath_node_set::sort(bool reverse)
+       {
+               _type = impl::xpath_sort(_begin, _end, _type, reverse);
+       }
+
+       PUGI__FN xpath_node xpath_node_set::first() const
+       {
+               return impl::xpath_first(_begin, _end, _type);
+       }
+
+       PUGI__FN xpath_parse_result::xpath_parse_result(): error("Internal error"), offset(0)
+       {
+       }
+
+       PUGI__FN xpath_parse_result::operator bool() const
+       {
+               return error == 0;
+       }
+
+       PUGI__FN const char* xpath_parse_result::description() const
+       {
+               return error ? error : "No error";
+       }
+
+       PUGI__FN xpath_variable::xpath_variable(xpath_value_type type_): _type(type_), _next(0)
+       {
+       }
+
+       PUGI__FN const char_t* xpath_variable::name() const
+       {
+               switch (_type)
+               {
+               case xpath_type_node_set:
+                       return static_cast<const impl::xpath_variable_node_set*>(this)->name;
+
+               case xpath_type_number:
+                       return static_cast<const impl::xpath_variable_number*>(this)->name;
+
+               case xpath_type_string:
+                       return static_cast<const impl::xpath_variable_string*>(this)->name;
+
+               case xpath_type_boolean:
+                       return static_cast<const impl::xpath_variable_boolean*>(this)->name;
+
+               default:
+                       assert(false && "Invalid variable type"); // unreachable
+                       return 0;
+               }
+       }
+
+       PUGI__FN xpath_value_type xpath_variable::type() const
+       {
+               return _type;
+       }
+
+       PUGI__FN bool xpath_variable::get_boolean() const
+       {
+               return (_type == xpath_type_boolean) ? static_cast<const impl::xpath_variable_boolean*>(this)->value : false;
+       }
+
+       PUGI__FN double xpath_variable::get_number() const
+       {
+               return (_type == xpath_type_number) ? static_cast<const impl::xpath_variable_number*>(this)->value : impl::gen_nan();
+       }
+
+       PUGI__FN const char_t* xpath_variable::get_string() const
+       {
+               const char_t* value = (_type == xpath_type_string) ? static_cast<const impl::xpath_variable_string*>(this)->value : 0;
+               return value ? value : PUGIXML_TEXT("");
+       }
+
+       PUGI__FN const xpath_node_set& xpath_variable::get_node_set() const
+       {
+               return (_type == xpath_type_node_set) ? static_cast<const impl::xpath_variable_node_set*>(this)->value : impl::dummy_node_set;
+       }
+
+       PUGI__FN bool xpath_variable::set(bool value)
+       {
+               if (_type != xpath_type_boolean) return false;
+
+               static_cast<impl::xpath_variable_boolean*>(this)->value = value;
+               return true;
+       }
+
+       PUGI__FN bool xpath_variable::set(double value)
+       {
+               if (_type != xpath_type_number) return false;
+
+               static_cast<impl::xpath_variable_number*>(this)->value = value;
+               return true;
+       }
+
+       PUGI__FN bool xpath_variable::set(const char_t* value)
+       {
+               if (_type != xpath_type_string) return false;
+
+               impl::xpath_variable_string* var = static_cast<impl::xpath_variable_string*>(this);
+
+               // duplicate string
+               size_t size = (impl::strlength(value) + 1) * sizeof(char_t);
+
+               char_t* copy = static_cast<char_t*>(impl::xml_memory::allocate(size));
+               if (!copy) return false;
+
+               memcpy(copy, value, size);
+
+               // replace old string
+               if (var->value) impl::xml_memory::deallocate(var->value);
+               var->value = copy;
+
+               return true;
+       }
+
+       PUGI__FN bool xpath_variable::set(const xpath_node_set& value)
+       {
+               if (_type != xpath_type_node_set) return false;
+
+               static_cast<impl::xpath_variable_node_set*>(this)->value = value;
+               return true;
+       }
+
+       PUGI__FN xpath_variable_set::xpath_variable_set()
+       {
+               for (size_t i = 0; i < sizeof(_data) / sizeof(_data[0]); ++i)
+                       _data[i] = 0;
+       }
+
+       PUGI__FN xpath_variable_set::~xpath_variable_set()
+       {
+               for (size_t i = 0; i < sizeof(_data) / sizeof(_data[0]); ++i)
+                       _destroy(_data[i]);
+       }
+
+       PUGI__FN xpath_variable_set::xpath_variable_set(const xpath_variable_set& rhs)
+       {
+               for (size_t i = 0; i < sizeof(_data) / sizeof(_data[0]); ++i)
+                       _data[i] = 0;
+
+               _assign(rhs);
+       }
+
+       PUGI__FN xpath_variable_set& xpath_variable_set::operator=(const xpath_variable_set& rhs)
+       {
+               if (this == &rhs) return *this;
+
+               _assign(rhs);
+
+               return *this;
+       }
+
+#ifdef PUGIXML_HAS_MOVE
+       PUGI__FN xpath_variable_set::xpath_variable_set(xpath_variable_set&& rhs) PUGIXML_NOEXCEPT
+       {
+               for (size_t i = 0; i < sizeof(_data) / sizeof(_data[0]); ++i)
+               {
+                       _data[i] = rhs._data[i];
+                       rhs._data[i] = 0;
+               }
+       }
+
+       PUGI__FN xpath_variable_set& xpath_variable_set::operator=(xpath_variable_set&& rhs) PUGIXML_NOEXCEPT
+       {
+               for (size_t i = 0; i < sizeof(_data) / sizeof(_data[0]); ++i)
+               {
+                       _destroy(_data[i]);
+
+                       _data[i] = rhs._data[i];
+                       rhs._data[i] = 0;
+               }
+
+               return *this;
+       }
+#endif
+
+       PUGI__FN void xpath_variable_set::_assign(const xpath_variable_set& rhs)
+       {
+               xpath_variable_set temp;
+
+               for (size_t i = 0; i < sizeof(_data) / sizeof(_data[0]); ++i)
+                       if (rhs._data[i] && !_clone(rhs._data[i], &temp._data[i]))
+                               return;
+
+               _swap(temp);
+       }
+
+       PUGI__FN void xpath_variable_set::_swap(xpath_variable_set& rhs)
+       {
+               for (size_t i = 0; i < sizeof(_data) / sizeof(_data[0]); ++i)
+               {
+                       xpath_variable* chain = _data[i];
+
+                       _data[i] = rhs._data[i];
+                       rhs._data[i] = chain;
+               }
+       }
+
+       PUGI__FN xpath_variable* xpath_variable_set::_find(const char_t* name) const
+       {
+               const size_t hash_size = sizeof(_data) / sizeof(_data[0]);
+               size_t hash = impl::hash_string(name) % hash_size;
+
+               // look for existing variable
+               for (xpath_variable* var = _data[hash]; var; var = var->_next)
+                       if (impl::strequal(var->name(), name))
+                               return var;
+
+               return 0;
+       }
+
+       PUGI__FN bool xpath_variable_set::_clone(xpath_variable* var, xpath_variable** out_result)
+       {
+               xpath_variable* last = 0;
+
+               while (var)
+               {
+                       // allocate storage for new variable
+                       xpath_variable* nvar = impl::new_xpath_variable(var->_type, var->name());
+                       if (!nvar) return false;
+
+                       // link the variable to the result immediately to handle failures gracefully
+                       if (last)
+                               last->_next = nvar;
+                       else
+                               *out_result = nvar;
+
+                       last = nvar;
+
+                       // copy the value; this can fail due to out-of-memory conditions
+                       if (!impl::copy_xpath_variable(nvar, var)) return false;
+
+                       var = var->_next;
+               }
+
+               return true;
+       }
+
+       PUGI__FN void xpath_variable_set::_destroy(xpath_variable* var)
+       {
+               while (var)
+               {
+                       xpath_variable* next = var->_next;
+
+                       impl::delete_xpath_variable(var->_type, var);
+
+                       var = next;
+               }
+       }
+
+       PUGI__FN xpath_variable* xpath_variable_set::add(const char_t* name, xpath_value_type type)
+       {
+               const size_t hash_size = sizeof(_data) / sizeof(_data[0]);
+               size_t hash = impl::hash_string(name) % hash_size;
+
+               // look for existing variable
+               for (xpath_variable* var = _data[hash]; var; var = var->_next)
+                       if (impl::strequal(var->name(), name))
+                               return var->type() == type ? var : 0;
+
+               // add new variable
+               xpath_variable* result = impl::new_xpath_variable(type, name);
+
+               if (result)
+               {
+                       result->_next = _data[hash];
+
+                       _data[hash] = result;
+               }
+
+               return result;
+       }
+
+       PUGI__FN bool xpath_variable_set::set(const char_t* name, bool value)
+       {
+               xpath_variable* var = add(name, xpath_type_boolean);
+               return var ? var->set(value) : false;
+       }
+
+       PUGI__FN bool xpath_variable_set::set(const char_t* name, double value)
+       {
+               xpath_variable* var = add(name, xpath_type_number);
+               return var ? var->set(value) : false;
+       }
+
+       PUGI__FN bool xpath_variable_set::set(const char_t* name, const char_t* value)
+       {
+               xpath_variable* var = add(name, xpath_type_string);
+               return var ? var->set(value) : false;
+       }
+
+       PUGI__FN bool xpath_variable_set::set(const char_t* name, const xpath_node_set& value)
+       {
+               xpath_variable* var = add(name, xpath_type_node_set);
+               return var ? var->set(value) : false;
+       }
+
+       PUGI__FN xpath_variable* xpath_variable_set::get(const char_t* name)
+       {
+               return _find(name);
+       }
+
+       PUGI__FN const xpath_variable* xpath_variable_set::get(const char_t* name) const
+       {
+               return _find(name);
+       }
+
+       PUGI__FN xpath_query::xpath_query(const char_t* query, xpath_variable_set* variables): _impl(0)
+       {
+               impl::xpath_query_impl* qimpl = impl::xpath_query_impl::create();
+
+               if (!qimpl)
+               {
+               #ifdef PUGIXML_NO_EXCEPTIONS
+                       _result.error = "Out of memory";
+               #else
+                       throw std::bad_alloc();
+               #endif
+               }
+               else
+               {
+                       using impl::auto_deleter; // MSVC7 workaround
+                       auto_deleter<impl::xpath_query_impl> impl(qimpl, impl::xpath_query_impl::destroy);
+
+                       qimpl->root = impl::xpath_parser::parse(query, variables, &qimpl->alloc, &_result);
+
+                       if (qimpl->root)
+                       {
+                               qimpl->root->optimize(&qimpl->alloc);
+
+                               _impl = impl.release();
+                               _result.error = 0;
+                       }
+                       else
+                       {
+                       #ifdef PUGIXML_NO_EXCEPTIONS
+                               if (qimpl->oom) _result.error = "Out of memory";
+                       #else
+                               if (qimpl->oom) throw std::bad_alloc();
+                               throw xpath_exception(_result);
+                       #endif
+                       }
+               }
+       }
+
+       PUGI__FN xpath_query::xpath_query(): _impl(0)
+       {
+       }
+
+       PUGI__FN xpath_query::~xpath_query()
+       {
+               if (_impl)
+                       impl::xpath_query_impl::destroy(static_cast<impl::xpath_query_impl*>(_impl));
+       }
+
+#ifdef PUGIXML_HAS_MOVE
+       PUGI__FN xpath_query::xpath_query(xpath_query&& rhs) PUGIXML_NOEXCEPT
+       {
+               _impl = rhs._impl;
+               _result = rhs._result;
+               rhs._impl = 0;
+               rhs._result = xpath_parse_result();
+       }
+
+       PUGI__FN xpath_query& xpath_query::operator=(xpath_query&& rhs) PUGIXML_NOEXCEPT
+       {
+               if (this == &rhs) return *this;
+
+               if (_impl)
+                       impl::xpath_query_impl::destroy(static_cast<impl::xpath_query_impl*>(_impl));
+
+               _impl = rhs._impl;
+               _result = rhs._result;
+               rhs._impl = 0;
+               rhs._result = xpath_parse_result();
+
+               return *this;
+       }
+#endif
+
+       PUGI__FN xpath_value_type xpath_query::return_type() const
+       {
+               if (!_impl) return xpath_type_none;
+
+               return static_cast<impl::xpath_query_impl*>(_impl)->root->rettype();
+       }
+
+       PUGI__FN bool xpath_query::evaluate_boolean(const xpath_node& n) const
+       {
+               if (!_impl) return false;
+
+               impl::xpath_context c(n, 1, 1);
+               impl::xpath_stack_data sd;
+
+               bool r = static_cast<impl::xpath_query_impl*>(_impl)->root->eval_boolean(c, sd.stack);
+
+               if (sd.oom)
+               {
+               #ifdef PUGIXML_NO_EXCEPTIONS
+                       return false;
+               #else
+                       throw std::bad_alloc();
+               #endif
+               }
+
+               return r;
+       }
+
+       PUGI__FN double xpath_query::evaluate_number(const xpath_node& n) const
+       {
+               if (!_impl) return impl::gen_nan();
+
+               impl::xpath_context c(n, 1, 1);
+               impl::xpath_stack_data sd;
+
+               double r = static_cast<impl::xpath_query_impl*>(_impl)->root->eval_number(c, sd.stack);
+
+               if (sd.oom)
+               {
+               #ifdef PUGIXML_NO_EXCEPTIONS
+                       return impl::gen_nan();
+               #else
+                       throw std::bad_alloc();
+               #endif
+               }
+
+               return r;
+       }
+
+#ifndef PUGIXML_NO_STL
+       PUGI__FN string_t xpath_query::evaluate_string(const xpath_node& n) const
+       {
+               if (!_impl) return string_t();
+
+               impl::xpath_context c(n, 1, 1);
+               impl::xpath_stack_data sd;
+
+               impl::xpath_string r = static_cast<impl::xpath_query_impl*>(_impl)->root->eval_string(c, sd.stack);
+
+               if (sd.oom)
+               {
+               #ifdef PUGIXML_NO_EXCEPTIONS
+                       return string_t();
+               #else
+                       throw std::bad_alloc();
+               #endif
+               }
+
+               return string_t(r.c_str(), r.length());
+       }
+#endif
+
+       PUGI__FN size_t xpath_query::evaluate_string(char_t* buffer, size_t capacity, const xpath_node& n) const
+       {
+               impl::xpath_context c(n, 1, 1);
+               impl::xpath_stack_data sd;
+
+               impl::xpath_string r = _impl ? static_cast<impl::xpath_query_impl*>(_impl)->root->eval_string(c, sd.stack) : impl::xpath_string();
+
+               if (sd.oom)
+               {
+               #ifdef PUGIXML_NO_EXCEPTIONS
+                       r = impl::xpath_string();
+               #else
+                       throw std::bad_alloc();
+               #endif
+               }
+
+               size_t full_size = r.length() + 1;
+
+               if (capacity > 0)
+               {
+                       size_t size = (full_size < capacity) ? full_size : capacity;
+                       assert(size > 0);
+
+                       memcpy(buffer, r.c_str(), (size - 1) * sizeof(char_t));
+                       buffer[size - 1] = 0;
+               }
+
+               return full_size;
+       }
+
+       PUGI__FN xpath_node_set xpath_query::evaluate_node_set(const xpath_node& n) const
+       {
+               impl::xpath_ast_node* root = impl::evaluate_node_set_prepare(static_cast<impl::xpath_query_impl*>(_impl));
+               if (!root) return xpath_node_set();
+
+               impl::xpath_context c(n, 1, 1);
+               impl::xpath_stack_data sd;
+
+               impl::xpath_node_set_raw r = root->eval_node_set(c, sd.stack, impl::nodeset_eval_all);
+
+               if (sd.oom)
+               {
+               #ifdef PUGIXML_NO_EXCEPTIONS
+                       return xpath_node_set();
+               #else
+                       throw std::bad_alloc();
+               #endif
+               }
+
+               return xpath_node_set(r.begin(), r.end(), r.type());
+       }
+
+       PUGI__FN xpath_node xpath_query::evaluate_node(const xpath_node& n) const
+       {
+               impl::xpath_ast_node* root = impl::evaluate_node_set_prepare(static_cast<impl::xpath_query_impl*>(_impl));
+               if (!root) return xpath_node();
+
+               impl::xpath_context c(n, 1, 1);
+               impl::xpath_stack_data sd;
+
+               impl::xpath_node_set_raw r = root->eval_node_set(c, sd.stack, impl::nodeset_eval_first);
+
+               if (sd.oom)
+               {
+               #ifdef PUGIXML_NO_EXCEPTIONS
+                       return xpath_node();
+               #else
+                       throw std::bad_alloc();
+               #endif
+               }
+
+               return r.first();
+       }
+
+       PUGI__FN const xpath_parse_result& xpath_query::result() const
+       {
+               return _result;
+       }
+
+       PUGI__FN static void unspecified_bool_xpath_query(xpath_query***)
+       {
+       }
+
+       PUGI__FN xpath_query::operator xpath_query::unspecified_bool_type() const
+       {
+               return _impl ? unspecified_bool_xpath_query : 0;
+       }
+
+       PUGI__FN bool xpath_query::operator!() const
+       {
+               return !_impl;
+       }
+
+       PUGI__FN xpath_node xml_node::select_node(const char_t* query, xpath_variable_set* variables) const
+       {
+               xpath_query q(query, variables);
+               return q.evaluate_node(*this);
+       }
+
+       PUGI__FN xpath_node xml_node::select_node(const xpath_query& query) const
+       {
+               return query.evaluate_node(*this);
+       }
+
+       PUGI__FN xpath_node_set xml_node::select_nodes(const char_t* query, xpath_variable_set* variables) const
+       {
+               xpath_query q(query, variables);
+               return q.evaluate_node_set(*this);
+       }
+
+       PUGI__FN xpath_node_set xml_node::select_nodes(const xpath_query& query) const
+       {
+               return query.evaluate_node_set(*this);
+       }
+
+       PUGI__FN xpath_node xml_node::select_single_node(const char_t* query, xpath_variable_set* variables) const
+       {
+               xpath_query q(query, variables);
+               return q.evaluate_node(*this);
+       }
+
+       PUGI__FN xpath_node xml_node::select_single_node(const xpath_query& query) const
+       {
+               return query.evaluate_node(*this);
+       }
+}
+
+#endif
+
+#ifdef __BORLANDC__
+#      pragma option pop
+#endif
+
+// Intel C++ does not properly keep warning state for function templates,
+// so popping warning state at the end of translation unit leads to warnings in the middle.
+#if defined(_MSC_VER) && !defined(__INTEL_COMPILER)
+#      pragma warning(pop)
+#endif
+
+#if defined(_MSC_VER) && defined(__c2__)
+#      pragma clang diagnostic pop
+#endif
+
+// Undefine all local macros (makes sure we're not leaking macros in header-only mode)
+#undef PUGI__NO_INLINE
+#undef PUGI__UNLIKELY
+#undef PUGI__STATIC_ASSERT
+#undef PUGI__DMC_VOLATILE
+#undef PUGI__UNSIGNED_OVERFLOW
+#undef PUGI__MSVC_CRT_VERSION
+#undef PUGI__SNPRINTF
+#undef PUGI__NS_BEGIN
+#undef PUGI__NS_END
+#undef PUGI__FN
+#undef PUGI__FN_NO_INLINE
+#undef PUGI__GETHEADER_IMPL
+#undef PUGI__GETPAGE_IMPL
+#undef PUGI__GETPAGE
+#undef PUGI__NODETYPE
+#undef PUGI__IS_CHARTYPE_IMPL
+#undef PUGI__IS_CHARTYPE
+#undef PUGI__IS_CHARTYPEX
+#undef PUGI__ENDSWITH
+#undef PUGI__SKIPWS
+#undef PUGI__OPTSET
+#undef PUGI__PUSHNODE
+#undef PUGI__POPNODE
+#undef PUGI__SCANFOR
+#undef PUGI__SCANWHILE
+#undef PUGI__SCANWHILE_UNROLL
+#undef PUGI__ENDSEG
+#undef PUGI__THROW_ERROR
+#undef PUGI__CHECK_ERROR
+
+#endif
+
+/**
+ * Copyright (c) 2006-2018 Arseny Kapoulkine
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
diff --git a/utilities/pugixml/pugixml.hpp b/utilities/pugixml/pugixml.hpp
new file mode 100644 (file)
index 0000000..86403be
--- /dev/null
@@ -0,0 +1,1461 @@
+/**
+ * pugixml parser - version 1.9
+ * --------------------------------------------------------
+ * Copyright (C) 2006-2018, by Arseny Kapoulkine (arseny.kapoulkine@gmail.com)
+ * Report bugs and download new versions at http://pugixml.org/
+ *
+ * This library is distributed under the MIT License. See notice at the end
+ * of this file.
+ *
+ * This work is based on the pugxml parser, which is:
+ * Copyright (C) 2003, by Kristen Wegner (kristen@tima.net)
+ */
+
+#ifndef PUGIXML_VERSION
+// Define version macro; evaluates to major * 100 + minor so that it's safe to use in less-than comparisons
+#      define PUGIXML_VERSION 190
+#endif
+
+// Include user configuration file (this can define various configuration macros)
+#include "pugiconfig.hpp"
+
+#ifndef HEADER_PUGIXML_HPP
+#define HEADER_PUGIXML_HPP
+
+// Include stddef.h for size_t and ptrdiff_t
+#include <stddef.h>
+
+// Include exception header for XPath
+#if !defined(PUGIXML_NO_XPATH) && !defined(PUGIXML_NO_EXCEPTIONS)
+#      include <exception>
+#endif
+
+// Include STL headers
+#ifndef PUGIXML_NO_STL
+#      include <iterator>
+#      include <iosfwd>
+#      include <string>
+#endif
+
+// Macro for deprecated features
+#ifndef PUGIXML_DEPRECATED
+#      if defined(__GNUC__)
+#              define PUGIXML_DEPRECATED __attribute__((deprecated))
+#      elif defined(_MSC_VER) && _MSC_VER >= 1300
+#              define PUGIXML_DEPRECATED __declspec(deprecated)
+#      else
+#              define PUGIXML_DEPRECATED
+#      endif
+#endif
+
+// If no API is defined, assume default
+#ifndef PUGIXML_API
+#      define PUGIXML_API
+#endif
+
+// If no API for classes is defined, assume default
+#ifndef PUGIXML_CLASS
+#      define PUGIXML_CLASS PUGIXML_API
+#endif
+
+// If no API for functions is defined, assume default
+#ifndef PUGIXML_FUNCTION
+#      define PUGIXML_FUNCTION PUGIXML_API
+#endif
+
+// If the platform is known to have long long support, enable long long functions
+#ifndef PUGIXML_HAS_LONG_LONG
+#      if __cplusplus >= 201103
+#              define PUGIXML_HAS_LONG_LONG
+#      elif defined(_MSC_VER) && _MSC_VER >= 1400
+#              define PUGIXML_HAS_LONG_LONG
+#      endif
+#endif
+
+// If the platform is known to have move semantics support, compile move ctor/operator implementation
+#ifndef PUGIXML_HAS_MOVE
+#      if __cplusplus >= 201103
+#              define PUGIXML_HAS_MOVE
+#      elif defined(_MSC_VER) && _MSC_VER >= 1600
+#              define PUGIXML_HAS_MOVE
+#      endif
+#endif
+
+// If C++ is 2011 or higher, add 'noexcept' specifiers
+#ifndef PUGIXML_NOEXCEPT
+#      if __cplusplus >= 201103
+#              define PUGIXML_NOEXCEPT noexcept
+#      elif defined(_MSC_VER) && _MSC_VER >= 1900
+#              define PUGIXML_NOEXCEPT noexcept
+#      else
+#              define PUGIXML_NOEXCEPT
+#      endif
+#endif
+
+// Some functions can not be noexcept in compact mode
+#ifdef PUGIXML_COMPACT
+#      define PUGIXML_NOEXCEPT_IF_NOT_COMPACT
+#else
+#      define PUGIXML_NOEXCEPT_IF_NOT_COMPACT PUGIXML_NOEXCEPT
+#endif
+
+// If C++ is 2011 or higher, add 'override' qualifiers
+#ifndef PUGIXML_OVERRIDE
+#      if __cplusplus >= 201103
+#              define PUGIXML_OVERRIDE override
+#      elif defined(_MSC_VER) && _MSC_VER >= 1700
+#              define PUGIXML_OVERRIDE override
+#      else
+#              define PUGIXML_OVERRIDE
+#      endif
+#endif
+
+// Character interface macros
+#ifdef PUGIXML_WCHAR_MODE
+#      define PUGIXML_TEXT(t) L ## t
+#      define PUGIXML_CHAR wchar_t
+#else
+#      define PUGIXML_TEXT(t) t
+#      define PUGIXML_CHAR char
+#endif
+
+namespace pugi
+{
+       // Character type used for all internal storage and operations; depends on PUGIXML_WCHAR_MODE
+       typedef PUGIXML_CHAR char_t;
+
+#ifndef PUGIXML_NO_STL
+       // String type used for operations that work with STL string; depends on PUGIXML_WCHAR_MODE
+       typedef std::basic_string<PUGIXML_CHAR, std::char_traits<PUGIXML_CHAR>, std::allocator<PUGIXML_CHAR> > string_t;
+#endif
+}
+
+// The PugiXML namespace
+namespace pugi
+{
+       // Tree node types
+       enum xml_node_type
+       {
+               node_null,                      // Empty (null) node handle
+               node_document,          // A document tree's absolute root
+               node_element,           // Element tag, i.e. '<node/>'
+               node_pcdata,            // Plain character data, i.e. 'text'
+               node_cdata,                     // Character data, i.e. '<![CDATA[text]]>'
+               node_comment,           // Comment tag, i.e. '<!-- text -->'
+               node_pi,                        // Processing instruction, i.e. '<?name?>'
+               node_declaration,       // Document declaration, i.e. '<?xml version="1.0"?>'
+               node_doctype            // Document type declaration, i.e. '<!DOCTYPE doc>'
+       };
+
+       // Parsing options
+
+       // Minimal parsing mode (equivalent to turning all other flags off).
+       // Only elements and PCDATA sections are added to the DOM tree, no text conversions are performed.
+       const unsigned int parse_minimal = 0x0000;
+
+       // This flag determines if processing instructions (node_pi) are added to the DOM tree. This flag is off by default.
+       const unsigned int parse_pi = 0x0001;
+
+       // This flag determines if comments (node_comment) are added to the DOM tree. This flag is off by default.
+       const unsigned int parse_comments = 0x0002;
+
+       // This flag determines if CDATA sections (node_cdata) are added to the DOM tree. This flag is on by default.
+       const unsigned int parse_cdata = 0x0004;
+
+       // This flag determines if plain character data (node_pcdata) that consist only of whitespace are added to the DOM tree.
+       // This flag is off by default; turning it on usually results in slower parsing and more memory consumption.
+       const unsigned int parse_ws_pcdata = 0x0008;
+
+       // This flag determines if character and entity references are expanded during parsing. This flag is on by default.
+       const unsigned int parse_escapes = 0x0010;
+
+       // This flag determines if EOL characters are normalized (converted to #xA) during parsing. This flag is on by default.
+       const unsigned int parse_eol = 0x0020;
+
+       // This flag determines if attribute values are normalized using CDATA normalization rules during parsing. This flag is on by default.
+       const unsigned int parse_wconv_attribute = 0x0040;
+
+       // This flag determines if attribute values are normalized using NMTOKENS normalization rules during parsing. This flag is off by default.
+       const unsigned int parse_wnorm_attribute = 0x0080;
+
+       // This flag determines if document declaration (node_declaration) is added to the DOM tree. This flag is off by default.
+       const unsigned int parse_declaration = 0x0100;
+
+       // This flag determines if document type declaration (node_doctype) is added to the DOM tree. This flag is off by default.
+       const unsigned int parse_doctype = 0x0200;
+
+       // This flag determines if plain character data (node_pcdata) that is the only child of the parent node and that consists only
+       // of whitespace is added to the DOM tree.
+       // This flag is off by default; turning it on may result in slower parsing and more memory consumption.
+       const unsigned int parse_ws_pcdata_single = 0x0400;
+
+       // This flag determines if leading and trailing whitespace is to be removed from plain character data. This flag is off by default.
+       const unsigned int parse_trim_pcdata = 0x0800;
+
+       // This flag determines if plain character data that does not have a parent node is added to the DOM tree, and if an empty document
+       // is a valid document. This flag is off by default.
+       const unsigned int parse_fragment = 0x1000;
+
+       // This flag determines if plain character data is be stored in the parent element's value. This significantly changes the structure of
+       // the document; this flag is only recommended for parsing documents with many PCDATA nodes in memory-constrained environments.
+       // This flag is off by default.
+       const unsigned int parse_embed_pcdata = 0x2000;
+
+       // The default parsing mode.
+       // Elements, PCDATA and CDATA sections are added to the DOM tree, character/reference entities are expanded,
+       // End-of-Line characters are normalized, attribute values are normalized using CDATA normalization rules.
+       const unsigned int parse_default = parse_cdata | parse_escapes | parse_wconv_attribute | parse_eol;
+
+       // The full parsing mode.
+       // Nodes of all types are added to the DOM tree, character/reference entities are expanded,
+       // End-of-Line characters are normalized, attribute values are normalized using CDATA normalization rules.
+       const unsigned int parse_full = parse_default | parse_pi | parse_comments | parse_declaration | parse_doctype;
+
+       // These flags determine the encoding of input data for XML document
+       enum xml_encoding
+       {
+               encoding_auto,          // Auto-detect input encoding using BOM or < / <? detection; use UTF8 if BOM is not found
+               encoding_utf8,          // UTF8 encoding
+               encoding_utf16_le,      // Little-endian UTF16
+               encoding_utf16_be,      // Big-endian UTF16
+               encoding_utf16,         // UTF16 with native endianness
+               encoding_utf32_le,      // Little-endian UTF32
+               encoding_utf32_be,      // Big-endian UTF32
+               encoding_utf32,         // UTF32 with native endianness
+               encoding_wchar,         // The same encoding wchar_t has (either UTF16 or UTF32)
+               encoding_latin1
+       };
+
+       // Formatting flags
+
+       // Indent the nodes that are written to output stream with as many indentation strings as deep the node is in DOM tree. This flag is on by default.
+       const unsigned int format_indent = 0x01;
+
+       // Write encoding-specific BOM to the output stream. This flag is off by default.
+       const unsigned int format_write_bom = 0x02;
+
+       // Use raw output mode (no indentation and no line breaks are written). This flag is off by default.
+       const unsigned int format_raw = 0x04;
+
+       // Omit default XML declaration even if there is no declaration in the document. This flag is off by default.
+       const unsigned int format_no_declaration = 0x08;
+
+       // Don't escape attribute values and PCDATA contents. This flag is off by default.
+       const unsigned int format_no_escapes = 0x10;
+
+       // Open file using text mode in xml_document::save_file. This enables special character (i.e. new-line) conversions on some systems. This flag is off by default.
+       const unsigned int format_save_file_text = 0x20;
+
+       // Write every attribute on a new line with appropriate indentation. This flag is off by default.
+       const unsigned int format_indent_attributes = 0x40;
+
+       // Don't output empty element tags, instead writing an explicit start and end tag even if there are no children. This flag is off by default.
+       const unsigned int format_no_empty_element_tags = 0x80;
+
+       // The default set of formatting flags.
+       // Nodes are indented depending on their depth in DOM tree, a default declaration is output if document has none.
+       const unsigned int format_default = format_indent;
+
+       // Forward declarations
+       struct xml_attribute_struct;
+       struct xml_node_struct;
+
+       class xml_node_iterator;
+       class xml_attribute_iterator;
+       class xml_named_node_iterator;
+
+       class xml_tree_walker;
+
+       struct xml_parse_result;
+
+       class xml_node;
+
+       class xml_text;
+
+       #ifndef PUGIXML_NO_XPATH
+       class xpath_node;
+       class xpath_node_set;
+       class xpath_query;
+       class xpath_variable_set;
+       #endif
+
+       // Range-based for loop support
+       template <typename It> class xml_object_range
+       {
+       public:
+               typedef It const_iterator;
+               typedef It iterator;
+
+               xml_object_range(It b, It e): _begin(b), _end(e)
+               {
+               }
+
+               It begin() const { return _begin; }
+               It end() const { return _end; }
+
+       private:
+               It _begin, _end;
+       };
+
+       // Writer interface for node printing (see xml_node::print)
+       class PUGIXML_CLASS xml_writer
+       {
+       public:
+               virtual ~xml_writer() {}
+
+               // Write memory chunk into stream/file/whatever
+               virtual void write(const void* data, size_t size) = 0;
+       };
+
+       // xml_writer implementation for FILE*
+       class PUGIXML_CLASS xml_writer_file: public xml_writer
+       {
+       public:
+               // Construct writer from a FILE* object; void* is used to avoid header dependencies on stdio
+               xml_writer_file(void* file);
+
+               virtual void write(const void* data, size_t size) PUGIXML_OVERRIDE;
+
+       private:
+               void* file;
+       };
+
+       #ifndef PUGIXML_NO_STL
+       // xml_writer implementation for streams
+       class PUGIXML_CLASS xml_writer_stream: public xml_writer
+       {
+       public:
+               // Construct writer from an output stream object
+               xml_writer_stream(std::basic_ostream<char, std::char_traits<char> >& stream);
+               xml_writer_stream(std::basic_ostream<wchar_t, std::char_traits<wchar_t> >& stream);
+
+               virtual void write(const void* data, size_t size) PUGIXML_OVERRIDE;
+
+       private:
+               std::basic_ostream<char, std::char_traits<char> >* narrow_stream;
+               std::basic_ostream<wchar_t, std::char_traits<wchar_t> >* wide_stream;
+       };
+       #endif
+
+       // A light-weight handle for manipulating attributes in DOM tree
+       class PUGIXML_CLASS xml_attribute
+       {
+               friend class xml_attribute_iterator;
+               friend class xml_node;
+
+       private:
+               xml_attribute_struct* _attr;
+
+               typedef void (*unspecified_bool_type)(xml_attribute***);
+
+       public:
+               // Default constructor. Constructs an empty attribute.
+               xml_attribute();
+
+               // Constructs attribute from internal pointer
+               explicit xml_attribute(xml_attribute_struct* attr);
+
+               // Safe bool conversion operator
+               operator unspecified_bool_type() const;
+
+               // Borland C++ workaround
+               bool operator!() const;
+
+               // Comparison operators (compares wrapped attribute pointers)
+               bool operator==(const xml_attribute& r) const;
+               bool operator!=(const xml_attribute& r) const;
+               bool operator<(const xml_attribute& r) const;
+               bool operator>(const xml_attribute& r) const;
+               bool operator<=(const xml_attribute& r) const;
+               bool operator>=(const xml_attribute& r) const;
+
+               // Check if attribute is empty
+               bool empty() const;
+
+               // Get attribute name/value, or "" if attribute is empty
+               const char_t* name() const;
+               const char_t* value() const;
+
+               // Get attribute value, or the default value if attribute is empty
+               const char_t* as_string(const char_t* def = PUGIXML_TEXT("")) const;
+
+               // Get attribute value as a number, or the default value if conversion did not succeed or attribute is empty
+               int as_int(int def = 0) const;
+               unsigned int as_uint(unsigned int def = 0) const;
+               double as_double(double def = 0) const;
+               float as_float(float def = 0) const;
+
+       #ifdef PUGIXML_HAS_LONG_LONG
+               long long as_llong(long long def = 0) const;
+               unsigned long long as_ullong(unsigned long long def = 0) const;
+       #endif
+
+               // Get attribute value as bool (returns true if first character is in '1tTyY' set), or the default value if attribute is empty
+               bool as_bool(bool def = false) const;
+
+               // Set attribute name/value (returns false if attribute is empty or there is not enough memory)
+               bool set_name(const char_t* rhs);
+               bool set_value(const char_t* rhs);
+
+               // Set attribute value with type conversion (numbers are converted to strings, boolean is converted to "true"/"false")
+               bool set_value(int rhs);
+               bool set_value(unsigned int rhs);
+               bool set_value(long rhs);
+               bool set_value(unsigned long rhs);
+               bool set_value(double rhs);
+               bool set_value(float rhs);
+               bool set_value(bool rhs);
+
+       #ifdef PUGIXML_HAS_LONG_LONG
+               bool set_value(long long rhs);
+               bool set_value(unsigned long long rhs);
+       #endif
+
+               // Set attribute value (equivalent to set_value without error checking)
+               xml_attribute& operator=(const char_t* rhs);
+               xml_attribute& operator=(int rhs);
+               xml_attribute& operator=(unsigned int rhs);
+               xml_attribute& operator=(long rhs);
+               xml_attribute& operator=(unsigned long rhs);
+               xml_attribute& operator=(double rhs);
+               xml_attribute& operator=(float rhs);
+               xml_attribute& operator=(bool rhs);
+
+       #ifdef PUGIXML_HAS_LONG_LONG
+               xml_attribute& operator=(long long rhs);
+               xml_attribute& operator=(unsigned long long rhs);
+       #endif
+
+               // Get next/previous attribute in the attribute list of the parent node
+               xml_attribute next_attribute() const;
+               xml_attribute previous_attribute() const;
+
+               // Get hash value (unique for handles to the same object)
+               size_t hash_value() const;
+
+               // Get internal pointer
+               xml_attribute_struct* internal_object() const;
+       };
+
+#ifdef __BORLANDC__
+       // Borland C++ workaround
+       bool PUGIXML_FUNCTION operator&&(const xml_attribute& lhs, bool rhs);
+       bool PUGIXML_FUNCTION operator||(const xml_attribute& lhs, bool rhs);
+#endif
+
+       // A light-weight handle for manipulating nodes in DOM tree
+       class PUGIXML_CLASS xml_node
+       {
+               friend class xml_attribute_iterator;
+               friend class xml_node_iterator;
+               friend class xml_named_node_iterator;
+
+       protected:
+               xml_node_struct* _root;
+
+               typedef void (*unspecified_bool_type)(xml_node***);
+
+       public:
+               // Default constructor. Constructs an empty node.
+               xml_node();
+
+               // Constructs node from internal pointer
+               explicit xml_node(xml_node_struct* p);
+
+               // Safe bool conversion operator
+               operator unspecified_bool_type() const;
+
+               // Borland C++ workaround
+               bool operator!() const;
+
+               // Comparison operators (compares wrapped node pointers)
+               bool operator==(const xml_node& r) const;
+               bool operator!=(const xml_node& r) const;
+               bool operator<(const xml_node& r) const;
+               bool operator>(const xml_node& r) const;
+               bool operator<=(const xml_node& r) const;
+               bool operator>=(const xml_node& r) const;
+
+               // Check if node is empty.
+               bool empty() const;
+
+               // Get node type
+               xml_node_type type() const;
+
+               // Get node name, or "" if node is empty or it has no name
+               const char_t* name() const;
+
+               // Get node value, or "" if node is empty or it has no value
+               // Note: For <node>text</node> node.value() does not return "text"! Use child_value() or text() methods to access text inside nodes.
+               const char_t* value() const;
+
+               // Get attribute list
+               xml_attribute first_attribute() const;
+               xml_attribute last_attribute() const;
+
+               // Get children list
+               xml_node first_child() const;
+               xml_node last_child() const;
+
+               // Get next/previous sibling in the children list of the parent node
+               xml_node next_sibling() const;
+               xml_node previous_sibling() const;
+
+               // Get parent node
+               xml_node parent() const;
+
+               // Get root of DOM tree this node belongs to
+               xml_node root() const;
+
+               // Get text object for the current node
+               xml_text text() const;
+
+               // Get child, attribute or next/previous sibling with the specified name
+               xml_node child(const char_t* name) const;
+               xml_attribute attribute(const char_t* name) const;
+               xml_node next_sibling(const char_t* name) const;
+               xml_node previous_sibling(const char_t* name) const;
+
+               // Get attribute, starting the search from a hint (and updating hint so that searching for a sequence of attributes is fast)
+               xml_attribute attribute(const char_t* name, xml_attribute& hint) const;
+
+               // Get child value of current node; that is, value of the first child node of type PCDATA/CDATA
+               const char_t* child_value() const;
+
+               // Get child value of child with specified name. Equivalent to child(name).child_value().
+               const char_t* child_value(const char_t* name) const;
+
+               // Set node name/value (returns false if node is empty, there is not enough memory, or node can not have name/value)
+               bool set_name(const char_t* rhs);
+               bool set_value(const char_t* rhs);
+
+               // Add attribute with specified name. Returns added attribute, or empty attribute on errors.
+               xml_attribute append_attribute(const char_t* name);
+               xml_attribute prepend_attribute(const char_t* name);
+               xml_attribute insert_attribute_after(const char_t* name, const xml_attribute& attr);
+               xml_attribute insert_attribute_before(const char_t* name, const xml_attribute& attr);
+
+               // Add a copy of the specified attribute. Returns added attribute, or empty attribute on errors.
+               xml_attribute append_copy(const xml_attribute& proto);
+               xml_attribute prepend_copy(const xml_attribute& proto);
+               xml_attribute insert_copy_after(const xml_attribute& proto, const xml_attribute& attr);
+               xml_attribute insert_copy_before(const xml_attribute& proto, const xml_attribute& attr);
+
+               // Add child node with specified type. Returns added node, or empty node on errors.
+               xml_node append_child(xml_node_type type = node_element);
+               xml_node prepend_child(xml_node_type type = node_element);
+               xml_node insert_child_after(xml_node_type type, const xml_node& node);
+               xml_node insert_child_before(xml_node_type type, const xml_node& node);
+
+               // Add child element with specified name. Returns added node, or empty node on errors.
+               xml_node append_child(const char_t* name);
+               xml_node prepend_child(const char_t* name);
+               xml_node insert_child_after(const char_t* name, const xml_node& node);
+               xml_node insert_child_before(const char_t* name, const xml_node& node);
+
+               // Add a copy of the specified node as a child. Returns added node, or empty node on errors.
+               xml_node append_copy(const xml_node& proto);
+               xml_node prepend_copy(const xml_node& proto);
+               xml_node insert_copy_after(const xml_node& proto, const xml_node& node);
+               xml_node insert_copy_before(const xml_node& proto, const xml_node& node);
+
+               // Move the specified node to become a child of this node. Returns moved node, or empty node on errors.
+               xml_node append_move(const xml_node& moved);
+               xml_node prepend_move(const xml_node& moved);
+               xml_node insert_move_after(const xml_node& moved, const xml_node& node);
+               xml_node insert_move_before(const xml_node& moved, const xml_node& node);
+
+               // Remove specified attribute
+               bool remove_attribute(const xml_attribute& a);
+               bool remove_attribute(const char_t* name);
+
+               // Remove specified child
+               bool remove_child(const xml_node& n);
+               bool remove_child(const char_t* name);
+
+               // Parses buffer as an XML document fragment and appends all nodes as children of the current node.
+               // Copies/converts the buffer, so it may be deleted or changed after the function returns.
+               // Note: append_buffer allocates memory that has the lifetime of the owning document; removing the appended nodes does not immediately reclaim that memory.
+               xml_parse_result append_buffer(const void* contents, size_t size, unsigned int options = parse_default, xml_encoding encoding = encoding_auto);
+
+               // Find attribute using predicate. Returns first attribute for which predicate returned true.
+               template <typename Predicate> xml_attribute find_attribute(Predicate pred) const
+               {
+                       if (!_root) return xml_attribute();
+
+                       for (xml_attribute attrib = first_attribute(); attrib; attrib = attrib.next_attribute())
+                               if (pred(attrib))
+                                       return attrib;
+
+                       return xml_attribute();
+               }
+
+               // Find child node using predicate. Returns first child for which predicate returned true.
+               template <typename Predicate> xml_node find_child(Predicate pred) const
+               {
+                       if (!_root) return xml_node();
+
+                       for (xml_node node = first_child(); node; node = node.next_sibling())
+                               if (pred(node))
+                                       return node;
+
+                       return xml_node();
+               }
+
+               // Find node from subtree using predicate. Returns first node from subtree (depth-first), for which predicate returned true.
+               template <typename Predicate> xml_node find_node(Predicate pred) const
+               {
+                       if (!_root) return xml_node();
+
+                       xml_node cur = first_child();
+
+                       while (cur._root && cur._root != _root)
+                       {
+                               if (pred(cur)) return cur;
+
+                               if (cur.first_child()) cur = cur.first_child();
+                               else if (cur.next_sibling()) cur = cur.next_sibling();
+                               else
+                               {
+                                       while (!cur.next_sibling() && cur._root != _root) cur = cur.parent();
+
+                                       if (cur._root != _root) cur = cur.next_sibling();
+                               }
+                       }
+
+                       return xml_node();
+               }
+
+               // Find child node by attribute name/value
+               xml_node find_child_by_attribute(const char_t* name, const char_t* attr_name, const char_t* attr_value) const;
+               xml_node find_child_by_attribute(const char_t* attr_name, const char_t* attr_value) const;
+
+       #ifndef PUGIXML_NO_STL
+               // Get the absolute node path from root as a text string.
+               string_t path(char_t delimiter = '/') const;
+       #endif
+
+               // Search for a node by path consisting of node names and . or .. elements.
+               xml_node first_element_by_path(const char_t* path, char_t delimiter = '/') const;
+
+               // Recursively traverse subtree with xml_tree_walker
+               bool traverse(xml_tree_walker& walker);
+
+       #ifndef PUGIXML_NO_XPATH
+               // Select single node by evaluating XPath query. Returns first node from the resulting node set.
+               xpath_node select_node(const char_t* query, xpath_variable_set* variables = 0) const;
+               xpath_node select_node(const xpath_query& query) const;
+
+               // Select node set by evaluating XPath query
+               xpath_node_set select_nodes(const char_t* query, xpath_variable_set* variables = 0) const;
+               xpath_node_set select_nodes(const xpath_query& query) const;
+
+               // (deprecated: use select_node instead) Select single node by evaluating XPath query.
+               PUGIXML_DEPRECATED xpath_node select_single_node(const char_t* query, xpath_variable_set* variables = 0) const;
+               PUGIXML_DEPRECATED xpath_node select_single_node(const xpath_query& query) const;
+
+       #endif
+
+               // Print subtree using a writer object
+               void print(xml_writer& writer, const char_t* indent = PUGIXML_TEXT("\t"), unsigned int flags = format_default, xml_encoding encoding = encoding_auto, unsigned int depth = 0) const;
+
+       #ifndef PUGIXML_NO_STL
+               // Print subtree to stream
+               void print(std::basic_ostream<char, std::char_traits<char> >& os, const char_t* indent = PUGIXML_TEXT("\t"), unsigned int flags = format_default, xml_encoding encoding = encoding_auto, unsigned int depth = 0) const;
+               void print(std::basic_ostream<wchar_t, std::char_traits<wchar_t> >& os, const char_t* indent = PUGIXML_TEXT("\t"), unsigned int flags = format_default, unsigned int depth = 0) const;
+       #endif
+
+               // Child nodes iterators
+               typedef xml_node_iterator iterator;
+
+               iterator begin() const;
+               iterator end() const;
+
+               // Attribute iterators
+               typedef xml_attribute_iterator attribute_iterator;
+
+               attribute_iterator attributes_begin() const;
+               attribute_iterator attributes_end() const;
+
+               // Range-based for support
+               xml_object_range<xml_node_iterator> children() const;
+               xml_object_range<xml_named_node_iterator> children(const char_t* name) const;
+               xml_object_range<xml_attribute_iterator> attributes() const;
+
+               // Get node offset in parsed file/string (in char_t units) for debugging purposes
+               ptrdiff_t offset_debug() const;
+
+               // Get hash value (unique for handles to the same object)
+               size_t hash_value() const;
+
+               // Get internal pointer
+               xml_node_struct* internal_object() const;
+       };
+
+#ifdef __BORLANDC__
+       // Borland C++ workaround
+       bool PUGIXML_FUNCTION operator&&(const xml_node& lhs, bool rhs);
+       bool PUGIXML_FUNCTION operator||(const xml_node& lhs, bool rhs);
+#endif
+
+       // A helper for working with text inside PCDATA nodes
+       class PUGIXML_CLASS xml_text
+       {
+               friend class xml_node;
+
+               xml_node_struct* _root;
+
+               typedef void (*unspecified_bool_type)(xml_text***);
+
+               explicit xml_text(xml_node_struct* root);
+
+               xml_node_struct* _data_new();
+               xml_node_struct* _data() const;
+
+       public:
+               // Default constructor. Constructs an empty object.
+               xml_text();
+
+               // Safe bool conversion operator
+               operator unspecified_bool_type() const;
+
+               // Borland C++ workaround
+               bool operator!() const;
+
+               // Check if text object is empty
+               bool empty() const;
+
+               // Get text, or "" if object is empty
+               const char_t* get() const;
+
+               // Get text, or the default value if object is empty
+               const char_t* as_string(const char_t* def = PUGIXML_TEXT("")) const;
+
+               // Get text as a number, or the default value if conversion did not succeed or object is empty
+               int as_int(int def = 0) const;
+               unsigned int as_uint(unsigned int def = 0) const;
+               double as_double(double def = 0) const;
+               float as_float(float def = 0) const;
+
+       #ifdef PUGIXML_HAS_LONG_LONG
+               long long as_llong(long long def = 0) const;
+               unsigned long long as_ullong(unsigned long long def = 0) const;
+       #endif
+
+               // Get text as bool (returns true if first character is in '1tTyY' set), or the default value if object is empty
+               bool as_bool(bool def = false) const;
+
+               // Set text (returns false if object is empty or there is not enough memory)
+               bool set(const char_t* rhs);
+
+               // Set text with type conversion (numbers are converted to strings, boolean is converted to "true"/"false")
+               bool set(int rhs);
+               bool set(unsigned int rhs);
+               bool set(long rhs);
+               bool set(unsigned long rhs);
+               bool set(double rhs);
+               bool set(float rhs);
+               bool set(bool rhs);
+
+       #ifdef PUGIXML_HAS_LONG_LONG
+               bool set(long long rhs);
+               bool set(unsigned long long rhs);
+       #endif
+
+               // Set text (equivalent to set without error checking)
+               xml_text& operator=(const char_t* rhs);
+               xml_text& operator=(int rhs);
+               xml_text& operator=(unsigned int rhs);
+               xml_text& operator=(long rhs);
+               xml_text& operator=(unsigned long rhs);
+               xml_text& operator=(double rhs);
+               xml_text& operator=(float rhs);
+               xml_text& operator=(bool rhs);
+
+       #ifdef PUGIXML_HAS_LONG_LONG
+               xml_text& operator=(long long rhs);
+               xml_text& operator=(unsigned long long rhs);
+       #endif
+
+               // Get the data node (node_pcdata or node_cdata) for this object
+               xml_node data() const;
+       };
+
+#ifdef __BORLANDC__
+       // Borland C++ workaround
+       bool PUGIXML_FUNCTION operator&&(const xml_text& lhs, bool rhs);
+       bool PUGIXML_FUNCTION operator||(const xml_text& lhs, bool rhs);
+#endif
+
+       // Child node iterator (a bidirectional iterator over a collection of xml_node)
+       class PUGIXML_CLASS xml_node_iterator
+       {
+               friend class xml_node;
+
+       private:
+               mutable xml_node _wrap;
+               xml_node _parent;
+
+               xml_node_iterator(xml_node_struct* ref, xml_node_struct* parent);
+
+       public:
+               // Iterator traits
+               typedef ptrdiff_t difference_type;
+               typedef xml_node value_type;
+               typedef xml_node* pointer;
+               typedef xml_node& reference;
+
+       #ifndef PUGIXML_NO_STL
+               typedef std::bidirectional_iterator_tag iterator_category;
+       #endif
+
+               // Default constructor
+               xml_node_iterator();
+
+               // Construct an iterator which points to the specified node
+               xml_node_iterator(const xml_node& node);
+
+               // Iterator operators
+               bool operator==(const xml_node_iterator& rhs) const;
+               bool operator!=(const xml_node_iterator& rhs) const;
+
+               xml_node& operator*() const;
+               xml_node* operator->() const;
+
+               const xml_node_iterator& operator++();
+               xml_node_iterator operator++(int);
+
+               const xml_node_iterator& operator--();
+               xml_node_iterator operator--(int);
+       };
+
+       // Attribute iterator (a bidirectional iterator over a collection of xml_attribute)
+       class PUGIXML_CLASS xml_attribute_iterator
+       {
+               friend class xml_node;
+
+       private:
+               mutable xml_attribute _wrap;
+               xml_node _parent;
+
+               xml_attribute_iterator(xml_attribute_struct* ref, xml_node_struct* parent);
+
+       public:
+               // Iterator traits
+               typedef ptrdiff_t difference_type;
+               typedef xml_attribute value_type;
+               typedef xml_attribute* pointer;
+               typedef xml_attribute& reference;
+
+       #ifndef PUGIXML_NO_STL
+               typedef std::bidirectional_iterator_tag iterator_category;
+       #endif
+
+               // Default constructor
+               xml_attribute_iterator();
+
+               // Construct an iterator which points to the specified attribute
+               xml_attribute_iterator(const xml_attribute& attr, const xml_node& parent);
+
+               // Iterator operators
+               bool operator==(const xml_attribute_iterator& rhs) const;
+               bool operator!=(const xml_attribute_iterator& rhs) const;
+
+               xml_attribute& operator*() const;
+               xml_attribute* operator->() const;
+
+               const xml_attribute_iterator& operator++();
+               xml_attribute_iterator operator++(int);
+
+               const xml_attribute_iterator& operator--();
+               xml_attribute_iterator operator--(int);
+       };
+
+       // Named node range helper
+       class PUGIXML_CLASS xml_named_node_iterator
+       {
+               friend class xml_node;
+
+       public:
+               // Iterator traits
+               typedef ptrdiff_t difference_type;
+               typedef xml_node value_type;
+               typedef xml_node* pointer;
+               typedef xml_node& reference;
+
+       #ifndef PUGIXML_NO_STL
+               typedef std::bidirectional_iterator_tag iterator_category;
+       #endif
+
+               // Default constructor
+               xml_named_node_iterator();
+
+               // Construct an iterator which points to the specified node
+               xml_named_node_iterator(const xml_node& node, const char_t* name);
+
+               // Iterator operators
+               bool operator==(const xml_named_node_iterator& rhs) const;
+               bool operator!=(const xml_named_node_iterator& rhs) const;
+
+               xml_node& operator*() const;
+               xml_node* operator->() const;
+
+               const xml_named_node_iterator& operator++();
+               xml_named_node_iterator operator++(int);
+
+               const xml_named_node_iterator& operator--();
+               xml_named_node_iterator operator--(int);
+
+       private:
+               mutable xml_node _wrap;
+               xml_node _parent;
+               const char_t* _name;
+
+               xml_named_node_iterator(xml_node_struct* ref, xml_node_struct* parent, const char_t* name);
+       };
+
+       // Abstract tree walker class (see xml_node::traverse)
+       class PUGIXML_CLASS xml_tree_walker
+       {
+               friend class xml_node;
+
+       private:
+               int _depth;
+
+       protected:
+               // Get current traversal depth
+               int depth() const;
+
+       public:
+               xml_tree_walker();
+               virtual ~xml_tree_walker();
+
+               // Callback that is called when traversal begins
+               virtual bool begin(xml_node& node);
+
+               // Callback that is called for each node traversed
+               virtual bool for_each(xml_node& node) = 0;
+
+               // Callback that is called when traversal ends
+               virtual bool end(xml_node& node);
+       };
+
+       // Parsing status, returned as part of xml_parse_result object
+       enum xml_parse_status
+       {
+               status_ok = 0,                          // No error
+
+               status_file_not_found,          // File was not found during load_file()
+               status_io_error,                        // Error reading from file/stream
+               status_out_of_memory,           // Could not allocate memory
+               status_internal_error,          // Internal error occurred
+
+               status_unrecognized_tag,        // Parser could not determine tag type
+
+               status_bad_pi,                          // Parsing error occurred while parsing document declaration/processing instruction
+               status_bad_comment,                     // Parsing error occurred while parsing comment
+               status_bad_cdata,                       // Parsing error occurred while parsing CDATA section
+               status_bad_doctype,                     // Parsing error occurred while parsing document type declaration
+               status_bad_pcdata,                      // Parsing error occurred while parsing PCDATA section
+               status_bad_start_element,       // Parsing error occurred while parsing start element tag
+               status_bad_attribute,           // Parsing error occurred while parsing element attribute
+               status_bad_end_element,         // Parsing error occurred while parsing end element tag
+               status_end_element_mismatch,// There was a mismatch of start-end tags (closing tag had incorrect name, some tag was not closed or there was an excessive closing tag)
+
+               status_append_invalid_root,     // Unable to append nodes since root type is not node_element or node_document (exclusive to xml_node::append_buffer)
+
+               status_no_document_element      // Parsing resulted in a document without element nodes
+       };
+
+       // Parsing result
+       struct PUGIXML_CLASS xml_parse_result
+       {
+               // Parsing status (see xml_parse_status)
+               xml_parse_status status;
+
+               // Last parsed offset (in char_t units from start of input data)
+               ptrdiff_t offset;
+
+               // Source document encoding
+               xml_encoding encoding;
+
+               // Default constructor, initializes object to failed state
+               xml_parse_result();
+
+               // Cast to bool operator
+               operator bool() const;
+
+               // Get error description
+               const char* description() const;
+       };
+
+       // Document class (DOM tree root)
+       class PUGIXML_CLASS xml_document: public xml_node
+       {
+       private:
+               char_t* _buffer;
+
+               char _memory[192];
+
+               // Non-copyable semantics
+               xml_document(const xml_document&);
+               xml_document& operator=(const xml_document&);
+
+               void _create();
+               void _destroy();
+               void _move(xml_document& rhs) PUGIXML_NOEXCEPT_IF_NOT_COMPACT;
+
+       public:
+               // Default constructor, makes empty document
+               xml_document();
+
+               // Destructor, invalidates all node/attribute handles to this document
+               ~xml_document();
+
+       #ifdef PUGIXML_HAS_MOVE
+               // Move semantics support
+               xml_document(xml_document&& rhs) PUGIXML_NOEXCEPT_IF_NOT_COMPACT;
+               xml_document& operator=(xml_document&& rhs) PUGIXML_NOEXCEPT_IF_NOT_COMPACT;
+       #endif
+
+               // Removes all nodes, leaving the empty document
+               void reset();
+
+               // Removes all nodes, then copies the entire contents of the specified document
+               void reset(const xml_document& proto);
+
+       #ifndef PUGIXML_NO_STL
+               // Load document from stream.
+               xml_parse_result load(std::basic_istream<char, std::char_traits<char> >& stream, unsigned int options = parse_default, xml_encoding encoding = encoding_auto);
+               xml_parse_result load(std::basic_istream<wchar_t, std::char_traits<wchar_t> >& stream, unsigned int options = parse_default);
+       #endif
+
+               // (deprecated: use load_string instead) Load document from zero-terminated string. No encoding conversions are applied.
+               PUGIXML_DEPRECATED xml_parse_result load(const char_t* contents, unsigned int options = parse_default);
+
+               // Load document from zero-terminated string. No encoding conversions are applied.
+               xml_parse_result load_string(const char_t* contents, unsigned int options = parse_default);
+
+               // Load document from file
+               xml_parse_result load_file(const char* path, unsigned int options = parse_default, xml_encoding encoding = encoding_auto);
+               xml_parse_result load_file(const wchar_t* path, unsigned int options = parse_default, xml_encoding encoding = encoding_auto);
+
+               // Load document from buffer. Copies/converts the buffer, so it may be deleted or changed after the function returns.
+               xml_parse_result load_buffer(const void* contents, size_t size, unsigned int options = parse_default, xml_encoding encoding = encoding_auto);
+
+               // Load document from buffer, using the buffer for in-place parsing (the buffer is modified and used for storage of document data).
+               // You should ensure that buffer data will persist throughout the document's lifetime, and free the buffer memory manually once document is destroyed.
+               xml_parse_result load_buffer_inplace(void* contents, size_t size, unsigned int options = parse_default, xml_encoding encoding = encoding_auto);
+
+               // Load document from buffer, using the buffer for in-place parsing (the buffer is modified and used for storage of document data).
+               // You should allocate the buffer with pugixml allocation function; document will free the buffer when it is no longer needed (you can't use it anymore).
+               xml_parse_result load_buffer_inplace_own(void* contents, size_t size, unsigned int options = parse_default, xml_encoding encoding = encoding_auto);
+
+               // Save XML document to writer (semantics is slightly different from xml_node::print, see documentation for details).
+               void save(xml_writer& writer, const char_t* indent = PUGIXML_TEXT("\t"), unsigned int flags = format_default, xml_encoding encoding = encoding_auto) const;
+
+       #ifndef PUGIXML_NO_STL
+               // Save XML document to stream (semantics is slightly different from xml_node::print, see documentation for details).
+               void save(std::basic_ostream<char, std::char_traits<char> >& stream, const char_t* indent = PUGIXML_TEXT("\t"), unsigned int flags = format_default, xml_encoding encoding = encoding_auto) const;
+               void save(std::basic_ostream<wchar_t, std::char_traits<wchar_t> >& stream, const char_t* indent = PUGIXML_TEXT("\t"), unsigned int flags = format_default) const;
+       #endif
+
+               // Save XML to file
+               bool save_file(const char* path, const char_t* indent = PUGIXML_TEXT("\t"), unsigned int flags = format_default, xml_encoding encoding = encoding_auto) const;
+               bool save_file(const wchar_t* path, const char_t* indent = PUGIXML_TEXT("\t"), unsigned int flags = format_default, xml_encoding encoding = encoding_auto) const;
+
+               // Get document element
+               xml_node document_element() const;
+       };
+
+#ifndef PUGIXML_NO_XPATH
+       // XPath query return type
+       enum xpath_value_type
+       {
+               xpath_type_none,          // Unknown type (query failed to compile)
+               xpath_type_node_set,  // Node set (xpath_node_set)
+               xpath_type_number,        // Number
+               xpath_type_string,        // String
+               xpath_type_boolean        // Boolean
+       };
+
+       // XPath parsing result
+       struct PUGIXML_CLASS xpath_parse_result
+       {
+               // Error message (0 if no error)
+               const char* error;
+
+               // Last parsed offset (in char_t units from string start)
+               ptrdiff_t offset;
+
+               // Default constructor, initializes object to failed state
+               xpath_parse_result();
+
+               // Cast to bool operator
+               operator bool() const;
+
+               // Get error description
+               const char* description() const;
+       };
+
+       // A single XPath variable
+       class PUGIXML_CLASS xpath_variable
+       {
+               friend class xpath_variable_set;
+
+       protected:
+               xpath_value_type _type;
+               xpath_variable* _next;
+
+               xpath_variable(xpath_value_type type);
+
+               // Non-copyable semantics
+               xpath_variable(const xpath_variable&);
+               xpath_variable& operator=(const xpath_variable&);
+
+       public:
+               // Get variable name
+               const char_t* name() const;
+
+               // Get variable type
+               xpath_value_type type() const;
+
+               // Get variable value; no type conversion is performed, default value (false, NaN, empty string, empty node set) is returned on type mismatch error
+               bool get_boolean() const;
+               double get_number() const;
+               const char_t* get_string() const;
+               const xpath_node_set& get_node_set() const;
+
+               // Set variable value; no type conversion is performed, false is returned on type mismatch error
+               bool set(bool value);
+               bool set(double value);
+               bool set(const char_t* value);
+               bool set(const xpath_node_set& value);
+       };
+
+       // A set of XPath variables
+       class PUGIXML_CLASS xpath_variable_set
+       {
+       private:
+               xpath_variable* _data[64];
+
+               void _assign(const xpath_variable_set& rhs);
+               void _swap(xpath_variable_set& rhs);
+
+               xpath_variable* _find(const char_t* name) const;
+
+               static bool _clone(xpath_variable* var, xpath_variable** out_result);
+               static void _destroy(xpath_variable* var);
+
+       public:
+               // Default constructor/destructor
+               xpath_variable_set();
+               ~xpath_variable_set();
+
+               // Copy constructor/assignment operator
+               xpath_variable_set(const xpath_variable_set& rhs);
+               xpath_variable_set& operator=(const xpath_variable_set& rhs);
+
+       #ifdef PUGIXML_HAS_MOVE
+               // Move semantics support
+               xpath_variable_set(xpath_variable_set&& rhs) PUGIXML_NOEXCEPT;
+               xpath_variable_set& operator=(xpath_variable_set&& rhs) PUGIXML_NOEXCEPT;
+       #endif
+
+               // Add a new variable or get the existing one, if the types match
+               xpath_variable* add(const char_t* name, xpath_value_type type);
+
+               // Set value of an existing variable; no type conversion is performed, false is returned if there is no such variable or if types mismatch
+               bool set(const char_t* name, bool value);
+               bool set(const char_t* name, double value);
+               bool set(const char_t* name, const char_t* value);
+               bool set(const char_t* name, const xpath_node_set& value);
+
+               // Get existing variable by name
+               xpath_variable* get(const char_t* name);
+               const xpath_variable* get(const char_t* name) const;
+       };
+
+       // A compiled XPath query object
+       class PUGIXML_CLASS xpath_query
+       {
+       private:
+               void* _impl;
+               xpath_parse_result _result;
+
+               typedef void (*unspecified_bool_type)(xpath_query***);
+
+               // Non-copyable semantics
+               xpath_query(const xpath_query&);
+               xpath_query& operator=(const xpath_query&);
+
+       public:
+               // Construct a compiled object from XPath expression.
+               // If PUGIXML_NO_EXCEPTIONS is not defined, throws xpath_exception on compilation errors.
+               explicit xpath_query(const char_t* query, xpath_variable_set* variables = 0);
+
+               // Constructor
+               xpath_query();
+
+               // Destructor
+               ~xpath_query();
+
+       #ifdef PUGIXML_HAS_MOVE
+               // Move semantics support
+               xpath_query(xpath_query&& rhs) PUGIXML_NOEXCEPT;
+               xpath_query& operator=(xpath_query&& rhs) PUGIXML_NOEXCEPT;
+       #endif
+
+               // Get query expression return type
+               xpath_value_type return_type() const;
+
+               // Evaluate expression as boolean value in the specified context; performs type conversion if necessary.
+               // If PUGIXML_NO_EXCEPTIONS is not defined, throws std::bad_alloc on out of memory errors.
+               bool evaluate_boolean(const xpath_node& n) const;
+
+               // Evaluate expression as double value in the specified context; performs type conversion if necessary.
+               // If PUGIXML_NO_EXCEPTIONS is not defined, throws std::bad_alloc on out of memory errors.
+               double evaluate_number(const xpath_node& n) const;
+
+       #ifndef PUGIXML_NO_STL
+               // Evaluate expression as string value in the specified context; performs type conversion if necessary.
+               // If PUGIXML_NO_EXCEPTIONS is not defined, throws std::bad_alloc on out of memory errors.
+               string_t evaluate_string(const xpath_node& n) const;
+       #endif
+
+               // Evaluate expression as string value in the specified context; performs type conversion if necessary.
+               // At most capacity characters are written to the destination buffer, full result size is returned (includes terminating zero).
+               // If PUGIXML_NO_EXCEPTIONS is not defined, throws std::bad_alloc on out of memory errors.
+               // If PUGIXML_NO_EXCEPTIONS is defined, returns empty  set instead.
+               size_t evaluate_string(char_t* buffer, size_t capacity, const xpath_node& n) const;
+
+               // Evaluate expression as node set in the specified context.
+               // If PUGIXML_NO_EXCEPTIONS is not defined, throws xpath_exception on type mismatch and std::bad_alloc on out of memory errors.
+               // If PUGIXML_NO_EXCEPTIONS is defined, returns empty node set instead.
+               xpath_node_set evaluate_node_set(const xpath_node& n) const;
+
+               // Evaluate expression as node set in the specified context.
+               // Return first node in document order, or empty node if node set is empty.
+               // If PUGIXML_NO_EXCEPTIONS is not defined, throws xpath_exception on type mismatch and std::bad_alloc on out of memory errors.
+               // If PUGIXML_NO_EXCEPTIONS is defined, returns empty node instead.
+               xpath_node evaluate_node(const xpath_node& n) const;
+
+               // Get parsing result (used to get compilation errors in PUGIXML_NO_EXCEPTIONS mode)
+               const xpath_parse_result& result() const;
+
+               // Safe bool conversion operator
+               operator unspecified_bool_type() const;
+
+               // Borland C++ workaround
+               bool operator!() const;
+       };
+
+       #ifndef PUGIXML_NO_EXCEPTIONS
+       // XPath exception class
+       class PUGIXML_CLASS xpath_exception: public std::exception
+       {
+       private:
+               xpath_parse_result _result;
+
+       public:
+               // Construct exception from parse result
+               explicit xpath_exception(const xpath_parse_result& result);
+
+               // Get error message
+               virtual const char* what() const throw() PUGIXML_OVERRIDE;
+
+               // Get parse result
+               const xpath_parse_result& result() const;
+       };
+       #endif
+
+       // XPath node class (either xml_node or xml_attribute)
+       class PUGIXML_CLASS xpath_node
+       {
+       private:
+               xml_node _node;
+               xml_attribute _attribute;
+
+               typedef void (*unspecified_bool_type)(xpath_node***);
+
+       public:
+               // Default constructor; constructs empty XPath node
+               xpath_node();
+
+               // Construct XPath node from XML node/attribute
+               xpath_node(const xml_node& node);
+               xpath_node(const xml_attribute& attribute, const xml_node& parent);
+
+               // Get node/attribute, if any
+               xml_node node() const;
+               xml_attribute attribute() const;
+
+               // Get parent of contained node/attribute
+               xml_node parent() const;
+
+               // Safe bool conversion operator
+               operator unspecified_bool_type() const;
+
+               // Borland C++ workaround
+               bool operator!() const;
+
+               // Comparison operators
+               bool operator==(const xpath_node& n) const;
+               bool operator!=(const xpath_node& n) const;
+       };
+
+#ifdef __BORLANDC__
+       // Borland C++ workaround
+       bool PUGIXML_FUNCTION operator&&(const xpath_node& lhs, bool rhs);
+       bool PUGIXML_FUNCTION operator||(const xpath_node& lhs, bool rhs);
+#endif
+
+       // A fixed-size collection of XPath nodes
+       class PUGIXML_CLASS xpath_node_set
+       {
+       public:
+               // Collection type
+               enum type_t
+               {
+                       type_unsorted,                  // Not ordered
+                       type_sorted,                    // Sorted by document order (ascending)
+                       type_sorted_reverse             // Sorted by document order (descending)
+               };
+
+               // Constant iterator type
+               typedef const xpath_node* const_iterator;
+
+               // We define non-constant iterator to be the same as constant iterator so that various generic algorithms (i.e. boost foreach) work
+               typedef const xpath_node* iterator;
+
+               // Default constructor. Constructs empty set.
+               xpath_node_set();
+
+               // Constructs a set from iterator range; data is not checked for duplicates and is not sorted according to provided type, so be careful
+               xpath_node_set(const_iterator begin, const_iterator end, type_t type = type_unsorted);
+
+               // Destructor
+               ~xpath_node_set();
+
+               // Copy constructor/assignment operator
+               xpath_node_set(const xpath_node_set& ns);
+               xpath_node_set& operator=(const xpath_node_set& ns);
+
+       #ifdef PUGIXML_HAS_MOVE
+               // Move semantics support
+               xpath_node_set(xpath_node_set&& rhs) PUGIXML_NOEXCEPT;
+               xpath_node_set& operator=(xpath_node_set&& rhs) PUGIXML_NOEXCEPT;
+       #endif
+
+               // Get collection type
+               type_t type() const;
+
+               // Get collection size
+               size_t size() const;
+
+               // Indexing operator
+               const xpath_node& operator[](size_t index) const;
+
+               // Collection iterators
+               const_iterator begin() const;
+               const_iterator end() const;
+
+               // Sort the collection in ascending/descending order by document order
+               void sort(bool reverse = false);
+
+               // Get first node in the collection by document order
+               xpath_node first() const;
+
+               // Check if collection is empty
+               bool empty() const;
+
+       private:
+               type_t _type;
+
+               xpath_node _storage;
+
+               xpath_node* _begin;
+               xpath_node* _end;
+
+               void _assign(const_iterator begin, const_iterator end, type_t type);
+               void _move(xpath_node_set& rhs) PUGIXML_NOEXCEPT;
+       };
+#endif
+
+#ifndef PUGIXML_NO_STL
+       // Convert wide string to UTF8
+       std::basic_string<char, std::char_traits<char>, std::allocator<char> > PUGIXML_FUNCTION as_utf8(const wchar_t* str);
+       std::basic_string<char, std::char_traits<char>, std::allocator<char> > PUGIXML_FUNCTION as_utf8(const std::basic_string<wchar_t, std::char_traits<wchar_t>, std::allocator<wchar_t> >& str);
+
+       // Convert UTF8 to wide string
+       std::basic_string<wchar_t, std::char_traits<wchar_t>, std::allocator<wchar_t> > PUGIXML_FUNCTION as_wide(const char* str);
+       std::basic_string<wchar_t, std::char_traits<wchar_t>, std::allocator<wchar_t> > PUGIXML_FUNCTION as_wide(const std::basic_string<char, std::char_traits<char>, std::allocator<char> >& str);
+#endif
+
+       // Memory allocation function interface; returns pointer to allocated memory or NULL on failure
+       typedef void* (*allocation_function)(size_t size);
+
+       // Memory deallocation function interface
+       typedef void (*deallocation_function)(void* ptr);
+
+       // Override default memory management functions. All subsequent allocations/deallocations will be performed via supplied functions.
+       void PUGIXML_FUNCTION set_memory_management_functions(allocation_function allocate, deallocation_function deallocate);
+
+       // Get current memory management functions
+       allocation_function PUGIXML_FUNCTION get_memory_allocation_function();
+       deallocation_function PUGIXML_FUNCTION get_memory_deallocation_function();
+}
+
+#if !defined(PUGIXML_NO_STL) && (defined(_MSC_VER) || defined(__ICC))
+namespace std
+{
+       // Workarounds for (non-standard) iterator category detection for older versions (MSVC7/IC8 and earlier)
+       std::bidirectional_iterator_tag PUGIXML_FUNCTION _Iter_cat(const pugi::xml_node_iterator&);
+       std::bidirectional_iterator_tag PUGIXML_FUNCTION _Iter_cat(const pugi::xml_attribute_iterator&);
+       std::bidirectional_iterator_tag PUGIXML_FUNCTION _Iter_cat(const pugi::xml_named_node_iterator&);
+}
+#endif
+
+#if !defined(PUGIXML_NO_STL) && defined(__SUNPRO_CC)
+namespace std
+{
+       // Workarounds for (non-standard) iterator category detection
+       std::bidirectional_iterator_tag PUGIXML_FUNCTION __iterator_category(const pugi::xml_node_iterator&);
+       std::bidirectional_iterator_tag PUGIXML_FUNCTION __iterator_category(const pugi::xml_attribute_iterator&);
+       std::bidirectional_iterator_tag PUGIXML_FUNCTION __iterator_category(const pugi::xml_named_node_iterator&);
+}
+#endif
+
+#endif
+
+// Make sure implementation is included in header-only mode
+// Use macro expansion in #include to work around QMake (QTBUG-11923)
+#if defined(PUGIXML_HEADER_ONLY) && !defined(PUGIXML_SOURCE)
+#      define PUGIXML_SOURCE "pugixml.cpp"
+#      include PUGIXML_SOURCE
+#endif
+
+/**
+ * Copyright (c) 2006-2018 Arseny Kapoulkine
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
index 436dfc64449e004af7fa4a11f8c8a04f3bcd2994..fe6120ec7e47c80058f5de72391c75d19ed10e12 100644 (file)
@@ -9,6 +9,12 @@ endif(COMMAND cmake_policy)
 #=========================================================
 OPTION(CLITK_USE_PACS_CONNECTION "USE PACS CONNECTION"       OFF)
 #=========================================================
+
+# Look for OpenGL2 version
+if (VTK_RENDERING_BACKEND STREQUAL "OpenGL2")
+  add_definitions(-DVTK_OPENGL2)
+endif ()
+
 #List of vv tools to compile
 set(vv_TOOLS
   vvToolMedianFilter
@@ -16,7 +22,6 @@ set(vv_TOOLS
   vvToolCropImage
   vvToolBinarize
   vvToolImageArithm
-  vvToolResample
   vvToolMIP
   vvToolConvert ## with dummy vvToolConvert.ui
   vvToolROIManager
@@ -40,7 +45,6 @@ set(vv_TOOLS
 set(vvToolBinarize_LIBS clitkBinarizeImageLib)
 set(vvToolProfile_LIBS clitkProfileImageLib)
 set(vvToolHistogram_LIBS clitkHistogramImageLib)
-set(vvToolResample_LIBS clitkResampleImageLib)
 set(vvToolConvert_LIBS clitkImageConvertLib)
 set(vvToolExtractPatient_LIBS clitkSegmentationGgoLib)
 set(vvToolExtractLung_LIBS clitkSegmentationGgoLib)
@@ -363,7 +367,7 @@ target_link_libraries(vv vvLib)
 
 #=========================================================
 # Install options (also used by CPack)
-install(TARGETS vv DESTINATION bin PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_EXECUTE WORLD_EXECUTE)
+install(TARGETS vv DESTINATION bin PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE)
 
 #=========================================================
 
index f7e71323c2a7a44190af2fb9be219de154087a4d..110993b1ce7c4f1ed985d0b9fdabc695a689e6e7 100644 (file)
             <number>0</number>
            </property>
            <item>
-            <widget class="QLabel" name="mLabelSelected_3" >
-             <property name="sizePolicy" >
-              <sizepolicy vsizetype="Minimum" hsizetype="Preferred" >
-               <horstretch>0</horstretch>
-               <verstretch>0</verstretch>
-              </sizepolicy>
-             </property>
-             <property name="text" >
-              <string>&lt;b>Files in current DICOM serie&lt;/b></string>
-             </property>
-             <property name="textFormat" >
-              <enum>Qt::RichText</enum>
-             </property>
-            </widget>
+            <layout class="QHBoxLayout" name="horizontalLayout_3" >
+             <item>
+              <widget class="QLabel" name="mLabelSelected_3" >
+               <property name="sizePolicy" >
+                <sizepolicy vsizetype="Minimum" hsizetype="Preferred" >
+                 <horstretch>0</horstretch>
+                 <verstretch>0</verstretch>
+                </sizepolicy>
+               </property>
+               <property name="text" >
+                <string>&lt;b>Files in current DICOM serie&lt;/b></string>
+               </property>
+               <property name="textFormat" >
+                <enum>Qt::RichText</enum>
+               </property>
+              </widget>
+             </item>
+             <item>
+              <widget class="QCheckBox" name="mIsMatrixCheckBox" >
+               <property name="text" >
+                <string>Patient coordinate system</string>
+               </property>
+              </widget>
+             </item>
+            </layout>
            </item>
            <item>
             <widget class="QListWidget" name="mDicomDetailsListWidget" >
index 7ffecb5edbe6e4b3671005864ce82b435c31788d..b703e96b4d5da7436ff024654d292c3936924837 100644 (file)
@@ -144,6 +144,9 @@ void vtkVOXImageWriter::Write( )
                                     ext[2], ext[3],
                                     ext[4], ext[5]);
   this->GetInput()->UpdateData();
+#elif VTK_MAJOR_VERSION >= 8 || (VTK_MAJOR_VERSION == 7 && VTK_MINOR_VERSION >= 1)
+  this->UpdateExtent(ext);
+  this->Update();
 #else
   this->SetUpdateExtent(ext);
   this->Update();
index 0ab7cb806d5c7aa7100ea1e620b73169f3763c2a..d72f253ecfa4f4d397eef6207faead81fb82bd45 100644 (file)
@@ -47,6 +47,11 @@ void vvAnimatedGIFWriter::Write()
   int *wExtent = this->GetInput()->GetWholeExtent();
   this->GetInput()->SetUpdateExtent(wExtent);
   this->GetInput()->Update();
+#elif VTK_MAJOR_VERSION >= 8 || (VTK_MAJOR_VERSION == 7 && VTK_MINOR_VERSION >= 1)
+  this->UpdateInformation();
+  int *wExtent = this->GetInput()->GetInformation()->Get(vtkDataObject::DATA_EXTENT());
+  this->UpdateExtent(wExtent);
+  this->Update();
 #else
   this->UpdateInformation();
   int *wExtent = this->GetInput()->GetInformation()->Get(vtkDataObject::DATA_EXTENT());
index b5935c3692ea3a7c98afe69b14a7c5fba645bea5..1c18def90c2216c5b2d24c7ef3a0834e2f917513 100644 (file)
@@ -110,22 +110,48 @@ void vvBinaryImageOverlayActor::Initialize(bool IsVisible)
   // Create an actor for each time slice
   for (unsigned int numImage = 0; numImage < mSlicer->GetImage()->GetVTKImages().size(); numImage++) {
     // how many intensity ?
-    vtkSmartPointer<vtkImageMapToRGBA> mOverlayMapper = vtkSmartPointer<vtkImageMapToRGBA>::New();
-    if (mImage->IsTimeSequence())
-    {
- #if VTK_MAJOR_VERSION <= 5
-        mOverlayMapper->SetInput(mImage->GetVTKImages()[numImage]);
+    
+
+
+    if (!mFusionReslice) {
+      mFusionReslice = vtkSmartPointer<vtkImageReslice>::New();
+      mFusionReslice->SetInterpolationModeToLinear();
+      mFusionReslice->AutoCropOutputOn();
+      mFusionReslice->SetBackgroundColor(-1000,-1000,-1000,1);
+    }
+
+    mConcatenatedFusionTransform = vtkSmartPointer<vtkTransform>::New();
+    mConcatenatedFusionTransform->Identity();
+    if (!mImage->GetTransform().empty()){
+      mConcatenatedFusionTransform->Concatenate(mImage->GetTransform()[0]);
+    }
+    mConcatenatedFusionTransform->Concatenate(mSlicer->GetSlicingTransform());
+    mFusionReslice->SetResliceAxes(mConcatenatedFusionTransform->GetMatrix());
+    if (mImage->IsTimeSequence()) {
+#if VTK_MAJOR_VERSION <= 5
+    mFusionReslice->SetInput(0, mImage->GetVTKImages()[numImage]);
+    mFusionReslice->UpdateInformation();
 #else
-        mOverlayMapper->SetInputData(mImage->GetVTKImages()[numImage]);
+    mFusionReslice->SetInputData(0, mImage->GetVTKImages()[numImage]);
 #endif
-    }
-    else {
+    } else {
 #if VTK_MAJOR_VERSION <= 5
-        mOverlayMapper->SetInput(mImage->GetVTKImages()[0]);
+    mFusionReslice->SetInput(0, mImage->GetVTKImages()[0]);
+    mFusionReslice->UpdateInformation();
 #else
-        mOverlayMapper->SetInputData(mImage->GetVTKImages()[0]);
+    mFusionReslice->SetInputData(0, mImage->GetVTKImages()[0]);
 #endif
     }
+    mFusionReslice->Update();
+
+
+    
+    vtkSmartPointer<vtkImageMapToRGBA> mOverlayMapper = vtkSmartPointer<vtkImageMapToRGBA>::New();
+#if VTK_MAJOR_VERSION <= 5
+    mOverlayMapper->SetInput(mFusionReslice->GetOutput());
+#else
+    mOverlayMapper->SetInputConnection(mFusionReslice->GetOutputPort(0));
+#endif
 
     double range[2];
     if (mImage->IsTimeSequence())
index 9559bc78a0bd1e8952cfcd38df74198cc4440031..2e575c316caf14d51f444c22cb274fa9d974bff5 100644 (file)
@@ -27,6 +27,8 @@ class vtkActor;
 class vvImage;
 class vtkImageMapToRGBA;
 class vtkImageActor;
+class vtkImageReslice;
+class vtkTransform;
 
 //------------------------------------------------------------------------------
 class vvBinaryImageOverlayActor : public itk::LightObject
@@ -66,6 +68,8 @@ class vvBinaryImageOverlayActor : public itk::LightObject
 
   std::vector<vtkSmartPointer<vtkImageMapToRGBA> > mMapperList;
   std::vector<vtkSmartPointer<vtkImageActor> > mImageActorList;
+  vtkSmartPointer<vtkImageReslice> mFusionReslice;
+  vtkSmartPointer<vtkTransform> mConcatenatedFusionTransform;
 
   void ComputeExtent(int orientation, 
                     int slice, 
index 2093c7d7e0721c21330520c976261269f7b0651b..77ae059723db66360a7cfcdd65d22b096aa68d3d 100644 (file)
@@ -17,12 +17,15 @@ It is distributed under dual licence
 ===========================================================================**/
 
 #include "vvBlendImageActor.h"
-
+#ifdef VTK_OPENGL2
+  #include <vtk_glew.h>
+#else
+  #include <vtkgl.h>
+  #include <vtkOpenGLExtensionManager.h>
+#endif
 #include <vtkOpenGLRenderWindow.h>
-#include <vtkOpenGLExtensionManager.h>
 #include <vtkOpenGLRenderer.h>
 #include <vtkOpenGL.h>
-#include <vtkgl.h>
 #include <vtkObjectFactory.h>
 
 vtkStandardNewMacro(vvBlendImageActor);
@@ -40,6 +43,13 @@ void vvBlendImageActor::Render(vtkRenderer *ren)
 {
   //Change blending to maximum per component instead of weighted sum
   vtkOpenGLRenderWindow *renwin = dynamic_cast<vtkOpenGLRenderWindow*>(ren->GetRenderWindow());
+#ifdef VTK_OPENGL2
+  const char *extensions = renwin->ReportCapabilities();
+
+  //Call normal render
+  VTK_IMAGE_ACTOR::Render(ren);
+
+#else
   vtkOpenGLExtensionManager *extensions = renwin->GetExtensionManager();
   if (extensions->ExtensionSupported("GL_EXT_blend_minmax")) {
     extensions->LoadExtension("GL_EXT_blend_minmax");
@@ -53,6 +63,7 @@ void vvBlendImageActor::Render(vtkRenderer *ren)
   if (vtkgl::BlendEquationEXT!=0) {
     vtkgl::BlendEquationEXT( vtkgl::FUNC_ADD );
   }
+#endif
 }
 
 void vvBlendImageActor::PrintSelf(ostream& os, vtkIndent indent)
index 12a2959aa33679b8ab5f6bde9349e0d859eeba5c..980b3e7d1f86633811d1f8afe08ce93a5214f199 100644 (file)
@@ -54,8 +54,8 @@ vvDeformableRegistration::vvDeformableRegistration(vvImage::Pointer image,unsign
 void vvDeformableRegistration::abort()
 {
   aborted=true;
-  std::system("killall deformableregistration");
-  std::system("killall clitkVFMerge");
+  int tempReturn = std::system("killall deformableregistration");
+  tempReturn = std::system("killall clitkVFMerge");
 }
 
 unsigned int vvDeformableRegistration::getProgress()
@@ -71,16 +71,16 @@ void vvDeformableRegistration::cleanup(int frame_number) //remove temporary file
   for (int i=0; i<frame_number; i++) {
     std::stringstream filename;
     filename << temp_dir << "/deformation_" << i << ".vf";
-    std::system((std::string("rm ") + filename.str()).c_str());
+    int tempReturn = std::system((std::string("rm ") + filename.str()).c_str());
   }
   for (int i=0; i<frame_number; i++) {
     std::stringstream filename;
     filename << temp_dir << "/temp_" << i << ".vox";
-    std::system(("rm " + filename.str()).c_str());
+    int tempReturn = std::system(("rm " + filename.str()).c_str());
   }
   std::stringstream filename;
   filename << temp_dir;
-  std::system(("rm -r " + filename.str()).c_str());
+  int tempReturn = std::system(("rm -r " + filename.str()).c_str());
 }
 
 void vvDeformableRegistration::partial_run(int low_index,int high_index,int refimage,std::string ref_file)
@@ -111,7 +111,7 @@ void vvDeformableRegistration::partial_run(int low_index,int high_index,int refi
       registration_command << " --vf=" << old_vf.str();
     }
     DD(registration_command.str());
-    std::system(registration_command.str().c_str());
+    int tempReturn = std::system(registration_command.str().c_str());
     progress_mutex.lock();
     progress++;
     progress_mutex.unlock();
@@ -170,7 +170,7 @@ void vvDeformableRegistration::run()
   command << "clitkZeroVF -i " << temp_dir << "/deformation_" << computed_vf << ".vf -o "  << temp_dir <<
           "/deformation_" << refimage << ".vf";
   DD(command.str()); //create zero VF for the ref image
-  std::system(command.str().c_str());
+  int tempReturn = std::system(command.str().c_str());
   command.str("");
   command << "clitkVFMerge ";
   for (unsigned int i=0; i<images.size(); i++) command << temp_dir << "/deformation_" << i << ".vf ";
@@ -179,10 +179,10 @@ void vvDeformableRegistration::run()
   command << " --zorigin " << images[0]->GetOrigin()[2];
   command << " -o " << output_filename << std::endl;
   DD(command.str());
-  std::system(command.str().c_str());
+  tempReturn = std::system(command.str().c_str());
   cleanup(images.size());
   if (aborted) {
-    std::system(("rm " + output_filename).c_str());
+    tempReturn = std::system(("rm " + output_filename).c_str());
     return;
   }
   vvImageReader::Pointer reader = vvImageReader::New();
index 1856dd272602231ebce81c60c099174390e7c24b..ae1a6b1f3229321f2d176ae548e16f7f47090ff7 100644 (file)
@@ -175,6 +175,10 @@ int vvGlyph2D::RequestData(
 #if VTK_MAJOR_VERSION <= 5
     defaultSource->SetUpdateExtent(0, 1, 0);
     this->SetSource(defaultSource);
+#elif VTK_MAJOR_VERSION >= 8 || (VTK_MAJOR_VERSION == 7 && VTK_MINOR_VERSION >= 1)
+    int extentTemp[3] = {0, 1, 0};
+    this->UpdateExtent(extentTemp);
+    this->SetSourceData(defaultSource);
 #else
     this->SetUpdateExtent(0, 1, 0);
     this->SetSourceData(defaultSource);
index 8ddc4df78c71c06a24a8466ac40664ede6a9ef4a..f3fc53befc9cc1e1edeec2d9093b3044d18dd5c6 100644 (file)
@@ -88,14 +88,14 @@ void vvInteractorStyleNavigator::StartPick()
   if (this->State != VTKIS_NONE) {
     return;
   }
-  this->StartState(VTKIS_PICK);
+  this->StartState(VTKIS_PICK_NEW);
   this->InvokeEvent(vtkCommand::StartPickEvent, this);
 }
 
 //----------------------------------------------------------------------------
 void vvInteractorStyleNavigator::EndPick()
 {
-  if (this->State != VTKIS_PICK) {
+  if (this->State != VTKIS_PICK_NEW) {
     return;
   }
   this->InvokeEvent(vtkCommand::EndPickEvent, this);
@@ -115,7 +115,7 @@ void vvInteractorStyleNavigator::OnMouseMove()
     this->InvokeEvent(vtkCommand::InteractionEvent, NULL);
     break;
 
-  case VTKIS_PICK:
+  case VTKIS_PICK_NEW:
     this->FindPokedRenderer(x, y);
     this->Pick();
     this->InvokeEvent(vtkCommand::InteractionEvent, NULL);
@@ -151,7 +151,7 @@ void vvInteractorStyleNavigator::OnEnter()
   case VTKIS_WINDOW_LEVEL:
     break;
 
-  case VTKIS_PICK:
+  case VTKIS_PICK_NEW:
     break;
 
   case VTKIS_PAN:
@@ -176,7 +176,7 @@ void vvInteractorStyleNavigator::OnLeave()
   case VTKIS_WINDOW_LEVEL:
     break;
 
-  case VTKIS_PICK:
+  case VTKIS_PICK_NEW:
     break;
 
   case VTKIS_PAN:
@@ -266,7 +266,7 @@ void vvInteractorStyleNavigator::OnLeftButtonUp()
 {
   //  DD("OnLeftButtonUp");
   switch (this->State) {
-  case VTKIS_PICK:
+  case VTKIS_PICK_NEW:
     this->EndPick();
     if ( this->Interactor ) {
       this->ReleaseFocus();
index e0de7c0fc0fda275f06fa261b45b17f46747c391..e379292bc62aae2e2b2fdd142d1defb710ffbf60 100644 (file)
@@ -22,7 +22,7 @@
 // Motion flags
 
 #define VTKIS_WINDOW_LEVEL 1024
-#define VTKIS_PICK         1025
+#define VTKIS_PICK_NEW     1025
 
 class vvInteractorStyleNavigator : public vtkInteractorStyle
 
index aa10ded56d9944dcb11d86c8f39085e3bf95f96e..e88a5d9695b961b8a410467d4d1f1de783c21adf 100644 (file)
@@ -902,11 +902,13 @@ void vvMainWindow::LoadImages(std::vector<std::string> files, vvImageReader::Loa
       // Change filename if an image with the same already exist
       int number = GetImageDuplicateFilenameNumber(files[i] + std::string("_slice"));
 
-      if (filetype == vvImageReader::IMAGE || filetype == vvImageReader::IMAGEWITHTIME || filetype == vvImageReader::SLICED) {
+      if (filetype == vvImageReader::IMAGE || filetype == vvImageReader::IMAGEWITHTIME || filetype == vvImageReader::SLICED)
         SetImageSucceed = imageManager->SetImage(files[i],filetype, number, j);
-      } else {
+      else if (filetype == vvImageReader::DICOM)
+        SetImageSucceed = imageManager->SetImages(files,filetype, number, dicomSeriesSelector->IsPatientCoordianteSystemChecked());
+      else
         SetImageSucceed = imageManager->SetImages(files,filetype, number);
-      }
+
       if (!SetImageSucceed) {
         QApplication::restoreOverrideCursor();
         QString error = "Cannot open file \n";
@@ -3302,7 +3304,11 @@ void vvMainWindow::SaveScreenshotAllSlices()
     // Screenshot
     vtkSmartPointer<vtkWindowToImageFilter> windowToImageFilter = vtkSmartPointer<vtkWindowToImageFilter>::New();
     windowToImageFilter->SetInput(renderWindow);
+#if VTK_MAJOR_VERSION >= 8 && VTK_MINOR_VERSION >= 1
+    windowToImageFilter->SetScale(1);
+#else
     windowToImageFilter->SetMagnification(1);
+#endif
     windowToImageFilter->SetInputBufferTypeToRGBA(); //also record the alpha (transparency) channel
     windowToImageFilter->Update();
 
@@ -3352,7 +3358,11 @@ void vvMainWindow::SaveScreenshot(QVTKWidget *widget)
   if (!fileName.isEmpty()) {
     vtkSmartPointer<vtkWindowToImageFilter> w2i = vtkSmartPointer<vtkWindowToImageFilter>::New();
     w2i->SetInput(widget->GetRenderWindow());
+#if VTK_MAJOR_VERSION >= 8 && VTK_MINOR_VERSION >= 1
+    w2i->SetScale(1);
+#else
     w2i->SetMagnification(1);
+#endif
     w2i->SetInputBufferTypeToRGBA(); //also record the alpha (transparency) channel
     w2i->Update();
     vtkImageData *image = w2i->GetOutput();
index 1d6f19b2465cf680439c19a440b890c4cd96f4b2..b3d3c7f24b2b18fee32a4624d794228cdea29f3e 100644 (file)
@@ -27,9 +27,9 @@
 #include <cassert>
 
 vvMeshActor::vvMeshActor() :
-  mCutDimension(0), mMesh(NULL),
-  mMarching(NULL), mMapper(NULL),
-  mActor(NULL),
+  mCutDimension(0), mMesh(ITK_NULLPTR),
+  mMarching(ITK_NULLPTR), mMapper(ITK_NULLPTR),
+  mActor(ITK_NULLPTR),
   mSuperpostionMode(false), mTimeSlice(0)
 {}
 
index 61cee8fcd1a9cdb00b07b48cb46b07f92d0b1825..37bb0dcc69376ed53056c71811bed04d889d7237 100644 (file)
@@ -37,7 +37,7 @@ public:
     void SetCutSlice(double slice);
     /**Initialize the actor and set the inputs. If vf is not null, will use
      **it to propagate the contour on all time frames */ 
-    void Init(vvMesh::Pointer mesh,int time_slice, vvImage::Pointer vf=NULL);
+    void Init(vvMesh::Pointer mesh,int time_slice, vvImage::Pointer vf=ITK_NULLPTR);
     ///Set the time slice (only useful when using a 4D contour)
     void SetTimeSlice(int time);
     ///Toggles between normal 4D mode and superposition mode
index 44850bb158aadbd504b4d2edd3c033023e1d2ec5..9c279827128062caf963b9207ff8d247ced02664 100644 (file)
@@ -58,6 +58,8 @@ vvDicomSeriesSelector::vvDicomSeriesSelector(QWidget* parent)
           this, SLOT(itemSelectionChanged()));
   connect(ui.mDicomDetailsListWidget, SIGNAL(itemSelectionChanged()),
           this, SLOT(itemDetailsSelectionChanged()));
+  connect(ui.mIsMatrixCheckBox, SIGNAL(stateChanged(int)),
+          this, SLOT(itemMatrixSelectionChanged(int)));
 
   // Initialization
   /*   if (config::get_current_path() != QString(0))
@@ -67,6 +69,8 @@ vvDicomSeriesSelector::vvDicomSeriesSelector(QWidget* parent)
 
   mPreviousPath = mFoldername;
   ui.mFolderLineEdit->setText(mFoldername);
+  ui.mIsMatrixCheckBox->setChecked(0);
+  mPatientCoordinateSystem = false;
   //  ui.mTableWidget->setRowCount(0);
 }
 //====================================================================
@@ -401,4 +405,13 @@ void vvDicomSeriesSelector::AddSerieToTheTable(int i, std::vector<std::string> &
 }
 //====================================================================
 
+
+//====================================================================
+void vvDicomSeriesSelector::itemMatrixSelectionChanged(int state)
+{
+  mPatientCoordinateSystem = state;
+}
+//====================================================================
+
+
 #endif // VVDICOMSERIESSELECTOR_CXX
index 8b3aaac0deab00f0f8f3f5e3e8da3fcc28380a57..aa905fcfead1932b43474dd4afd732dc1f74a729 100644 (file)
@@ -37,12 +37,14 @@ public:
     std::vector<std::string> * GetFilenames() {
         return mFilenames;
     }
+    bool IsPatientCoordianteSystemChecked() { return mPatientCoordinateSystem; }
 
 protected slots:
     void BrowseButtonRelease();
     void SearchButtonRelease();
     void itemSelectionChanged();
     void itemDetailsSelectionChanged();
+    void itemMatrixSelectionChanged(int state);
 
 protected:
     QString mPreviousPath;
@@ -68,6 +70,7 @@ private:
     std::map<std::string, gdcm::File*> mDicomHeader;
 #endif
     std::map<std::string, std::string> mDicomDetails;
+    bool mPatientCoordinateSystem;
 };
 
 #endif // VVDICOMSERIESSELECTOR_H
index 4e1a9dc880b487c8b38969c67f567e1fb44cd5a4..d6f5089722b056ce41a81cb29542387ed2e68f67 100644 (file)
@@ -185,12 +185,11 @@ void vvROIActor::Initialize(double depth, bool IsVisible)
       mOverlayActors.push_back(vvBinaryImageOverlayActor::New());
 
       // BG or FG
-      if (m_modeBG) {
-       mOverlayActors[i]->SetImage(mROI->GetImage(), mROI->GetBackgroundValueLabelImage());
-      }
-      else {
-       mOverlayActors[i]->SetImage(mROI->GetImage(), mROI->GetForegroundValueLabelImage(), false);
-      }
+      if (m_modeBG)
+        mOverlayActors[i]->SetImage(mROI->GetImage(), mROI->GetBackgroundValueLabelImage());
+      else
+        mOverlayActors[i]->SetImage(mROI->GetImage(), mROI->GetForegroundValueLabelImage(), false);
+
 
 
       mOverlayActors[i]->SetColor(mROI->GetDisplayColor()[0],
@@ -236,7 +235,7 @@ void vvROIActor::Update(bool force)
 
 //------------------------------------------------------------------------------
 void vvROIActor::UpdateSlice(int slicer, int slices, int force)
-{ 
+{
   if (!mROI->GetImage())  return;
   if ((!mIsVisible) && (!mIsContourVisible)) return; 
   if (!mSlicerManager) {
index 04c90290f9fc16c9465ee6d4a332cf51ea682244..799d339288fa52e82e0b9a2178f71d45a833bc58 100644 (file)
@@ -70,7 +70,7 @@ public slots:
   vvSlicerManager * mSlicerManager;
   std::vector<vvImageContour::Pointer> mImageContour;
   std::vector< vvBinaryImageOverlayActor::Pointer > mOverlayActors;
-  
+
   bool mIsVisible;
   bool mIsContourVisible;
   double mOpacity;
index ce6d335abdd69cac344c49c0305f596be7fb9ebf..a53daf24da6eda141db18747f319af5fb0d452cd 100644 (file)
@@ -359,8 +359,7 @@ void vvSlicer::SetImage(vvImage::Pointer image)
     mConcatenatedTransform->Identity();
     mConcatenatedTransform->Concatenate(mImage->GetTransform()[0]);
     mConcatenatedTransform->Concatenate(mSlicingTransform);
-    mImageReslice->SetResliceTransform(mConcatenatedTransform);
-    //mImageReslice->SetResliceAxes(mConcatenatedTransform->GetMatrix());
+    mImageReslice->SetResliceAxes(mConcatenatedTransform->GetMatrix());
 #if VTK_MAJOR_VERSION <= 5
     mImageReslice->SetInput(0, mImage->GetFirstVTKImageData());
 #else
@@ -378,8 +377,7 @@ void vvSlicer::SetImage(vvImage::Pointer image)
 #if VTK_MAJOR_VERSION <= 5
     this->GetInput()->GetWholeExtent(extent);
 #else
-    int* ext = mImageReslice->GetInputInformation()->Get(vtkStreamingDemandDrivenPipeline::WHOLE_EXTENT());
-    copyExtent(ext, extent);
+    mImageReslice->GetOutput()->GetExtent(extent);
 #endif
 
     // Prevent crash when reload -> change slice if outside extent
@@ -393,6 +391,9 @@ void vvSlicer::SetImage(vvImage::Pointer image)
 #if VTK_MAJOR_VERSION <= 5
     mImageReslice->GetOutput()->SetUpdateExtent(extent);
     mImageReslice->GetOutput()->Update();
+#elif VTK_MAJOR_VERSION >= 8 || (VTK_MAJOR_VERSION == 7 && VTK_MINOR_VERSION >= 1)
+    mImageReslice->UpdateExtent(extent);
+    mImageReslice->Update();
 #else
     mImageReslice->SetUpdateExtent(extent);
     mImageReslice->Update();
@@ -424,7 +425,7 @@ void vvSlicer::SetOverlay(vvImage::Pointer overlay)
     mConcatenatedOverlayTransform->Identity();
     mConcatenatedOverlayTransform->Concatenate(mOverlay->GetTransform()[0]);
     mConcatenatedOverlayTransform->Concatenate(mSlicingTransform);
-    mOverlayReslice->SetResliceTransform(mConcatenatedOverlayTransform);
+    mOverlayReslice->SetResliceAxes(mConcatenatedOverlayTransform->GetMatrix());
 #if VTK_MAJOR_VERSION <= 5
     mOverlayReslice->SetInput(0, mOverlay->GetFirstVTKImageData());
     mImageReslice->UpdateInformation();
@@ -491,7 +492,7 @@ void vvSlicer::SetFusion(vvImage::Pointer fusion, int fusionSequenceCode)
     mConcatenatedFusionTransform->Identity();
     mConcatenatedFusionTransform->Concatenate(mFusion->GetTransform()[0]);
     mConcatenatedFusionTransform->Concatenate(mSlicingTransform);
-    mFusionReslice->SetResliceTransform(mConcatenatedFusionTransform);
+    mFusionReslice->SetResliceAxes(mConcatenatedFusionTransform->GetMatrix());
 #if VTK_MAJOR_VERSION <= 5
     mFusionReslice->SetInput(0, mFusion->GetFirstVTKImageData());
     mFusionReslice->UpdateInformation();
@@ -596,7 +597,7 @@ void vvSlicer::SetVF(vvImage::Pointer vf)
     mConcatenatedVFTransform->Identity();
     mConcatenatedVFTransform->Concatenate(mVF->GetTransform()[0]);
     mConcatenatedVFTransform->Concatenate(mSlicingTransform);
-    mVFReslice->SetResliceTransform(mConcatenatedVFTransform);
+    mVFReslice->SetResliceAxes(mConcatenatedVFTransform->GetMatrix());
 #if VTK_MAJOR_VERSION <= 5
     mVFReslice->SetInput(0, mVF->GetFirstVTKImageData());
 #else
@@ -1790,6 +1791,8 @@ void vvSlicer::Render()
     }
 #if VTK_MAJOR_VERSION <= 5
     mOverlayMapper->GetOutput()->SetUpdateExtent(mOverlayActor->GetDisplayExtent());
+#elif VTK_MAJOR_VERSION >= 8 || (VTK_MAJOR_VERSION == 7 && VTK_MINOR_VERSION >= 1)
+    mOverlayMapper->UpdateExtent(mOverlayActor->GetDisplayExtent());
 #else
     mOverlayMapper->SetUpdateExtent(mOverlayActor->GetDisplayExtent());
 #endif
@@ -1798,6 +1801,8 @@ void vvSlicer::Render()
   if (mFusion && mFusionActor->GetVisibility()) {
 #if VTK_MAJOR_VERSION <= 5
     mFusionMapper->GetOutput()->SetUpdateExtent(mFusionActor->GetDisplayExtent());
+#elif VTK_MAJOR_VERSION >= 8 || (VTK_MAJOR_VERSION == 7 && VTK_MINOR_VERSION >= 1)
+    mFusionMapper->UpdateExtent(mFusionActor->GetDisplayExtent());
 #else
     mFusionMapper->SetUpdateExtent(mFusionActor->GetDisplayExtent());
 #endif
index 139c8089afc5d8a61d07db22f7d11c75e787a7d8..9548dfded6f2944a4f61dadb3480b57ccd7747c9 100644 (file)
@@ -221,7 +221,7 @@ void vvSlicerManager::SetImage(vvImage::Pointer image)
 
 
 //----------------------------------------------------------------------------
-bool vvSlicerManager::SetImages(std::vector<std::string> filenames, vvImageReader::LoadedImageType type, int n)
+bool vvSlicerManager::SetImages(std::vector<std::string> filenames, vvImageReader::LoadedImageType type, int n, bool patientCoordinateSystem)
 { 
   mType = type;
   std::string fileWithoutExtension = vtksys::SystemTools::GetFilenameWithoutExtension(filenames[0]);
@@ -237,6 +237,8 @@ bool vvSlicerManager::SetImages(std::vector<std::string> filenames, vvImageReade
   if (mReader.IsNull())
     mReader = vvImageReader::New();
   mReader->SetInputFilenames(filenames);
+  if (type == vvImageReader::DICOM)
+    mReader->SetPatientCoordinateSystem(patientCoordinateSystem);
   mReader->Update(type);
 
   mBaseFileName = vtksys::SystemTools::GetFilenameName(vtksys::SystemTools::GetFilenameWithoutLastExtension(mFileName));
@@ -1392,7 +1394,6 @@ void vvSlicerManager::SetLocalColorWindowing(const int slicer, const bool bCtrlK
     this->SetColorLevel(0.5*(min+max));
     this->SetPreset(WL_USER);
   }
-  this->Render();
   this->UpdateWindowLevel();
 }
 //----------------------------------------------------------------------------
index 0d5c9aee218eacc9be8b615a1ce98608ce8d4eed..4acb1668cd22ed64e4b20abf07e883671771982a 100644 (file)
@@ -74,7 +74,7 @@ class vvSlicerManager : public QObject {
 
   bool SetImage(std::string filename, vvImageReader::LoadedImageType type, int n=0, unsigned int slice=0);
   void SetImage(vvImage::Pointer image);
-  bool SetImages(std::vector<std::string> filenames, vvImageReader::LoadedImageType type, int n=0);
+  bool SetImages(std::vector<std::string> filenames, vvImageReader::LoadedImageType type, int n=0, bool patientCoordinateSystem=0);
 
   bool SetOverlay(std::vector<std::string> filenames, int dim, std::string component, vvImageReader::LoadedImageType type);
   bool SetFusion(std::vector<std::string> filenames,int dim, std::string component, vvImageReader::LoadedImageType type);
index 24b0864f70b59621695c02937f5f51700d644f8e..293fbcde3991f30e91ba1c547e4b52e8099b2cec 100644 (file)
@@ -447,7 +447,6 @@ void vvToolHistogram::SaveAs()
         
         vtkSmartPointer<vtkFloatArray> arrX = vtkSmartPointer<vtkFloatArray>::New();
         vtkSmartPointer<vtkFloatArray> arrY = vtkSmartPointer<vtkFloatArray>::New();
-        vtkSmartPointer<vtkFloatArray> coords = vtkSmartPointer<vtkFloatArray>::New();
         arrX = mFilter->GetArrayX();
         arrY = mFilter->GetArrayY();
         int i(0);
index 7ef23101a2ab9d5d0263caad118f0c09a6120e73..baf34f86bc35daa0e393d35388bd50633aa11ba8 100644 (file)
@@ -615,7 +615,11 @@ void vvToolProfile::InitializeLine()
       unsigned char red[3] = { 255, 0, 0 };
       vtkSmartPointer<vtkUnsignedCharArray> colors = vtkSmartPointer<vtkUnsignedCharArray>::New();
       colors->SetNumberOfComponents(3);
+#if VTK_MAJOR_VERSION >= 8 || (VTK_MAJOR_VERSION == 7 && VTK_MINOR_VERSION >= 1)
+      colors->InsertNextTypedTuple(red);
+#else
       colors->InsertNextTupleValue(red);
+#endif
       mLinesPolyData->GetCellData()->SetScalars(colors);
       
       for(int i=0;i<mCurrentSlicerManager->GetNumberOfSlicers(); i++) {
index 4c2832e0ced363bbc1d1cfe995f47a86700f8872..00eff1df1472e45fef50c19338c488d03c4eacc2 100644 (file)
@@ -55,7 +55,7 @@ vvToolROIManager::vvToolROIManager(vvMainWindowBase * parent, Qt::WindowFlags f)
   QWidget(parent->GetTab()),
   vvToolBase<vvToolROIManager>(parent),
   Ui::vvToolROIManager()
-{ 
+{
   // Store parent
   mMainWindow = parent;
 
@@ -95,7 +95,7 @@ vvToolROIManager::vvToolROIManager(vvMainWindowBase * parent, Qt::WindowFlags f)
 
 //------------------------------------------------------------------------------
 vvToolROIManager::~vvToolROIManager()
-{ 
+{
   mROIActorsList.clear();
 }
 //------------------------------------------------------------------------------
@@ -104,7 +104,7 @@ vvToolROIManager::~vvToolROIManager()
 //------------------------------------------------------------------------------
 // STATIC
 void vvToolROIManager::Initialize()
-{ 
+{
   SetToolName("ROIManager");
   SetToolMenuName("Open ROI (binary image or RT-STRUCT)");
   SetToolIconFilename(":/common/icons/tool-roi.png");
@@ -117,7 +117,7 @@ void vvToolROIManager::Initialize()
 
 //------------------------------------------------------------------------------
 void  vvToolROIManager::InitializeNewTool(bool ReadStateFlag)
-{ 
+{
   // Check if we need to start a new tool or read in the state file to load
   if (ReadStateFlag == false) {
     // Select the current image as the target
@@ -224,7 +224,7 @@ void  vvToolROIManager::InitializeNewTool(bool ReadStateFlag)
 
 //------------------------------------------------------------------------------
 void vvToolROIManager::InputIsSelected(vvSlicerManager *m)
-{ 
+{
   // Initialization
   mCurrentSlicerManager = m;
   mCurrentImage = mCurrentSlicerManager->GetImage();
@@ -240,7 +240,7 @@ void vvToolROIManager::InputIsSelected(vvSlicerManager *m)
 
 //------------------------------------------------------------------------------
 void vvToolROIManager::AnImageIsBeingClosed(vvSlicerManager * m)
-{ 
+{
   if (m == mCurrentSlicerManager) {
     close();
     return;
@@ -249,7 +249,7 @@ void vvToolROIManager::AnImageIsBeingClosed(vvSlicerManager * m)
 //------------------------------------------------------------------------------
 
 void vvToolROIManager::RemoveROI()
-{ 
+{
 
   // Search the indice of the selected ROI
   QList<QTreeWidgetItem *> l = mTree->selectedItems();
@@ -288,7 +288,7 @@ void vvToolROIManager::RemoveROI()
 
 //------------------------------------------------------------------------------
 void vvToolROIManager::close()
-{ 
+{
   disconnect(mTree, SIGNAL(itemSelectionChanged()), this, SLOT(SelectedItemChangedInTree()));
   disconnect(mCheckBoxShow, SIGNAL(toggled(bool)), this, SLOT(VisibleROIToggled(bool)));
   disconnect(mOpacitySlider, SIGNAL(valueChanged(int)), this, SLOT(OpacityChanged(int)));
@@ -315,7 +315,7 @@ void vvToolROIManager::close()
 
 //------------------------------------------------------------------------------
 void vvToolROIManager::SelectedImageHasChanged(vvSlicerManager * m)
-{ 
+{
   if (mCurrentSlicerManager == NULL) return;
   if (m == NULL) return;
   if (m != mCurrentSlicerManager) hide();
@@ -328,7 +328,7 @@ void vvToolROIManager::SelectedImageHasChanged(vvSlicerManager * m)
 
 //------------------------------------------------------------------------------
 void vvToolROIManager::Open()
-{ 
+{
   // Open images
   QString Extensions = "Images or Dicom-Struct files ( *.mha *.mhd *.hdr *.his *.dcm RS*)";
   Extensions += ";;All Files (*)";
@@ -349,7 +349,7 @@ void vvToolROIManager::Open()
 
 //------------------------------------------------------------------------------
 void vvToolROIManager::OpenBinaryImage(QStringList & filename)
-{ 
+{
   if (filename.size() == 0) return;
 
   vvProgressDialog p("Reading ROI ...", true);
@@ -393,7 +393,7 @@ void vvToolROIManager::OpenBinaryImage(QStringList & filename)
 
 //------------------------------------------------------------------------------
 void vvToolROIManager::OpenDicomImage(std::string filename)
-{ 
+{
   // GUI selector of roi
   vvMeshReader reader;
   reader.SetFilename(filename);
@@ -409,6 +409,9 @@ void vvToolROIManager::OpenDicomImage(std::string filename)
 
     // Read information
     clitk::DicomRT_StructureSet::Pointer s = clitk::DicomRT_StructureSet::New();
+    vtkSmartPointer<vtkMatrix4x4> transformMatrix = vtkSmartPointer<vtkMatrix4x4>::New();
+    transformMatrix = mCurrentImage->GetTransform()[0]->GetMatrix();
+    s->SetTransformMatrix(transformMatrix);
     s->Read(filename);
 
     // Loop on selected struct
@@ -445,7 +448,7 @@ void vvToolROIManager::AddImage(vvImage * binaryImage,
                                 std::string name,
                                 std::string filename,
                                 double BG, bool modeBG)
-{ 
+{
   // Check Dimension
   int dim = mCurrentImage->GetNumberOfDimensions();
   int bin_dim = binaryImage->GetNumberOfDimensions();
@@ -547,7 +550,7 @@ void vvToolROIManager::AddImage(vvImage * binaryImage,
 
 //------------------------------------------------------------------------------
 void vvToolROIManager::UpdateAllContours()
-{ 
+{
   if (mCurrentSlicerManager == NULL) return;
   // Render loaded ROIs (the first is sufficient)
   for(unsigned int i=0; i<mROIList.size(); i++) {
@@ -560,7 +563,7 @@ void vvToolROIManager::UpdateAllContours()
 
 //------------------------------------------------------------------------------
 void vvToolROIManager::UpdateAllROIStatus()
-{ 
+{
   int nbVisible = 0;
   int nbContourVisible = 0;
   int nb = mROIList.size();
@@ -594,7 +597,7 @@ void vvToolROIManager::UpdateAllROIStatus()
 
 //------------------------------------------------------------------------------
 void vvToolROIManager::SelectedItemChangedInTree()
-{ 
+{
   // Search which roi is selected
   QList<QTreeWidgetItem *> l = mTree->selectedItems();
   if (l.size() == 0) {
@@ -679,7 +682,7 @@ void vvToolROIManager::SelectedItemChangedInTree()
 
 //------------------------------------------------------------------------------
 void vvToolROIManager::VisibleROIToggled(bool b)
-{ 
+{
   if (mCurrentROIActor == NULL) return;
   if (b == mCurrentROIActor->IsVisible()) return; // nothing to do
   mCurrentROIActor->SetVisible(b);
@@ -691,7 +694,7 @@ void vvToolROIManager::VisibleROIToggled(bool b)
 
 //------------------------------------------------------------------------------
 void vvToolROIManager::VisibleContourROIToggled(bool b)
-{ 
+{
   if (mCurrentROIActor == NULL) return;
   if (mCurrentROIActor->IsContourVisible() == b) return; // nothing to do
   mCurrentROIActor->SetContourVisible(b);
@@ -704,7 +707,7 @@ void vvToolROIManager::VisibleContourROIToggled(bool b)
 
 //------------------------------------------------------------------------------
 void vvToolROIManager::OpacityChanged(int v)
-{ 
+{
   if (mCurrentROIActor == NULL) return;
   mCurrentROIActor->SetOpacity((double)v/100.0);
   mCurrentROIActor->UpdateColor();
@@ -715,7 +718,7 @@ void vvToolROIManager::OpacityChanged(int v)
 
 //------------------------------------------------------------------------------
 void vvToolROIManager::AllVisibleROIToggled(int b)
-{ 
+{
   disconnect(mCheckBoxShowAll, SIGNAL(stateChanged(int)), this, SLOT(AllVisibleROIToggled(int)));
   bool status = false;
   if ((mCheckBoxShowAll->checkState() == Qt::Checked) ||
@@ -735,7 +738,7 @@ void vvToolROIManager::AllVisibleROIToggled(int b)
 
 //------------------------------------------------------------------------------
 void vvToolROIManager::AllVisibleContourROIToggled(int b)
-{ 
+{
   disconnect(mContourCheckBoxShowAll, SIGNAL(stateChanged(int)), this, SLOT(AllVisibleContourROIToggled(int)));
   bool status = false;
   if ((mContourCheckBoxShowAll->checkState() == Qt::Checked) ||
@@ -756,7 +759,7 @@ void vvToolROIManager::AllVisibleContourROIToggled(int b)
 
 //------------------------------------------------------------------------------
 void vvToolROIManager::ChangeColor()
-{ 
+{
   if (mCurrentROIActor == NULL) return;
   QColor color;
   color.setRgbF(mCurrentROIActor->GetROI()->GetDisplayColor()[0],
@@ -782,7 +785,7 @@ void vvToolROIManager::ChangeColor()
 
 //------------------------------------------------------------------------------
 void vvToolROIManager::ChangeContourColor()
-{ 
+{
   if (mCurrentROIActor == NULL) return;
   QColor color;
   color.setRgbF(mCurrentROIActor->GetContourColor()[0],
@@ -800,7 +803,7 @@ void vvToolROIManager::ChangeContourColor()
 
 //------------------------------------------------------------------------------
 void vvToolROIManager::ChangeContourWidth(int n)
-{ 
+{
   if (mCurrentROIActor == NULL) return;
   mCurrentROIActor->SetContourWidth(n);
   mCurrentROIActor->UpdateColor();
@@ -811,7 +814,7 @@ void vvToolROIManager::ChangeContourWidth(int n)
 
 //------------------------------------------------------------------------------
 void vvToolROIManager::ChangeDepth(int n)
-{ 
+{
   if (mCurrentROIActor == NULL) return;
   mCurrentROIActor->SetDepth(n);
   // mCurrentROIActor->UpdateImage(); // FIXME
@@ -825,7 +828,7 @@ void vvToolROIManager::ChangeDepth(int n)
 
 //------------------------------------------------------------------------------
 void vvToolROIManager::ReloadCurrentROI()
-{ 
+{
   if (mCurrentROI->GetFilename() == "") {
     return; // do nothing (contour from rt struct do not reload)
   }
@@ -870,7 +873,7 @@ void  vvToolROIManager::SaveState(std::shared_ptr<QXmlStreamWriter> & m_XmlWrite
 #else
 void  vvToolROIManager::SaveState(std::auto_ptr<QXmlStreamWriter> & m_XmlWriter)
 #endif
-{ 
+{
   // Get index of the image
   int n = mMainWindow->GetSlicerManagers().size();
   int index=-1;
@@ -916,7 +919,7 @@ void  vvToolROIManager::SaveState(std::auto_ptr<QXmlStreamWriter> & m_XmlWriter)
 
 //------------------------------------------------------------------------------
 void vvToolROIManager::ReadXMLInformation()
-{ 
+{
   std::string value="";
   mInitialImageIndex = -1;
   while (!(m_XmlReader->isEndElement() && value == GetToolName().toStdString())) {
@@ -938,7 +941,7 @@ void vvToolROIManager::ReadXMLInformation()
 
 //------------------------------------------------------------------------------
 void vvToolROIManager::ReadXMLInformation_ROI()
-{ 
+{
   QString s;
   std::string value="";
   QSharedPointer<vvROIActor> param = QSharedPointer<vvROIActor>(new vvROIActor);