diff --git a/SConstruct b/SConstruct index b9a3faf9c..bbeb231cf 100644 --- a/SConstruct +++ b/SConstruct @@ -39,8 +39,8 @@ opts.Add(PathOption('BOOST_LIBS', 'Search path for boost library files', '/usr/' opts.Add('BOOST_TOOLKIT','Specify boost toolkit e.g. gcc41.','',False) opts.Add(('FREETYPE_CONFIG', 'The path to the freetype-config executable.', 'freetype-config')) opts.Add(('XML2_CONFIG', 'The path to the xml2-config executable.', 'xml2-config')) -opts.Add(PathOption('FRIBIDI_INCLUDES', 'Search path for fribidi include files', '/usr/include')) -opts.Add(PathOption('FRIBIDI_LIBS','Search path for fribidi include files','/usr/' + LIBDIR_SCHEMA)) +opts.Add(PathOption('ICU_INCLUDES', 'Search path for ICU include files', '/usr/include')) +opts.Add(PathOption('ICU_LIBS','Search path for ICU include files','/usr/' + LIBDIR_SCHEMA)) opts.Add(PathOption('PNG_INCLUDES', 'Search path for libpng include files', '/usr/include')) opts.Add(PathOption('PNG_LIBS','Search path for libpng include files','/usr/' + LIBDIR_SCHEMA)) opts.Add(PathOption('JPEG_INCLUDES', 'Search path for libjpeg include files', '/usr/include')) @@ -58,7 +58,6 @@ opts.Add(ListOption('INPUT_PLUGINS','Input drivers to include','all',['postgis', opts.Add(ListOption('BINDINGS','Language bindings to build','all',['python'])) opts.Add(BoolOption('DEBUG', 'Compile a debug version of mapnik', 'False')) opts.Add('DESTDIR', 'The root directory to install into. Useful mainly for binary package building', '/') -opts.Add(BoolOption('BIDI', 'BIDI support', 'False')) opts.Add(EnumOption('THREADING','Set threading support','multi', ['multi','single'])) opts.Add(EnumOption('XMLPARSER','Set xml parser ','tinyxml', ['tinyxml','spirit','libxml2'])) @@ -124,14 +123,8 @@ for prereq in ('BOOST', 'PNG', 'JPEG', 'TIFF', 'PGSQL', 'PROJ', 'GDAL',): uniq_add(env, 'LIBPATH', lib_path) env.ParseConfig(env['FREETYPE_CONFIG'] + ' --libs --cflags') - -if env['BIDI']: - env.Append(CXXFLAGS = '-DUSE_FRIBIDI') - if env['FRIBIDI_INCLUDES'] not in env['CPPPATH']: - env['CPPPATH'].append(env['FRIBIDI_INCLUDES']) - if env['FRIBIDI_LIBS'] not in env['LIBPATH']: - env['LIBPATH'].append(env['FRIBIDI_LIBS']) - env['LIBS'].append('fribidi') + +#env.ParseConfig('pkg-config --libs --cflags cairomm-1.0') if env['XMLPARSER'] == 'tinyxml': env.Append(CXXFLAGS = '-DBOOST_PROPERTY_TREE_XML_PARSER_TINYXML -DTIXML_USE_STL') @@ -147,16 +140,15 @@ C_LIBSHEADERS = [ ['z', 'zlib.h', True], ['jpeg', ['stdio.h', 'jpeglib.h'], True], ['proj', 'proj_api.h', True], - ['iconv', 'iconv.h', False], ['pq', 'libpq-fe.h', False] ] CXX_LIBSHEADERS = [ + ['icuuc','unicode/unistr.h',True], + ['icudata','unicode/utypes.h' , True], ['gdal', 'gdal_priv.h',False] ] -if env['BIDI'] : C_LIBSHEADERS.append(['fribidi','fribidi/fribidi.h',True]) - BOOST_LIBSHEADERS = [ # ['system', 'boost/system/system_error.hpp', True], # uncomment this on Darwin + boost_1_35 ['filesystem', 'boost/filesystem/operations.hpp', True], diff --git a/bindings/python/SConscript b/bindings/python/SConscript index bfd53a9c5..3a8db238e 100644 --- a/bindings/python/SConscript +++ b/bindings/python/SConscript @@ -41,6 +41,8 @@ else : libraries.append('boost_python%s' % env['BOOST_APPEND']) if env['PLATFORM'] == 'Darwin': + libraries.append('icuuc') + libraries.append('icudata') if env['THREADING'] == 'multi': libraries.append('boost_regex%s%s' % (env['BOOST_APPEND'],thread_suffix)) else : diff --git a/bindings/python/mapnik/ogcserver/common.py b/bindings/python/mapnik/ogcserver/common.py index b205fa2e6..739b52f12 100644 --- a/bindings/python/mapnik/ogcserver/common.py +++ b/bindings/python/mapnik/ogcserver/common.py @@ -283,11 +283,7 @@ class WMSBaseServiceHandler(BaseServiceHandler): m = self._buildMap(params) im = Image(params['width'], params['height']) render(m, im) - im = fromstring('RGBA', (params['width'], params['height']), rawdata(im)) - fh = StringIO() - im.save(fh, PIL_TYPE_MAPPING[params['format']], quality=100) - fh.seek(0) - return Response(params['format'], fh.read()) + return Response(params['format'], im.tostring(params['format']) def GetFeatureInfo(self, params, querymethodname='query_point'): m = self._buildMap(params) @@ -460,4 +456,4 @@ class XMLFeatureInfo: self.currentfeature.append(attribute) def __str__(self): - return '\n' + ElementTree.tostring(self.rootelement) \ No newline at end of file + return '\n' + ElementTree.tostring(self.rootelement) diff --git a/bindings/python/mapnik_feature.cpp b/bindings/python/mapnik_feature.cpp index 015982b67..2bf7e78cb 100644 --- a/bindings/python/mapnik_feature.cpp +++ b/bindings/python/mapnik_feature.cpp @@ -43,9 +43,9 @@ namespace boost { namespace python { return ::PyFloat_FromDouble(val); } - PyObject * operator() (std::wstring const& s) const + PyObject * operator() (UnicodeString const& s) const { - return ::PyUnicode_FromWideChar(s.data(),implicit_cast(s.size())); + return ::PyUnicode_FromWideChar((wchar_t*)s.getBuffer(),implicit_cast(s.length())); } }; diff --git a/include/mapnik/expression.hpp b/include/mapnik/expression.hpp index 887296a6c..05f830603 100644 --- a/include/mapnik/expression.hpp +++ b/include/mapnik/expression.hpp @@ -51,7 +51,7 @@ namespace mapnik { literal(double val) : expression(), value_(val) {} - literal(std::wstring const& val) + literal(UnicodeString const& val) : expression(), value_(val) {} literal(literal const& other) diff --git a/include/mapnik/filter_parser.hpp b/include/mapnik/filter_parser.hpp index 3c6baff1f..0d9ec0be8 100644 --- a/include/mapnik/filter_parser.hpp +++ b/include/mapnik/filter_parser.hpp @@ -91,16 +91,17 @@ namespace mapnik template void operator() (Iter start,Iter end) const { - std::wstring str(start,end); - std::wstring quote = L"\\"; - std::wstring::size_type idx; - idx = str.find(quote); - while (idx != string::npos) - { - str.erase(idx,1); - idx = str.find(quote); - } - exprs_.push(shared_ptr >(new literal(str))); + //std::wstring str(start,end); + //std::wstring quote = L"\\"; + //std::wstring::size_type idx; + //idx = str.find(quote); + //while (idx != string::npos) + ///{ + // str.erase(idx,1); + // idx = str.find(quote); + //} + UnicodeString str(start,end-start); + exprs_.push(shared_ptr >(new literal(str))); } stack > >& exprs_; }; diff --git a/include/mapnik/font_engine_freetype.hpp b/include/mapnik/font_engine_freetype.hpp index a62c7678c..71371cbdc 100644 --- a/include/mapnik/font_engine_freetype.hpp +++ b/include/mapnik/font_engine_freetype.hpp @@ -52,6 +52,10 @@ extern "C" #include #include +// icu +#include +#include + namespace mapnik { class font_face : boost::noncopyable @@ -84,7 +88,12 @@ namespace mapnik { return face_; } - + + unsigned get_char(unsigned c) const + { + return FT_Get_Char_Index(face_, c); + } + bool set_pixel_sizes(unsigned size) { if (! FT_Set_Pixel_Sizes( face_, 0, size )) @@ -311,19 +320,75 @@ namespace mapnik { unsigned width = 0; unsigned height = 0; + UErrorCode err = U_ZERO_ERROR; + UnicodeString const& ustr = info.get_string(); + const UChar * text = ustr.getBuffer(); + UBiDi * bidi = ubidi_openSized(ustr.length(),0,&err); - std::wstring const& text = info.get_string(); - - for (std::wstring::const_iterator i=text.begin();i!=text.end();++i) + if (U_SUCCESS(err)) { - dimension_t char_dim = character_dimensions(*i); - - info.add_info(*i, char_dim.first, char_dim.second); - - width += char_dim.first; - height = char_dim.second > height ? char_dim.second : height; + ubidi_setPara(bidi,text,ustr.length(), UBIDI_DEFAULT_LTR,0,&err); + if (U_SUCCESS(err)) + { + int32_t count = ubidi_countRuns(bidi,&err); + int32_t logicalStart; + int32_t length; + + for (int32_t i=0; i< count;++i) + { + if (UBIDI_LTR == ubidi_getVisualRun(bidi,i,&logicalStart,&length)) + { + do { + UChar ch = text[logicalStart++]; + dimension_t char_dim = character_dimensions(ch); + info.add_info(ch, char_dim.first, char_dim.second); + width += char_dim.first; + height = char_dim.second > height ? char_dim.second : height; + + } while (--length > 0); + } + else + { + logicalStart += length; + + int32_t j=0,i=length; + UnicodeString arabic; + UChar * buf = arabic.getBuffer(length); + do { + UChar ch = text[--logicalStart]; + buf[j++] = ch; + } while (--i > 0); + + arabic.releaseBuffer(length); + if ( *arabic.getBuffer() >= 0x0600 && *arabic.getBuffer() <= 0x06ff) + { + + UnicodeString shaped; + u_shapeArabic(arabic.getBuffer(),arabic.length(),shaped.getBuffer(arabic.length()),arabic.length(), + U_SHAPE_LETTERS_SHAPE|U_SHAPE_LENGTH_FIXED_SPACES_NEAR| + U_SHAPE_TEXT_DIRECTION_VISUAL_LTR + ,&err); + + shaped.releaseBuffer(arabic.length()); + + if (U_SUCCESS(err)) + { + for (int j=0;j height ? char_dim.second : height; + } + } + } + } + } + } + ubidi_close(bidi); } + info.set_dimensions(width, height); } diff --git a/include/mapnik/label_collision_detector.hpp b/include/mapnik/label_collision_detector.hpp index 1352f8588..7563d9455 100644 --- a/include/mapnik/label_collision_detector.hpp +++ b/include/mapnik/label_collision_detector.hpp @@ -28,6 +28,7 @@ #include // stl #include +#include namespace mapnik { @@ -139,10 +140,10 @@ namespace mapnik struct label { label(Envelope const& b) : box(b) {} - label(Envelope const& b, std::wstring const& t) : box(b), text(t) {} + label(Envelope const& b, UnicodeString const& t) : box(b), text(t) {} Envelope box; - std::wstring text; + UnicodeString text; }; typedef quad_tree< label > tree_t; @@ -171,7 +172,7 @@ namespace mapnik return true; } - bool has_placement(Envelope const& box, std::wstring const& text, double distance) + bool has_placement(Envelope const& box, UnicodeString const& text, double distance) { Envelope bigger_box(box.minx() - distance, box.miny() - distance, box.maxx() + distance, box.maxy() + distance); @@ -195,7 +196,7 @@ namespace mapnik tree_.insert(label(box), box); } - void insert(Envelope const& box, std::wstring const& text) + void insert(Envelope const& box, UnicodeString const& text) { tree_.insert(label(box, text), box); } diff --git a/include/mapnik/text_path.hpp b/include/mapnik/text_path.hpp index c0f0cebaf..94b25639d 100644 --- a/include/mapnik/text_path.hpp +++ b/include/mapnik/text_path.hpp @@ -27,6 +27,7 @@ #define __TEXT_PATH_H__ #include +#include namespace mapnik { @@ -51,11 +52,11 @@ namespace mapnik protected: typedef boost::ptr_vector characters_t; characters_t characters_; - std::wstring const& text_; + UnicodeString const& text_; double width_; double height_; public: - string_info(std::wstring const& text) + string_info(UnicodeString const& text) : text_(text), width_(0), height_(0) {} @@ -91,7 +92,7 @@ namespace mapnik return std::pair(width_, height_); } - std::wstring const& get_string() const + UnicodeString const& get_string() const { return text_; } diff --git a/include/mapnik/unicode.hpp b/include/mapnik/unicode.hpp index 70f2b6a4b..7ed17a5c6 100644 --- a/include/mapnik/unicode.hpp +++ b/include/mapnik/unicode.hpp @@ -24,9 +24,8 @@ #ifndef UNICODE_HPP #define UNICODE_HPP -extern "C" { -#include -} +#include +#include #include @@ -35,10 +34,11 @@ namespace mapnik { { public: explicit transcoder (std::string const& encoding); - std::wstring transcode(std::string const& input) const; + UnicodeString transcode(const char* data) const; ~transcoder(); private: - iconv_t desc_; + bool ok_; + UConverter * conv_; }; } diff --git a/include/mapnik/value.hpp b/include/mapnik/value.hpp index e5d9f669b..b4939a94d 100644 --- a/include/mapnik/value.hpp +++ b/include/mapnik/value.hpp @@ -33,10 +33,12 @@ #include #include #include +// uci +#include namespace mapnik { - typedef boost::variant value_base; + typedef boost::variant value_base; namespace impl { struct equals @@ -64,8 +66,8 @@ namespace mapnik { return lhs == rhs; } - bool operator() (std::wstring const& lhs, - std::wstring const& rhs) const + bool operator() (UnicodeString const& lhs, + UnicodeString const& rhs) const { return lhs == rhs; } @@ -96,7 +98,7 @@ namespace mapnik { return lhs > rhs; } - bool operator() (std::wstring const& lhs, std::wstring const& rhs) const + bool operator() (UnicodeString const& lhs, UnicodeString const& rhs) const { return lhs > rhs; } @@ -127,7 +129,7 @@ namespace mapnik { return lhs >= rhs; } - bool operator() (std::wstring const& lhs, std::wstring const& rhs ) const + bool operator() (UnicodeString const& lhs, UnicodeString const& rhs ) const { return lhs >= rhs; } @@ -158,8 +160,8 @@ namespace mapnik { return lhs < rhs; } - bool operator()( std::wstring const& lhs, - std::wstring const& rhs ) const + bool operator()( UnicodeString const& lhs, + UnicodeString const& rhs ) const { return lhs < rhs; } @@ -191,8 +193,8 @@ namespace mapnik { } template - bool operator()( std::wstring const& lhs, - std::wstring const& rhs ) const + bool operator()( UnicodeString const& lhs, + UnicodeString const& rhs ) const { return lhs <= rhs; } @@ -213,8 +215,8 @@ namespace mapnik { return lhs + rhs ; } - value_type operator() (std::wstring const& lhs , - std::wstring const& rhs ) const + value_type operator() (UnicodeString const& lhs , + UnicodeString const& rhs ) const { return lhs + rhs; } @@ -245,8 +247,8 @@ namespace mapnik { return lhs - rhs ; } - value_type operator() (std::wstring const& lhs, - std::wstring const& ) const + value_type operator() (UnicodeString const& lhs, + UnicodeString const& ) const { return lhs; } @@ -277,8 +279,8 @@ namespace mapnik { return lhs * rhs; } - value_type operator() (std::wstring const& lhs, - std::wstring const& ) const + value_type operator() (UnicodeString const& lhs, + UnicodeString const& ) const { return lhs; } @@ -310,8 +312,8 @@ namespace mapnik { return lhs / rhs; } - value_type operator() (std::wstring const& lhs, - std::wstring const&) const + value_type operator() (UnicodeString const& lhs, + UnicodeString const&) const { return lhs; } @@ -338,27 +340,28 @@ namespace mapnik { return ss.str(); } // specializations - std::string operator() (std::wstring const& val) const + std::string operator() (UnicodeString const& val) const { - std::stringstream ss; - std::wstring::const_iterator pos = val.begin(); - ss << std::hex ; - for (;pos!=val.end();++pos) - { - wchar_t c = *pos; - if (c < 0x80) - { - ss << char(c); - } - else - { - ss << "\\x"; - unsigned c0 = (c >> 8) & 0xff; - if (c0) ss << c0; - ss << (c & 0xff); - } - } - return ss.str(); + //std::stringstream ss; + //std::wstring::const_iterator pos = val.begin(); + //ss << std::hex ; + //for (;pos!=val.end();++pos) + //{ + // wchar_t c = *pos; + // if (c < 0x80) + // { + // ss << char(c); + // } + // else + // { + // ss << "\\x"; + // unsigned c0 = (c >> 8) & 0xff; + // if (c0) ss << c0; + // ss << (c & 0xff); + // } + //} + //return ss.str(); + return "TODO"; } std::string operator() (double val) const @@ -369,37 +372,38 @@ namespace mapnik { } }; - struct to_unicode : public boost::static_visitor + struct to_unicode : public boost::static_visitor { template - std::wstring operator() (T val) const + UnicodeString operator() (T val) const { - std::basic_ostringstream out; + std::basic_ostringstream out; out << val; - return out.str(); + return UnicodeString(out.str().c_str()); } // specializations - std::wstring const& operator() (std::wstring const& val) const + UnicodeString const& operator() (UnicodeString const& val) const { return val; } - std::wstring operator() (double val) const + UnicodeString operator() (double val) const { - std::basic_ostringstream out; + std::basic_ostringstream out; out << std::setprecision(16) << val; - return out.str(); + return UnicodeString(out.str().c_str()); } }; struct to_expression_string : public boost::static_visitor { - std::string operator() (std::wstring const& val) const + std::string operator() (UnicodeString const& val) const { + /* std::stringstream ss; - std::wstring::const_iterator pos = val.begin(); + UnicodeString::const_iterator pos = val.begin(); ss << std::hex ; for (;pos!=val.end();++pos) { @@ -417,6 +421,8 @@ namespace mapnik { } } return "\'" + ss.str() + "\'"; + */ + return "TODO"; } template @@ -496,7 +502,7 @@ namespace mapnik { return boost::apply_visitor(impl::to_string(),base_); } - std::wstring to_unicode() const + UnicodeString to_unicode() const { return boost::apply_visitor(impl::to_unicode(),base_); } diff --git a/plugins/input/gdal/SConscript b/plugins/input/gdal/SConscript index 9888fc3b4..e063a2479 100644 --- a/plugins/input/gdal/SConscript +++ b/plugins/input/gdal/SConscript @@ -34,8 +34,8 @@ gdal_src = Split( libraries = ['gdal' ] if env['PLATFORM'] == 'Darwin': libraries.append('mapnik') - libraries.append('iconv') - if env['BIDI'] : libraries.append('fribidi') + libraries.append('icuuc') + libraries.append('icudata') gdal_inputdriver = env.SharedLibrary('gdal', source=gdal_src, SHLIBPREFIX='', SHLIBSUFFIX='.input', LIBS=libraries) diff --git a/plugins/input/postgis/SConscript b/plugins/input/postgis/SConscript index be4bc3426..a0d1967c0 100644 --- a/plugins/input/postgis/SConscript +++ b/plugins/input/postgis/SConscript @@ -35,8 +35,8 @@ libraries = ['pq'] if env['PLATFORM'] == 'Darwin': libraries.append('mapnik') - libraries.append('iconv') - if env['BIDI'] : libraries.append('fribidi') + libraries.append('icuuc') + libraries.append('icudata') if env['THREADING'] == 'multi': libraries.append('boost_thread%s-mt' % env['BOOST_APPEND']) diff --git a/plugins/input/postgis/postgisfs.cpp b/plugins/input/postgis/postgisfs.cpp index d25c137d6..d6a88db7e 100644 --- a/plugins/input/postgis/postgisfs.cpp +++ b/plugins/input/postgis/postgisfs.cpp @@ -132,10 +132,11 @@ feature_ptr postgis_featureset::next() } else if (oid==25 || oid==1042 || oid==1043) // text or bpchar or varchar { - std::string str(buf); - trim(str); - std::wstring wstr = tr_->transcode(str); - boost::put(*feature,name,wstr); + //std::string str(buf); + //trim(str); + //std::wstring wstr = tr_->transcode(str); + UnicodeString ustr = tr_->transcode(buf); + boost::put(*feature,name,ustr); } else if (oid == 1700) // numeric { diff --git a/plugins/input/raster/SConscript b/plugins/input/raster/SConscript index edfe47f80..3143a12e4 100644 --- a/plugins/input/raster/SConscript +++ b/plugins/input/raster/SConscript @@ -35,6 +35,8 @@ raster_src = Split( libraries = [] if env['PLATFORM'] == 'Darwin': libraries.append('mapnik') + libraries.append('icuuc') + libraries.append('icudata') raster_inputdriver = env.SharedLibrary('raster', source=raster_src, SHLIBPREFIX='', SHLIBSUFFIX='.input', LIBS=libraries) diff --git a/plugins/input/shape/SConscript b/plugins/input/shape/SConscript index 68f924d28..005201eef 100644 --- a/plugins/input/shape/SConscript +++ b/plugins/input/shape/SConscript @@ -1,4 +1,4 @@ -# + # This file is part of Mapnik (c++ mapping toolkit) # # Copyright (C) 2006 Artem Pavlenko, Jean-Francois Doyon @@ -46,9 +46,8 @@ else: if env['PLATFORM'] == 'Darwin': libraries.append('mapnik') - libraries.append('iconv') - if env['BIDI'] : libraries.append('fribidi') - + libraries.append('icuuc') + libraries.append('icudata') shape_inputdriver = env.SharedLibrary('shape', SHLIBSUFFIX='.input', source=shape_src, SHLIBPREFIX='', LIBS = libraries) diff --git a/plugins/input/shape/dbffile.cpp b/plugins/input/shape/dbffile.cpp index 98e126099..dbaa4080b 100644 --- a/plugins/input/shape/dbffile.cpp +++ b/plugins/input/shape/dbffile.cpp @@ -113,8 +113,9 @@ void dbf_file::add_attribute(int col, mapnik::transcoder const& tr, Feature cons if (col>=0 && col path_type; - std::wstring text = feature[sym.get_name()].to_unicode(); + UnicodeString text = feature[sym.get_name()].to_unicode(); boost::shared_ptr const& data = sym.get_image(); if (text.length() > 0 && data) { @@ -666,7 +666,7 @@ namespace mapnik { typedef coord_transform2 path_type; - std::wstring text = feature[sym.get_name()].to_unicode(); + UnicodeString text = feature[sym.get_name()].to_unicode(); if ( text.length() > 0 ) { Color const& fill = sym.get_fill(); diff --git a/src/unicode.cpp b/src/unicode.cpp index 592203d62..bfaea9e8c 100644 --- a/src/unicode.cpp +++ b/src/unicode.cpp @@ -76,7 +76,7 @@ namespace mapnik { } #endif - +/* inline std::wstring to_unicode(std::string const& text) { std::wstring out; @@ -159,22 +159,39 @@ namespace mapnik { return out; } - +*/ transcoder::transcoder (std::string const& encoding) + : ok_(false), + conv_(0) { #ifdef MAPNIK_DEBUG std::cerr << "ENCODING = " << encoding << "\n"; #endif -#ifndef WORDS_BIGENDIAN - desc_ = iconv_open("UCS-4LE",encoding.c_str()); -#else - desc_ = iconv_open("UCS-4BE",encoding.c_str()); -#endif +//#ifndef WORDS_BIGENDIAN + // desc_ = iconv_open("UCS-4LE",encoding.c_str()); +//#else + // desc_ = iconv_open("UCS-4BE",encoding.c_str()); +//#endif + + UErrorCode err = U_ZERO_ERROR; + conv_ = ucnv_open(encoding.c_str(),&err); + if (U_SUCCESS(err)) ok_ = true; + // TODO } - std::wstring transcoder::transcode(std::string const& input) const + UnicodeString transcoder::transcode(const char* data) const { + + UErrorCode err = U_ZERO_ERROR; + + UnicodeString ustr(data,-1,conv_,err); + if (ustr.isBogus()) + { + ustr.remove(); + } + return ustr; +/* if (desc_ == iconv_t(-1)) return to_unicode(input); size_t inleft = input.size(); std::wstring output(inleft,0); @@ -196,10 +213,13 @@ namespace mapnik { } #endif return output; +*/ } transcoder::~transcoder() { - if (desc_ != iconv_t(-1)) iconv_close(desc_); + // if (desc_ != iconv_t(-1)) iconv_close(desc_); + if (conv_) + ucnv_close(conv_); } }