diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/extension/internal/Makefile_insert | 4 | ||||
| -rw-r--r-- | src/extension/internal/emf-inout.cpp | 184 | ||||
| -rw-r--r-- | src/extension/internal/text_reassemble.c | 1948 | ||||
| -rw-r--r-- | src/extension/internal/text_reassemble.h | 248 | ||||
| -rw-r--r-- | src/extension/internal/uemf.c | 661 | ||||
| -rw-r--r-- | src/extension/internal/uemf.h | 20 | ||||
| -rw-r--r-- | src/extension/internal/uemf_utf.c | 455 | ||||
| -rw-r--r-- | src/extension/internal/uemf_utf.h | 49 |
8 files changed, 2945 insertions, 624 deletions
diff --git a/src/extension/internal/Makefile_insert b/src/extension/internal/Makefile_insert index a13c8a6ae..12556fbcc 100644 --- a/src/extension/internal/Makefile_insert +++ b/src/extension/internal/Makefile_insert @@ -150,8 +150,12 @@ ink_common_sources += \ extension/internal/filter/filter-file.cpp \ extension/internal/filter/filter.cpp \ extension/internal/filter/filter.h \ + extension/internal/text_reassemble.c \ + extension/internal/text_reassemble.h \ extension/internal/uemf.c \ extension/internal/uemf.h \ + extension/internal/uemf_utf.c \ + extension/internal/uemf_utf.h \ extension/internal/uemf_endian.c \ extension/internal/uemf_endian.h \ extension/internal/emf-print.h \ diff --git a/src/extension/internal/emf-inout.cpp b/src/extension/internal/emf-inout.cpp index ee593b62e..06a64f875 100644 --- a/src/extension/internal/emf-inout.cpp +++ b/src/extension/internal/emf-inout.cpp @@ -43,10 +43,15 @@ #include "document.h" #include "libunicode-convert/unicode-convert.h" +#include <png.h> //This must precede text_reassemble.h or it blows up in pngconf.h when compiling +#include <stdio.h> +#include <stdlib.h> +#include <stdint.h> #include "emf-print.h" #include "emf-inout.h" #include "uemf.h" +#include "text_reassemble.h" #define PRINT_EMF "org.inkscape.print.emf" @@ -73,12 +78,14 @@ which was based on: http://stackoverflow.com/questions/1821806/how-to-encode-png-to-buffer-using-libpng gcc -Wall -o testpng testpng.c -lpng -*/ + +Originally here, but moved up #include <png.h> #include <stdio.h> #include <stdlib.h> #include <stdint.h> +*/ /* A coloured pixel. */ @@ -409,6 +416,7 @@ typedef struct emf_callback_data { // both of these end up in <defs> under the names shown here. These structures allow duplicates to be avoided. EMF_STRINGS hatches; // hold pattern names, all like EMFhatch#_$$$$$$ where # is the EMF hatch code and $$$$$$ is the color EMF_STRINGS images; // hold images, all like Image#, where # is the slot the image lives. + TR_INFO *tri; // Text Reassembly data structure int n_obj; @@ -1507,11 +1515,29 @@ void common_image_extraction(PEMF_CALLBACK_DATA d, void *pEmr, //THis was a callback, just build it into a normal function int myEnhMetaFileProc(char *contents, unsigned int length, PEMF_CALLBACK_DATA d) { - uint32_t off=0; - uint32_t emr_mask; - int OK =1; + uint32_t off=0; + uint32_t emr_mask; + int OK =1; PU_ENHMETARECORD lpEMFR; - + TCHUNK_SPECS tsp; + + /* initialize the tsp for text reassembly */ + tsp.string = NULL; + tsp.ori = 0.0; /* degrees */ + tsp.fs = 12.0; /* font size */ + tsp.x = 0.0; + tsp.y = 0.0; + tsp.boff = 0.0; /* offset to baseline from LL corner of bounding rectangle, changes with fs and taln*/ + tsp.vadvance = 0.0; /* meaningful only when a complex contains two or more lines */ + tsp.taln = ALILEFT + ALIBASE; + tsp.ldir = LDIR_LR; + tsp.color = 0; /* RGBA Black */ + tsp.italics = 0; + tsp.weight = 80; + tsp.condensed = 100; + tsp.co = 0; + tsp.fi_idx = -1; /* set to an invalid */ + while(OK){ if(off>=length)return(0); //normally should exit from while after EMREOF sets OK to false. @@ -1528,7 +1554,18 @@ int myEnhMetaFileProc(char *contents, unsigned int length, PEMF_CALLBACK_DATA d) emr_mask = emr_properties(lpEMFR->iType); if(emr_mask == U_EMR_INVALID){ throw "Inkscape fatal memory allocation error - cannot continue"; } -// std::cout << "BEFORE DRAW logic d->mask: " << std::hex << d->mask << " emr_mask: " << emr_mask << std::dec << std::endl; +/* Uncomment the following to track down text problems */ +//std::cout << "tri->dirty:"<< d->tri->dirty << " emr_mask: " << std::hex << emr_mask << std::dec << std::endl; + if ( (emr_mask != 0xFFFFFFFF) && (emr_mask & U_DRAW_TEXT) && d->tri->dirty){ // next record is valid type and forces pending text to be drawn immediately + TR_layout_analyze(d->tri); + TR_layout_2_svg(d->tri); + SVGOStringStream ts; + ts << d->tri->out; + *(d->outsvg) += ts.str().c_str(); + d->tri = trinfo_clear(d->tri); + } + +//std::cout << "BEFORE DRAW logic d->mask: " << std::hex << d->mask << " emr_mask: " << emr_mask << std::dec << std::endl; /* std::cout << "BEFORE DRAW" << " test0 " << ( d->mask & U_DRAW_VISIBLE) @@ -1539,6 +1576,7 @@ std::cout << "BEFORE DRAW" << " test5 " << ((d->mask & U_DRAW_ONLYTO) && !(emr_mask & U_DRAW_ONLYTO) ) << std::endl; */ + if ( (emr_mask != 0xFFFFFFFF) && // next record is valid type (d->mask & U_DRAW_VISIBLE) && // This record is drawable ( (d->mask & U_DRAW_FORCE) || // This draw is forced by STROKE/FILL/STROKEANDFILL PATH @@ -1619,6 +1657,7 @@ std::cout << "BEFORE DRAW" (double)(pEmr->szlMillimeters.cx + pEmr->szlMillimeters.cy)/ (double)( pEmr->szlDevice.cx + pEmr->szlDevice.cy); } + trinfo_load_qe(d->tri, d->D2PscaleX); /* quantization error that will affect text positions */ /* Adobe Illustrator files set mapmode to MM_ANISOTROPIC and somehow or other this converts the rclFrame values from MM_HIMETRIC to MM_HIENGLISH, with another factor of 3 thrown @@ -2791,17 +2830,7 @@ std::cout << "BEFORE DRAW" double x = pix_to_x_point(d, x1, y1); double y = pix_to_y_point(d, x1, y1); - double dfact; - if (d->dc[d->level].textAlign & U_TA_BASEBIT){ dfact = 0.00; } // alignments 0x10 to U_TA_BASELINE 0x18 - else if(d->dc[d->level].textAlign & U_TA_BOTTOM){ dfact = -0.35; } // alignments U_TA_BOTTOM 0x08 to 0x0E, factor is approximate - else { dfact = 0.85; } // alignments U_TA_TOP 0x00 to 0x07, factor is approximate - if (d->dc[d->level].style.baseline_shift.value) { - x += dfact * std::sin(d->dc[d->level].style.baseline_shift.value*M_PI/180.0)*fabs(d->dc[d->level].style.font_size.computed); - y += dfact * std::cos(d->dc[d->level].style.baseline_shift.value*M_PI/180.0)*fabs(d->dc[d->level].style.font_size.computed); - } - else { - y += dfact * fabs(d->dc[d->level].style.font_size.computed); - } + /* Rotation issues are handled entirely in libTERE now */ uint32_t *dup_wt = NULL; @@ -2846,71 +2875,67 @@ std::cout << "BEFORE DRAW" } if (ansi_text) { -// gchar *p = ansi_text; -// while (*p) { -// if (*p < 32 || *p >= 127) { -// g_free(ansi_text); -// ansi_text = g_strdup(""); -// break; -// } -// p++; -// } SVGOStringStream ts; gchar *escaped_text = g_markup_escape_text(ansi_text, -1); -// float text_rgb[3]; -// sp_color_get_rgb_floatv( &(d->dc[d->level].style.fill.value.color), text_rgb ); - -// if (!d->dc[d->level].textColorSet) { -// d->dc[d->level].textColor = RGB(SP_COLOR_F_TO_U(text_rgb[0]), -// SP_COLOR_F_TO_U(text_rgb[1]), -// SP_COLOR_F_TO_U(text_rgb[2])); -// } - - char tmp[128]; - snprintf(tmp, 127, - "fill:#%02x%02x%02x;", - U_RGBAGetR(d->dc[d->level].textColor), - U_RGBAGetG(d->dc[d->level].textColor), - U_RGBAGetB(d->dc[d->level].textColor)); - - bool i = (d->dc[d->level].style.font_style.value == SP_CSS_FONT_STYLE_ITALIC); - //bool o = (d->dc[d->level].style.font_style.value == SP_CSS_FONT_STYLE_OBLIQUE); - bool b = (d->dc[d->level].style.font_weight.value == SP_CSS_FONT_WEIGHT_BOLD) || - (d->dc[d->level].style.font_weight.value >= SP_CSS_FONT_WEIGHT_500 && d->dc[d->level].style.font_weight.value <= SP_CSS_FONT_WEIGHT_900); + tsp.x = x*0.8; // TERE expects sizes in points. + tsp.y = y*0.8; + memcpy(&tsp.color, &d->dc[d->level].textColor, sizeof(uint32_t)); //It is already an RGBA binary value, but compiler is picky about types + switch(d->dc[d->level].style.font_style.value){ + case SP_CSS_FONT_STYLE_OBLIQUE: + tsp.italics = FC_SLANT_OBLIQUE; break; + case SP_CSS_FONT_STYLE_ITALIC: + tsp.italics = FC_SLANT_ITALIC; break; + default: + case SP_CSS_FONT_STYLE_NORMAL: + tsp.italics = FC_SLANT_ROMAN; break; + } + switch(d->dc[d->level].style.font_weight.value){ + case SP_CSS_FONT_WEIGHT_100: tsp.weight = FC_WEIGHT_THIN ; break; + case SP_CSS_FONT_WEIGHT_200: tsp.weight = FC_WEIGHT_EXTRALIGHT ; break; + case SP_CSS_FONT_WEIGHT_300: tsp.weight = FC_WEIGHT_LIGHT ; break; + case SP_CSS_FONT_WEIGHT_400: tsp.weight = FC_WEIGHT_NORMAL ; break; + case SP_CSS_FONT_WEIGHT_500: tsp.weight = FC_WEIGHT_MEDIUM ; break; + case SP_CSS_FONT_WEIGHT_600: tsp.weight = FC_WEIGHT_SEMIBOLD ; break; + case SP_CSS_FONT_WEIGHT_700: tsp.weight = FC_WEIGHT_BOLD ; break; + case SP_CSS_FONT_WEIGHT_800: tsp.weight = FC_WEIGHT_EXTRABOLD ; break; + case SP_CSS_FONT_WEIGHT_900: tsp.weight = FC_WEIGHT_HEAVY ; break; + case SP_CSS_FONT_WEIGHT_NORMAL: tsp.weight = FC_WEIGHT_NORMAL ; break; + case SP_CSS_FONT_WEIGHT_BOLD: tsp.weight = FC_WEIGHT_BOLD ; break; + case SP_CSS_FONT_WEIGHT_LIGHTER: tsp.weight = FC_WEIGHT_EXTRALIGHT ; break; + case SP_CSS_FONT_WEIGHT_BOLDER: tsp.weight = FC_WEIGHT_EXTRABOLD ; break; + default: tsp.weight = FC_WEIGHT_NORMAL ; break; + } + // EMF textalignment is a bit strange: 0x6 is center, 0x2 is right, 0x0 is left, the value 0x4 is also drawn left - int lcr = ((d->dc[d->level].textAlign & U_TA_CENTER) == U_TA_CENTER) ? 2 : ((d->dc[d->level].textAlign & U_TA_CENTER) == U_TA_LEFT) ? 0 : 1; - - ts << "<text\n"; - ts << " xml:space=\"preserve\"\n"; - ts << " x=\"" << x << "\"\n"; - ts << " y=\"" << y << "\"\n"; - if (d->dc[d->level].style.baseline_shift.value) { - ts << " transform=\"" - << "rotate(-" << d->dc[d->level].style.baseline_shift.value - << " " << x << " " << y << ")" - << "\"\n"; + tsp.taln = ((d->dc[d->level].textAlign & U_TA_CENTER) == U_TA_CENTER) ? ALICENTER : + (((d->dc[d->level].textAlign & U_TA_CENTER) == U_TA_LEFT) ? ALILEFT : + ALIRIGHT); + tsp.taln |= ((d->dc[d->level].textAlign & U_TA_BASEBIT) ? ALIBASE : + ((d->dc[d->level].textAlign & U_TA_BOTTOM) ? ALIBOT : + ALITOP)); + tsp.ldir = (d->dc[d->level].textAlign & U_TA_RTLREADING ? LDIR_RL : LDIR_LR); // language direction + tsp.condensed = FC_WIDTH_NORMAL; // Not implemented well in libTERE (yet) + tsp.ori = d->dc[d->level].style.baseline_shift.value; // For now orientation is always the same as escapement + tsp.string = (uint8_t *) U_strdup(escaped_text); // this will be free'd much later at a trinfo_clear(). + tsp.fs = d->dc[d->level].style.font_size.computed * 0.8; // Font size in points + (void) trinfo_load_fontname(d->tri, (uint8_t *)d->dc[d->level].font_name, &tsp); + // when font name includes narrow it may not be set to "condensed". Narrow fonts do not work well anyway though + // as the metrics from fontconfig may not match, or the font may not be present. + if(0<= TR_findcasesub(d->dc[d->level].font_name, (char *) "Narrow")){ tsp.co=1; } + else { tsp.co=0; } + + int status = trinfo_load_textrec(d->tri, &tsp, tsp.ori,TR_EMFBOT); // ori is actually escapement + if(status==-1){ // change of escapement, emit what we have and reset + TR_layout_analyze(d->tri); + TR_layout_2_svg(d->tri); + ts << d->tri->out; + *(d->outsvg) += ts.str().c_str(); + d->tri = trinfo_clear(d->tri); + (void) trinfo_load_textrec(d->tri, &tsp, tsp.ori,TR_EMFBOT); // ignore return status, it must work } - ts << "><tspan sodipodi:role=\"line\""; - ts << " x=\"" << x << "\"\n"; - ts << " y=\"" << y << "\"\n"; - ts << " style=\"" - << "font-size:" << fabs(d->dc[d->level].style.font_size.computed) << "px;" - << tmp - << "font-style:" << (i ? "italic" : "normal") << ";" - << "font-weight:" << (b ? "bold" : "normal") << ";" - << "text-align:" << (lcr==2 ? "center" : lcr==1 ? "end" : "start") << ";" - << "text-anchor:" << (lcr==2 ? "middle" : lcr==1 ? "end" : "start") << ";" - << "font-family:" << d->dc[d->level].font_name << ";" - << "\"\n"; - ts << " >"; - ts << escaped_text; - ts << "</tspan>"; - ts << "</text>\n"; - - *(d->outsvg) += ts.str().c_str(); g_free(escaped_text); free(ansi_text); @@ -3248,7 +3273,12 @@ Emf::open( Inkscape::Extension::Input * /*mod*/, const gchar *uri ) if(emf_readdata(uri, &contents, &length))return(NULL); d.pDesc = NULL; - + + // set up the text reassembly system + if(!(d.tri = trinfo_init(NULL)))return(NULL); + (void) trinfo_load_ft_opts(d.tri, 1, + FT_LOAD_NO_SCALE | FT_LOAD_NO_HINTING | FT_LOAD_NO_BITMAP, + FT_KERNING_UNSCALED); (void) myEnhMetaFileProc(contents,length, &d); free(contents); @@ -3282,6 +3312,8 @@ Emf::open( Inkscape::Extension::Input * /*mod*/, const gchar *uri ) if(d.dc[i].font_name)free(d.dc[i].font_name); } + d.tri = trinfo_release_except_FC(d.tri); + return doc; } diff --git a/src/extension/internal/text_reassemble.c b/src/extension/internal/text_reassemble.c new file mode 100644 index 000000000..623388373 --- /dev/null +++ b/src/extension/internal/text_reassemble.c @@ -0,0 +1,1948 @@ +/* text_reassemble.c +version 0.0.3 2012-12-07 +Copyright 2012, Caltech and David Mathog + +Reassemble formatted text from a series of text/position/font records. + +Method: + 1. For all ordered text objects which are sequential and share the same esc. + 2. For the first only pull x,y,esc and save, these define origin and rotation. + 3. Save the text object. + 4. Phase I: For all saved text objects construct lines. + 5. Check for allowed overlaps on sequential saved text object bounding rectangles. + 6 If found merge second with first, check next one. + 7. If not found, start a new complex (line). + 8. Phase II; for all lines construct paragraphs. + 9. Check alignment and line spacing of preceding line with current line. + 10. if alignment is the same, and line spacing is compatible merge current line into + current paragraph. Reaverage line spacing over all lines in paragraph. Check next one. + 11. If alignment does not match start a new paragraph. + (Test program) + 12. Over all phase II paragraphs + 13. Over all phase I lines in each paragraph. + 14. Over all text objects in each line. + Emit SVG correspnding to this construct to a file dump.svg. + 15. Clean up. + (General program) + Like for the Test program, but final represenation may not be SVG. + +During the accept stage (1) it uses fontconfig/freetype data to store up font faces and to +work out the extent of each substring. This code assumes all text goes L->R, if it goes the other way +the same groupings would occur, just mirror imaged. + +At step 5 it calculates overlapping bounding boxes == formatted strings. The bounding boxes are extended +out by 1 character laterally and .49 character vertically. If it was able to figure out justification +that is returned along with the number of formatted strings. + +The caller then retrieves the x,y,xe,ye,string,format,font data and uses it to construct a formatted +string in whatever format the caller happens to be using. For Inskcape this would be SVG. + +Finally the caller cleans up, releasing all of the stored memory. FreeType memory is always all released. +FontConfig memory is released except for, optionally, not calling FcFini(), which would likely cause +problems for any program that was using FontConfig elsewhere. + +NOTE ON COORDINATES: x is positive to the right, y is positive down. So (0,0) is the upper left corner, and the +lower left corner of a rectangle has a LARGER Y coordinate than the upper left. Ie, LL=(10,10) UR=(30,5) is typical. + + +Compilation of test program: +On Windows use: + + gcc -Wall -DWIN32 -DTEST \ + -I. -I/c/progs/devlibs32/include -I/c/progs/devlibs32/include/freetype2\ + -o text_reassemble text_reassemble.c uemf_utf.c \ + -lfreetype6 -lfontconfig-1 -lm -L/c/progs/devlibs32/bin + +On Linux use: + + gcc -Wall -DTEST -I. -I/usr/include/freetype2 -o text_reassemble text_reassemble.c uemf_utf.c -lfreetype -lfontconfig -lm + +Compilation of object file only (Windows): + + gcc -Wall -DWIN32 -c \ + -I. -I/c/progs/devlibs32/include -I/c/progs/devlibs32/include/freetype2\ + text_reassemble.c + +Compilation of object file only (Linux): + gcc -Wall -c -I. -I/usr/include/freetype2 text_reassemble.c + + +Optional compiler switches for development: + -DDBG_TR_PARA draw bounding rectangles for paragraphs in SVG output + -DDBG_TR_INPUT draw input text and their bounding rectangles in SVG output + -DTEST build the test program + -DDBG_LOOP force the test program to cycle 5 times. Useful for finding + memory leaks. Ouput file is overwritten each time. + + +*/ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "text_reassemble.h" +#include "uemf_utf.h" /* For a couple of text functions. Exact copy from libUEMF. */ + +/* end of functions from libUEMF */ + +/* Utility function, find a (sub)string in a caseinvariant manner, used for locating "Narrow" in font name. + Returns -1 if no match, else returns the position (numbered from 0) of the first character of the match. +*/ +int TR_findcasesub(char *string, char *sub){ + int i,j; + int match=0; + for(i=0; string[i]; i++){ + for(match=1,j=0; sub[j] && string[i+j]; j++){ + if(toupper(sub[j]) != toupper(string[i+j])){ + match=0; + break; + } + } + if(match && !sub[j])break; /* matched over the entire substring */ + } + return((match ? i : -1)); +} + +/** +Get the advance for the 32 bit character, returned value has units of 1/64th of a Point. + When load_flags == FT_LOAD_NO_SCALE is used the internal advance is in 1/64th of a point. + This does NOT stop scaling on kerning values! + When load_flags == FT_LOAD_TARGET_NORMAL is used the internal advance also seem to be in 1/64th of a point. The scale + factor seems to be (Font Size in points)*(DPI)/(32.0 pnts)*(72 dpi). +kern_mode, One of FT_KERNING_DEFAULT, FT_KERNING_UNFITTED, FT_KERNING_UNSCALED +wc is the current character +pc is the previous character, 0 if there was not one + If ymin,ymax are passed in, then if the character's limits decrease/increase that value, it is modified (for founding string bounding box) +On error return -1. +*/ +int TR_getadvance(FNT_SPECS *fsp, uint32_t wc, uint32_t pc, int load_flags, int kern_mode, int *ymin, int *ymax){ + FT_Glyph glyph; + int glyph_index; + int advance=-1; + FT_BBox bbox; + + glyph_index = FT_Get_Char_Index( fsp->face, wc); + if (!FT_Load_Glyph( fsp->face, glyph_index, load_flags )){ + if ( !FT_Get_Glyph( fsp->face->glyph, &glyph ) ) { + advance = fsp->face->glyph->advance.x; + FT_Glyph_Get_CBox( glyph, FT_GLYPH_BBOX_UNSCALED, &bbox ); + if(ymin && (bbox.yMin < *ymin))*ymin=bbox.yMin; + if(ymax && (bbox.yMax > *ymax))*ymax=bbox.yMax; + if(pc)advance += TR_getkern2(fsp, wc, pc, kern_mode); + FT_Done_Glyph(glyph); + } + } + return(advance); +} + +/** +Get the kerning for a pair of 32 bit characters, returned value has units of 1/64th of a Point. + When load_flags == FT_LOAD_NO_SCALE is used the internal advance is in 1/64th of a point. + This does NOT stop scaling on kerning values! + When load_flags == FT_LOAD_TARGET_NORMAL is used the internal advance also seem to be in 1/64th of a point. The scale + factor seems to be (Font Size in points)*(DPI)/(32.0 pnts)*(72 dpi). +kern_mode, One of FT_KERNING_DEFAULT, FT_KERNING_UNFITTED, FT_KERNING_UNSCALED +wc is the current character +pc is the previous character, 0 if there was not one +Returns 0 on error or if the kerning is 0. +*/ +int TR_getkern2(FNT_SPECS *fsp, uint32_t wc, uint32_t pc, int kern_mode){ + int this_glyph_index; + int prev_glyph_index; + int kern=0; + FT_Vector akerning; + + this_glyph_index = FT_Get_Char_Index( fsp->face, wc); + prev_glyph_index = FT_Get_Char_Index( fsp->face, pc); + if(!FT_Get_Kerning( fsp->face, + prev_glyph_index, + this_glyph_index, + kern_mode, + &akerning )){ + kern = akerning.x; /* Is sign correct? */ + } + return(kern); +} + +/** +Get the kerning for a pair of 32 bit characters, where one is the last charcter in the previous text block, +and the other is the first in the current text block. + When load_flags == FT_LOAD_NO_SCALE is used the internal advance is in 1/64th of a point. + This does NOT stop scaling on kerning values! + When load_flags == FT_LOAD_TARGET_NORMAL is used the internal advance also seem to be in 1/64th of a point. The scale + factor seems to be (Font Size in points)*(DPI)/(32.0 pnts)*(72 dpi). +kern_mode, One of FT_KERNING_DEFAULT, FT_KERNING_UNFITTED, FT_KERNING_UNSCALED +tsp is current text object +ptsp is the previous text object +wc is the current character +pc is the previous character +Returns 0 on error or if the kerning is 0. +*/ +int TR_kern_gap(FNT_SPECS *fsp, TCHUNK_SPECS *tsp, TCHUNK_SPECS *ptsp, int kern_mode){ + int kern=0; + uint32_t *text32=NULL; + uint32_t *ptxt32=NULL; + size_t tlen,plen; + while(ptsp && tsp){ + text32 = U_Utf8ToUtf32le((char *) tsp->string, 0, &tlen); + if(!text32){ // LATIN1 encoded >128 are generally not valid UTF, so the first will fail + text32 = U_Latin1ToUtf32le((char *) tsp->string,0, &tlen); + if(!text32)break; + } + ptxt32 = U_Utf8ToUtf32le((char *) ptsp->string,0,&plen); + if(!ptxt32){ // LATIN1 encoded >128 are generally not valid UTF, so the first will fail + ptxt32 = U_Latin1ToUtf32le((char *) ptsp->string,0, &plen); + if(!ptxt32)break; + } + kern = TR_getkern2(fsp, *text32, ptxt32[plen-1], kern_mode); + break; + } + if(text32)free(text32); + if(ptxt32)free(ptxt32); + return(kern); +} + + + + +/* If the complex is a TR_TXT or TR_LINE find its baseline. + If the complex is TR_PARA+* find the baseline of the last line. + If AscMax or DscMax exists find the maximum Ascender/Descender size in this complex. + If there are multiple text elements in a TR_LINE, the baseline is that of the + element that uses the largest font. This will definitely give the wrong + result if that line starts with a super or subscript that is full font size, but + they are usually smaller. + returns 0 if it screws up and cannot figure out the baseline. +*/ +double TR_baseline(TR_INFO *tri, int src, double *ymax, double *ymin){ + double baseline=0; + double tmp; + double yheight; + int last; + int i; + int trec; + FNT_SPECS *fsp; + static int depth=0; + CX_INFO *cxi=tri->cxi; + BR_INFO *bri=tri->bri; + TP_INFO *tpi=tri->tpi; + FT_INFO *fti=tri->fti; + last = cxi->cx[src].kids.used - 1; + switch (cxi->cx[src].type){ + case TR_TEXT: + trec = cxi->cx[src].kids.members[0]; /* for this complex type there is only ever one member */ + baseline = bri->rects[trec].yll - tpi->chunks[trec].boff; + fsp = &(fti->fonts[tpi->chunks[trec].fi_idx]); + yheight = fsp->face->bbox.yMax - fsp->face->bbox.yMin; + if(ymax){ + tmp = tpi->chunks[trec].fs * ((double)fsp->face->bbox.yMax/yheight); + if(*ymax <= tmp)*ymax = tmp; + } + else if(ymin){ + tmp = tpi->chunks[trec].fs * ((double)-fsp->face->bbox.yMin/yheight); /* yMin in face is negative */ + if(*ymin <= tmp)*ymin = tmp; + } + break; + case TR_LINE: + for(i=last;i>=0;i--){ /* here last is the count of */ + trec = cxi->cx[src].kids.members[i]; + fsp = &(fti->fonts[tpi->chunks[trec].fi_idx]); + yheight = fsp->face->bbox.yMax - fsp->face->bbox.yMin; + if(ymax){ + tmp = tpi->chunks[trec].fs * ((double)fsp->face->bbox.yMax/yheight); + if(*ymax <= tmp){ + *ymax = tmp; + baseline = bri->rects[trec].yll - tpi->chunks[trec].boff; + } + } + else if(ymin){ + tmp = tpi->chunks[trec].fs * ((double)-fsp->face->bbox.yMin/yheight); /* yMin in face is negative */ + if(*ymin <= tmp){ + *ymin = tmp; + baseline = bri->rects[trec].yll - tpi->chunks[trec].boff; + } + } + } + break; + case TR_PARA_UJ: + case TR_PARA_LJ: + case TR_PARA_CJ: + case TR_PARA_RJ: + depth++; + trec = cxi->cx[src].kids.members[last]; + baseline = TR_baseline(tri, trec, ymax, ymin); + break; + } + return(baseline); +} + +/* check or set vadvance on growing complex dst with positions of text in + potential TR_LINE/TR_TEXT src. Vadvance is a multiplicative factor like 1.25. + The distance between successive baselines is vadvance * max(font_size), where the maximum + is over all text elements in src. + lines is the index of the first text block that was added, so src - lines can be used + to determine the weight to give to each new vadvance value as it is merged into the + running weighted average. This improves the accuracy of the vertical advance, since + there can be some noise introduced when lines have different maximum font sizes. + Returns 0 on success. + Returns !0 on failure +*/ +int TR_check_set_vadvance(TR_INFO *tri, int src, int lines){ + int status = 0; + CX_INFO *cxi = tri->cxi; + TP_INFO *tpi = tri->tpi; + double ymax = 0.0; + double ymin = 0.0; + double prevbase; + double thisbase; + double weight; + int trec; + double newV; + int dst; + + dst = cxi->used-1; /* complex being grown */ + + prevbase = TR_baseline(tri, dst, NULL, &ymin); + thisbase = TR_baseline(tri, src, &ymax, NULL); + newV = (thisbase - prevbase)/(ymax + ymin); + trec = cxi->cx[dst].kids.members[0]; /* complex whose first text record holds vadvance for this complex */ + trec = cxi->cx[trec].kids.members[0]; /* text record that halds vadvance for this complex */ + if(tpi->chunks[trec].vadvance){ + /* already set on the first text (only place it is stored.) + See if the line to be added is compatible. + All text fields in a complex have the same advance, so just set/check the first one. + vadvance must be within 1% or do not add a new line */ + if(fabs(1.0 - (tpi->chunks[trec].vadvance/newV) > 0.01)){ + status = 1; + } + else { /* recalculate the weighted vadvance */ + weight = (1.0 / (double) (src - lines)); + tpi->chunks[trec].vadvance = tpi->chunks[trec].vadvance*(1.0-weight) + newV*weight; + } + } + else { /* only happens when src = lines + 1*/ + tpi->chunks[trec].vadvance = newV; + } + return(status); +} + + +/* Initialize the ftinfo system. Sets up a freetype library to use in this context. Returns NULL on failure. */ +FT_INFO *ftinfo_init(void){ + FT_INFO *fti = NULL; + if(FcInit()){ + fti = (FT_INFO *)calloc(1,sizeof(FT_INFO)); + if(fti){ + if(!FT_Init_FreeType( &(fti->library))){ + fti->space=0; + fti->used=0; + + if(ftinfo_make_insertable(fti)){ + FT_Done_FreeType(fti->library); + free(fti); + fti=NULL; + } + } + else { + free(fti); + fti=NULL; + } + } + if(!fti)FcFini(); + } + return(fti); +} + +/* verifies that there is space to add one more entry. + 0 on sucess, anything else is an error */ +int ftinfo_make_insertable(FT_INFO *fti){ + int status=0; + if(!fti)return(2); + if(fti->used < fti->space){ + /* already insertable */ + } + else { + fti->space += ALLOCINFO_CHUNK; + if((fti->fonts = (FNT_SPECS *) realloc(fti->fonts, fti->space * sizeof(FNT_SPECS) ))){ + memset(&fti->fonts[fti->used],0,(fti->space - fti->used)*sizeof(FNT_SPECS)); + } + else { + status=1; + } + } + return(status); +} + + +/* Insert an fsp into an fti + 0 on sucess, anything else is an error */ +int ftinfo_insert(FT_INFO *fti, FNT_SPECS *fsp){ + int status=1; + if(!fti)return(2); + if(!fsp)return(3); + if(!(status = ftinfo_make_insertable(fti))){ + memcpy(&(fti->fonts[fti->used]),fsp,sizeof(FNT_SPECS)); + fti->used++; + } + return(status); +} + + + +/* Shut down the ftinfo system. Release all memory. + call like: fi_ptr = ftinfo_release(fi_ptr) +*/ +FT_INFO *ftinfo_release(FT_INFO *fti){ + int i; + if(fti){ + for(i=0;i<fti->used;i++){ + FT_Done_Face(fti->fonts[i].face); /* release memory for face controlled by FreeType */ + free(fti->fonts[i].file); /* release memory holding copies of paths */ + free(fti->fonts[i].fname); /* release memory holding copies of font names */ + FcPatternDestroy(fti->fonts[i].fpat); /* release memory for FontConfit fpats */ + } + free(fti->fonts); + FT_Done_FreeType(fti->library); /* release all other FreeType memory */ + free(fti); + FcFini(); /* shut down FontConfig, release memory, patterns must have already been released or boom! */ + } + return NULL; +} + +/* Clear the ftinfo system. Release all Freetype memory but do NOT shut down Fontconfig. This would + be called in preference to ftinfo_release if some other part of the program needed to continue using + Fontconfig.: fi_ptr = ftinfo_clear(fi_ptr) +*/ +FT_INFO *ftinfo_clear(FT_INFO *fti){ + int i; + if(fti){ + for(i=0;i<fti->used;i++){ + FT_Done_Face(fti->fonts[i].face); /* release memory for face controlled by FreeType */ + free(fti->fonts[i].file); /* release memory holding copies of paths */ + free(fti->fonts[i].fname); /* release memory holding copies of font names */ + FcPatternDestroy(fti->fonts[i].fpat); /* release memory for FontConfit fpats */ + } + free(fti->fonts); + FT_Done_FreeType(fti->library); /* release all other FreeType memory */ + free(fti); + } + return NULL; +} + + +/* verifies that there is space to add one more entry. + 0 on sucess, anything else is an error */ +int csp_make_insertable(CHILD_SPECS *csp){ + int status=0; + if(!csp)return(2); + if(csp->used < csp->space){ + /* already insertable */ + } + else { + csp->space += ALLOCINFO_CHUNK; + if((csp->members = (int *) realloc(csp->members, csp->space * sizeof(int) ))){ + memset(&csp->members[csp->used],0,(csp->space - csp->used)*sizeof(int)); + } + else { + status=1; + } + } + return(status); +} + +/* Add a member (src) to a child spec. 0 on success, anything else is an error */ +int csp_insert(CHILD_SPECS *dst, int src){ + int status=1; + if(!dst)return(2); + if(!(status=csp_make_insertable(dst))){ + dst->members[dst->used]=src; + dst->used++; + } + return(status); +} + +/* Add all the members of child spec src to child spec dst. +0 on success, anything else is an error */ +int csp_merge(CHILD_SPECS *dst, CHILD_SPECS *src){ + int i; + int status=1; + if(!dst)return(2); + if(!src)return(3); + for(i=0;i<src->used;i++){ + status = csp_insert(dst, src->members[i]); + if(status)break; + } + return(status); +} + +/* Shut down the cxinfo system. Release all memory. + call like: (void) csp_release(csp_ptr). +*/ +void csp_release(CHILD_SPECS *csp){ + if(csp){ + free(csp->members); + csp->space = 0; + csp->used = 0; + } +} + + +/* Initialize the cxinfo system. Returns NULL on failure. */ +CX_INFO *cxinfo_init(void){ + CX_INFO *cxi = NULL; + cxi = (CX_INFO *)calloc(1,sizeof(CX_INFO)); + if(cxi){ + if(cxinfo_make_insertable(cxi)){ + free(cxi); + cxi=NULL; + } + } + return(cxi); +} + +/* verifies that there is space to add one more entry. + Creates the structure if it is passed a null pointer. + 0 on sucess, anything else is an error */ +int cxinfo_make_insertable(CX_INFO *cxi){ + int status=0; + if(cxi->used < cxi->space){ + /* already insertable */ + } + else { + cxi->space += ALLOCINFO_CHUNK; + if((cxi->cx = (CX_SPECS *) realloc(cxi->cx, cxi->space * sizeof(CX_SPECS) ))){ + memset(&cxi->cx[cxi->used],0,(cxi->space - cxi->used)*sizeof(CX_SPECS)); + } + else { + status=1; + } + } + return(status); +} + +/* Insert a complex of "type" with one member (src) and that src's associated rectangle (by index). + If type is TR_TEXT src is an index for tpi->chunks[] + If type is TR_LINE src is an index for cxi->kids[] + 0 on sucess, anything else is an error */ +int cxinfo_insert(CX_INFO *cxi, int src, int src_rt_tidx, enum tr_classes type){ + int status=1; + if(!cxi)return(2); + if(!(status=cxinfo_make_insertable(cxi))){ + cxi->cx[cxi->used].rt_cidx = src_rt_tidx; + cxi->cx[cxi->used].type = type; + status = csp_insert(&(cxi->cx[cxi->used].kids), src); + cxi->used++; + } + return(status); +} + +/* Append a a complex "src" of the last complex and change the complex type to "type". + If type is TR_LINE src is an index for tpi->chunks[] + If type is TR_PARA_* src is an index for cxi->kids[], and the incoming complex is a line. + 0 on sucess, anything else is an error */ +int cxinfo_append(CX_INFO *cxi, int src, enum tr_classes type){ + int status=1; + if(!cxi)return(2); + if(!(status=cxinfo_make_insertable(cxi))){ + cxi->cx[cxi->used-1].type = type; + status = csp_insert(&(cxi->cx[cxi->used-1].kids), src); + } + return(status); +} + + +/* Merge a complex dst with N members (N>=1) by adding a second complex src . Change the type to "type" + 0 on sucess, anything else is an error */ +int cxinfo_merge(CX_INFO *cxi, int dst, int src, enum tr_classes type){ + int status =1; + if(!cxi)return(2); + if(dst < 0 || dst >= cxi->used)return(3); + if(src < 0)return(4); + cxi->cx[dst].type = type; + status = csp_merge(&(cxi->cx[dst].kids), &(cxi->cx[src].kids)); + return(status); +} + +/* For debugging purposes,not used in production code. +*/ +void cxinfo_dump(TR_INFO *tri){ + int i,j,k; + CX_INFO *cxi = tri->cxi; + BR_INFO *bri = tri->bri; + TP_INFO *tpi = tri->tpi; + BRECT_SPECS *bsp; + CX_SPECS *csp; + if(cxi){ + printf("cxi space: %d\n",cxi->space); + printf("cxi used: %d\n",cxi->used); + printf("cxi phase1: %d\n",cxi->phase1); + printf("cxi lines: %d\n",cxi->lines); + printf("cxi paras: %d\n",cxi->paras); + + for(i=0;i<cxi->used;i++){ + csp = &(cxi->cx[i]); + bsp = &(bri->rects[csp->rt_cidx]); + printf("cxi cx[%d] type:%d rt_tidx:%d kids_used:%d kids:space:%d\n",i, csp->type, csp->rt_cidx, csp->kids.used, csp->kids.space); + printf("cxi cx[%d] br (LL,UR) (%lf,%lf),(%lf,%lf)\n",i,bsp->xll,bsp->yll,bsp->xur,bsp->yur); + for(j=0;j<csp->kids.used;j++){ + k = csp->kids.members[j]; + bsp = &(bri->rects[k]); + if(csp->type == TR_TEXT || csp->type == TR_LINE){ + printf("cxi cx[%d] member:%d tp_idx:%d rt_tidx:%d br (LL,UR) (%8.3lf,%8.3lf),(%8.3lf,%8.3lf) text:<%s>\n",i, j, k, tpi->chunks[k].rt_tidx, bsp->xll,bsp->yll,bsp->xur,bsp->yur, tpi->chunks[k].string); + } + else { /* TR_PARA_* */ + printf("cxi cx[%d] member:%d cx_idx:%d\n",i, j, k); + } + } + } + } + return; +} + +/* Shut down the cxinfo system. Release all memory. + call like: cxi_ptr = cxinfo_release(cxi_ptr) +*/ +CX_INFO *cxinfo_release(CX_INFO *cxi){ + int i; + if(cxi){ + for(i=0;i<cxi->used;i++){ csp_release(&cxi->cx[i].kids); } + free(cxi->cx); + free(cxi); /* release the overall cxinfo structure */ + } + return NULL; +} + + +/* Initialize the tpinfo system. Returns NULL on failure. */ +TP_INFO *tpinfo_init(void){ + TP_INFO *tpi = NULL; + tpi = (TP_INFO *)calloc(1,sizeof(TP_INFO)); + if(tpi){ + if(tpinfo_make_insertable(tpi)){ + free(tpi); + tpi=NULL; + } + } + return(tpi); +} + + +/* verifies that there is space to add one more entry. + 0 on sucess, anything else is an error */ +int tpinfo_make_insertable(TP_INFO *tpi){ + int status=0; + if(tpi->used >= tpi->space){ + tpi->space += ALLOCINFO_CHUNK; + if((tpi->chunks = (TCHUNK_SPECS *) realloc(tpi->chunks, tpi->space * sizeof(TCHUNK_SPECS) ))){ + memset(&tpi->chunks[tpi->used],0,(tpi->space - tpi->used)*sizeof(TCHUNK_SPECS)); + } + else { + status=1; + } + } + return(status); +} + +/* Insert a TCHUNK_SPEC as a tpi chunk.. + 0 on sucess, anything else is an error */ +int tpinfo_insert(TP_INFO *tpi, TCHUNK_SPECS *tsp){ + int status=1; + if(!tpi)return(2); + if(!tsp)return(3); + if(!(status = tpinfo_make_insertable(tpi))){ + memcpy(&(tpi->chunks[tpi->used]),tsp,sizeof(TCHUNK_SPECS)); + if(tsp->co)tpi->chunks[tpi->used].condensed = 75; /* Narrow was set in the font name */ + tpi->used++; + } + return(status); +} + +/* Shut down the tpinfo system. Release all memory. + call like: tpi_ptr = tpinfo_release(tpi_ptr) +*/ +TP_INFO *tpinfo_release(TP_INFO *tpi){ + int i; + if(tpi){ + for(i=0;i<tpi->used;i++){ + free(tpi->chunks[i].string); } + free(tpi->chunks); /* release the array */ + free(tpi); /* release the overall tpinfo structure */ + } + return NULL; +} + +/* Initialize the brinfo system. Returns NULL on failure. */ +BR_INFO *brinfo_init(void){ + BR_INFO *bri = NULL; + bri = (BR_INFO *)calloc(1,sizeof(BR_INFO)); + if(bri){ + if(brinfo_make_insertable(bri)){ + free(bri); + bri=NULL; + } + } + return(bri); +} + +/* verifies that there is space to add one more entry. + Creates rects if that pointer is NULL. + 0 on sucess, anything else is an error */ +int brinfo_make_insertable(BR_INFO *bri){ + int status=0; + if(!bri)return(2); + if(bri->used >= bri->space){ + bri->space += ALLOCINFO_CHUNK; + if(!(bri->rects = (BRECT_SPECS *) realloc(bri->rects, bri->space * sizeof(BRECT_SPECS) ))){ status = 1; } + } + return(status); +} + +/** Append a BRECT_SPEC element to brinfo. + Side effect - may realloc bri->rects, so element MUST NOT be a pointer into that array! + 0 on sucess, anything else is an error */ +int brinfo_insert(BR_INFO *bri, BRECT_SPECS *element){ + int status=1; + if(!bri)return(2); + if(!(status=brinfo_make_insertable(bri))){ + memcpy(&(bri->rects[bri->used]),element,sizeof(BRECT_SPECS)); + bri->used++; + } + return(status); +} + +/** Merge BRECT_SPEC element src with dst. dst becomes the merged result. + 0 on sucess, anything else is an error */ +int brinfo_merge(BR_INFO *bri, int dst, int src){ + if(!bri)return(1); + if(dst<0 || dst>= bri->used)return(2); + if(src<0 || src>= bri->used)return(3); + bri->rects[dst].xll = TEREMIN(bri->rects[dst].xll, bri->rects[src].xll); + bri->rects[dst].yll = TEREMAX(bri->rects[dst].yll, bri->rects[src].yll); /* MAX because Y is positive DOWN */ + bri->rects[dst].xur = TEREMAX(bri->rects[dst].xur, bri->rects[src].xur); + bri->rects[dst].yur = TEREMIN(bri->rects[dst].yur, bri->rects[src].yur); /* MIN because Y is positive DOWN */ +/* +printf("bri_Merge into rect:%d (LL,UR) dst:(%lf,%lf),(%lf,%lf) src:(%lf,%lf),(%lf,%lf)\n",dst, +(bri->rects[dst].xll), +(bri->rects[dst].yll), +(bri->rects[dst].xur), +(bri->rects[dst].yur), +(bri->rects[src].xll), +(bri->rects[src].yll), +(bri->rects[src].xur), +(bri->rects[src].yur)); +*/ + return(0); +} + +/** Check for an allowable overlap of two rectangles. The method works backwards, look for all reasons + they might not overlap, and none are found, then the rectangles do overlap. + An overlap here does not count just a line or a point - area must be involved. + dst one retangle to test + src the other rectangle to test + rp_src padding to apply to src, make it a little bigger, as in, allow leading or trailing spaces + 0 on sucess, 1 on no overlap, anything else is an error */ +int brinfo_overlap(BR_INFO *bri, int dst, int src, RT_PAD *rp_dst, RT_PAD *rp_src){ + int status; + BRECT_SPECS *br_dst; + BRECT_SPECS *br_src; + if(!bri)return(2); + if(dst<0 || dst>= bri->used)return(3); + if(src<0 || src>= bri->used)return(4); + br_dst=&bri->rects[dst]; + br_src=&bri->rects[src]; + if( /* Test all conditions that exclude overlap, if any are true, then no overlap */ + ((br_dst->xur + rp_dst->right) < (br_src->xll - rp_src->left) ) || /* dst fully to the left */ + ((br_dst->xll - rp_dst->left) > (br_src->xur + rp_src->right) ) || /* dst fully to the right */ + ((br_dst->yur - rp_dst->up) > (br_src->yll + rp_src->down) ) || /* dst fully below (Y is positive DOWN) */ + ((br_dst->yll + rp_dst->down) < (br_src->yur - rp_src->up) ) /* dst fully above (Y is positive DOWN) */ + ){ + status = 1; + } + else { /* overlap not excluded, so it must occur */ + status = 0; + } +/* +printf("Overlap status:%d\nOverlap trects (LL,UR) dst:(%lf,%lf),(%lf,%lf) src:(%lf,%lf),(%lf,%lf)\n", +status, +(br_dst->xll - rp_dst->left ), +(br_dst->yll - rp_dst->down ), +(br_dst->xur + rp_dst->right), +(br_dst->yur + rp_dst->up ), +(br_src->xll - rp_src->left ), +(br_src->yll - rp_src->down ), +(br_src->xur + rp_src->right), +(br_src->yur + rp_src->up )); +printf("Overlap brects (LL,UR) dst:(%lf,%lf),(%lf,%lf) src:(%lf,%lf),(%lf,%lf)\n", +(br_dst->xll), +(br_dst->yll), +(br_dst->xur), +(br_dst->yur), +(br_src->xll), +(br_src->yll), +(br_src->xur), +(br_src->yur)); +printf("Overlap rprect (LL,UR) dst:(%lf,%lf),(%lf,%lf) src:(%lf,%lf),(%lf,%lf)\n", +(rp_dst->left), +(rp_dst->down), +(rp_dst->right), +(rp_dst->up), +(rp_src->left), +(rp_src->down), +(rp_src->right), +(rp_src->up)); +*/ + return(status); +} + +/* Attempt to deduce justification of a paragraph from the bounding rectangles for two lines. If type in not UJ +then the alignment must match or UJ is returned. "slop" is the numeric inaccuracy which is permitted - two values +within that range are the same as identical.*/ +enum tr_classes brinfo_pp_alignment(BR_INFO *bri, int dst, int src, double slop, enum tr_classes type){ + enum tr_classes newtype; + BRECT_SPECS *br_dst = & bri->rects[dst]; + BRECT_SPECS *br_src = & bri->rects[src]; + if((br_dst->yur >= br_src->yur) || (br_dst->yll >= br_src->yll)){ /* Y is positive DOWN */ + /* lines in the wrong vertical order, no paragraph possible (Y is positive down) */ + newtype = TR_PARA_UJ; + } + else if(fabs(br_dst->xll - br_src->xll) < slop){ + /* LJ (might also be CJ but LJ takes precedence) */ + newtype = TR_PARA_LJ; + } + else if(fabs(br_dst->xur - br_src->xur) < slop){ + /* RJ */ + newtype = TR_PARA_RJ; + } + else if(fabs( (br_dst->xur + br_dst->xll)/2.0 - (br_src->xur + br_src->xll)/2.0 ) < slop){ + /* CJ */ + newtype = TR_PARA_CJ; + } + else { + /* not aligned */ + newtype = TR_PARA_UJ; + } + /* within a paragraph type can change from unknown to known, but not from one known type to another*/ + if((type != TR_PARA_UJ) && (newtype != type)){ + newtype = TR_PARA_UJ; + } +/* +printf("pp_align newtype:%d brects (LL,UR) dst:(%lf,%lf),(%lf,%lf) src:(%lf,%lf),(%lf,%lf)\n", +newtype, +(br_dst->xll), +(br_dst->yll), +(br_dst->xur), +(br_dst->yur), +(br_src->xll), +(br_src->yll), +(br_src->xur), +(br_src->yur)); +*/ + return(newtype); +} + +/* Shut down the tpinfo system. Release all memory. + call like: bri_ptr = brinfo_release(bri_ptr) +*/ +BR_INFO *brinfo_release(BR_INFO *bri){ + if(bri){ + free(bri->rects); + free(bri); /* release the overall brinfo structure */ + } + return NULL; +} + + + +/* Initialize the trinfo system. Returns NULL on failure. */ +TR_INFO *trinfo_init(TR_INFO *tri){ + if(tri)return(tri); /* tri is already set, double initialization is not allowed */ + if(!(tri = (TR_INFO *)calloc(1,sizeof(TR_INFO))) || + !(tri->fti = ftinfo_init()) || + !(tri->tpi = tpinfo_init()) || + !(tri->bri = brinfo_init()) || + !(tri->cxi = cxinfo_init()) + ){ tri = trinfo_release(tri); } + tri->use_kern = 1; + tri->load_flags = FT_LOAD_NO_SCALE; + tri->kern_mode = FT_KERNING_UNSCALED; + tri->out = NULL; /* This will allocate as needed, it might not ever be needed. */ + tri->outspace = 0; + tri->outused = 0; + return(tri); +} + +/* release all memory from the trinfo system. */ +TR_INFO *trinfo_release(TR_INFO *tri){ + if(tri){ + if(tri->bri)tri->bri=brinfo_release(tri->bri); + if(tri->tpi)tri->tpi=tpinfo_release(tri->tpi); + if(tri->fti)tri->fti=ftinfo_release(tri->fti); + if(tri->cxi)tri->cxi=cxinfo_release(tri->cxi); + if(tri->out){ free(tri->out); tri->out=NULL; }; + free(tri); + } + return(NULL); +} + +/* release everything except Fontconfig, which may still be needed elsewhere in a program +and there is no way to figure that out here. */ +TR_INFO *trinfo_release_except_FC(TR_INFO *tri){ + if(tri){ + if(tri->bri)tri->bri=brinfo_release(tri->bri); + if(tri->tpi)tri->tpi=tpinfo_release(tri->tpi); + if(tri->fti)tri->fti=ftinfo_clear(tri->fti); + if(tri->cxi)tri->cxi=cxinfo_release(tri->cxi); + if(tri->out){ free(tri->out); tri->out=NULL; }; + free(tri); + } + return(NULL); +} + +/* clear the text and rectangle memory from the trinfo system. Leave the font +information alone unless there is an error, in which case clear that too. The odds +are that at least some of the fonts will be reused, so faster to leave them in place. */ +TR_INFO *trinfo_clear(TR_INFO *tri){ + if(tri){ + tri->dirty = 0; /* set these back to their defaults */ + tri->esc = 0.0; + /* Do NOT modify use_kern, load_flags, or kern_mode */ + + if(tri->bri)tri->bri=brinfo_release(tri->bri); + if(tri->tpi)tri->tpi=tpinfo_release(tri->tpi); + if(tri->cxi)tri->cxi=cxinfo_release(tri->cxi); + if(tri->out){ + free(tri->out); + tri->out = NULL; + tri->outused = 0; + tri->outspace = 0; + }; + if(!(tri->tpi = tpinfo_init()) || /* re-init the pieces just released */ + !(tri->bri = brinfo_init()) || + !(tri->cxi = cxinfo_init()) + ){ + tri = trinfo_release(tri); /* something horrible happened, clean out tri and return NULL */ + } + } + return(tri); +} + +/* Load the face by fontname and font size, return the idx. If this combination is already loaded then look it up + and return the idx. +*/ + +int trinfo_load_fontname(TR_INFO *tri, uint8_t *fontname, TCHUNK_SPECS *tsp){ + FcPattern *pattern, *fpat; + FcResult result = FcResultMatch; + char *filename; + double fd; + int i; + FT_INFO *fti; + char buffer[512]; /* big enough */ + FNT_SPECS *fsp; + + if(!tri || !(tri->fti))return(1); + + fti = tri->fti; + /* construct a font name */ + sprintf(buffer,"%s:slant=%d:weight=%d:size=%lf:width=%d",fontname,tsp->italics,tsp->weight,tsp->fs,(tsp->co ? 75 : tsp->condensed)); + + for(i=0;i<fti->used;i++){ + if(0==strcmp((char *) fti->fonts[i].fname,buffer)){ + tsp->fi_idx=i; + return(0); + } + } + + ftinfo_make_insertable(fti); + tsp->fi_idx = fti->used; + + if((pattern = FcNameParse((const FcChar8 *)buffer)) == NULL)return(2); + if(!FcConfigSubstitute(NULL, pattern, FcMatchPattern))return(3); + FcDefaultSubstitute(pattern); + if((fpat = FcFontMatch(NULL, pattern, &result)) == NULL || result != FcResultMatch)return(4); + if(FcPatternGetString( fpat, FC_FILE, 0, (FcChar8 **)&filename) != FcResultMatch)return(5); + if(FcPatternGetDouble( fpat, FC_SIZE, 0, &fd) != FcResultMatch)return(7); + + /* copy these into memory for external use */ + fsp = &(fti->fonts[fti->used]); + fsp->file = (uint8_t *) U_strdup((char *) filename); + fsp->fname = (uint8_t *) U_strdup((char *) buffer); + fsp->fpat = fpat; + fsp->fsize = fd; + + /* release FC's own memory related to this call that does not need to be kept around so that face will work */ + FcPatternDestroy(pattern); + + /* get the face */ + if(FT_New_Face( fti->library, (const char *) fsp->file, 0, &(fsp->face) )){ return(8); } + + if(FT_Set_Char_Size( + fsp->face, /* handle to face object */ + 0, /* char_width in 1/64th of points */ + fd*64, /* char_height in 1/64th of points */ + 72, /* horizontal device resolution, DPI */ + 72) /* vebrical device resolution, DPI */ + ){ return(9); } + + fti->used++; + +/* + char *fs; + int fb; + if(FcPatternGetBool( fpat, FC_OUTLINE, 0, &fb)== FcResultMatch){ printf("outline: %d\n",fb);fflush(stdout); } + if(FcPatternGetBool( fpat, FC_SCALABLE, 0, &fb)== FcResultMatch){ printf("scalable: %d\n",fb);fflush(stdout); } + if(FcPatternGetDouble( fpat, FC_DPI, 0, &fd)== FcResultMatch){ printf("DPI: %lf\n",fd);fflush(stdout); } + if(FcPatternGetInteger( fpat, FC_FONTVERSION, 0, &fb)== FcResultMatch){ printf("fontversion: %d\n",fb);fflush(stdout); } + if(FcPatternGetString( fpat, FC_FULLNAME , 0, (FcChar8 **)&fs)== FcResultMatch){ printf("FULLNAME : %s\n",fs);fflush(stdout); } + if(FcPatternGetString( fpat, FC_FAMILY , 0, (FcChar8 **)&fs)== FcResultMatch){ printf("FAMILY : %s\n",fs);fflush(stdout); } + if(FcPatternGetString( fpat, FC_STYLE , 0, (FcChar8 **)&fs)== FcResultMatch){ printf("STYLE : %s\n",fs);fflush(stdout); } + if(FcPatternGetString( fpat, FC_FOUNDRY , 0, (FcChar8 **)&fs)== FcResultMatch){ printf("FOUNDRY : %s\n",fs);fflush(stdout); } + if(FcPatternGetString( fpat, FC_FAMILYLANG , 0, (FcChar8 **)&fs)== FcResultMatch){ printf("FAMILYLANG : %s\n",fs);fflush(stdout); } + if(FcPatternGetString( fpat, FC_STYLELANG , 0, (FcChar8 **)&fs)== FcResultMatch){ printf("STYLELANG : %s\n",fs);fflush(stdout); } + if(FcPatternGetString( fpat, FC_FULLNAMELANG, 0, (FcChar8 **)&fs)== FcResultMatch){ printf("FULLNAMELANG: %s\n",fs);fflush(stdout); } + if(FcPatternGetString( fpat, FC_CAPABILITY , 0, (FcChar8 **)&fs)== FcResultMatch){ printf("CAPABILITY : %s\n",fs);fflush(stdout); } + if(FcPatternGetString( fpat, FC_FONTFORMAT , 0, (FcChar8 **)&fs)== FcResultMatch){ printf("FONTFORMAT : %s\n",fs);fflush(stdout); } +*/ + + return(0); +} + + +/* Set the quantization error value. If coordinates have passed through an integer form limits + in accuracy may have been imposed. For instance, if the X coordinate of a point in such a file + is 1000, and the conversion factor from those coordinates to points is .04, then eq is .04. This + just says that single coordinates are only good to within .04, and two coordinates may differ by as much + as .08, just due to quantization error. So if some calculation shows a difference of + .02 it may be interpreted as this sort of error and set to 0.0. + + Returns 0 on success, >0 on error. +*/ +int trinfo_load_qe(TR_INFO *tri, double qe){ + if(!tri)return(1); + if(qe<0.0)return(2); + tri->qe=qe; + return(0); +} + +/* Set the FT parameters flags and kern mode and decide whether or not to to use kerning. + No error checking on those values. + Returns 0 on success, >0 on error. +*/ +int trinfo_load_ft_opts(TR_INFO *tri, int use_kern, int load_flags, int kern_mode){ + if(!tri)return(1); + tri->use_kern = use_kern; + tri->load_flags = load_flags; + tri->kern_mode = kern_mode; + return(0); +} + +/* Append text to the output buffer, expanding it if necessary. + returns 0 on success, -1 on failure +*/ +int trinfo_append_out(TR_INFO *tri, char *src){ + size_t slen; + if(!src)return(-1); + slen = strlen(src); + if(tri->outused + (int) slen + 1 < tri->outspace){ + /* already insertable */ + } + else { + tri->outspace += TEREMAX(ALLOCOUT_CHUNK,slen+1); + if(!(tri->out = realloc(tri->out, tri->outspace )))return(-1); + } + memcpy(tri->out + tri->outused, src, slen+1); /* copy the terminator */ + tri->outused += slen; /* do not count the terminator in the length */ + return(0); +} + + +/* load a text record into the system. On success returns 0. Any error returns !0. + Escapement must match that of first record. + Status of -1 indicates that an escapement change was detected. + idx etc in tsp must have been set. + load_flags - see TR_getadvance, must match graphics model of CURRENT program. + kern_mode - see TR_getadvance, must match graphics model of CURRENT program. + use_kern - true if kerning is used, must match graphics model of CURRENT program +*/ +int trinfo_load_textrec(TR_INFO *tri, TCHUNK_SPECS *tsp, double escapement, int flags){ + + int status; + double x,y,xe; + double asc,dsc; + int ymin,ymax; + TP_INFO *tpi; + FT_INFO *fti; + BR_INFO *bri; + int current,idx,taln; + uint32_t prev; + uint32_t *text32,*tptr; + FNT_SPECS *fsp; + BRECT_SPECS bsp; + + /* check incoming parameters */ + if(!tri)return(1); + if(!tsp)return(2); + if(!tsp->string)return(3); + fti = tri->fti; + tpi = tri->tpi; + bri = tri->bri; + idx = tsp->fi_idx; + taln = tsp->taln; + if(idx <0 || idx >= tri->fti->used)return(4); + + if(!tri->dirty){ + tri->x = tsp->x; + tri->y = tsp->y; + tri->esc = escapement; + tri->dirty = 1; + } + else { + if(tri->esc != escapement)return(-1); + } + + + tpinfo_insert(tpi,tsp); + current=tpi->used-1; + ymin = 64000; + ymax = -64000; + + /* The geometry model has origin Y at the top of screen, positive Y is down, maximum positive + Y is at the bottom of the screen. That makes "top" (by positive Y) actually the bottom + (as viewed on the screen.) */ + + escapement *= 2.0 * M_PI / 360.0; /* degrees to radians */ + x = tpi->chunks[current].x - tri->x; /* convert to internal orientation */ + y = tpi->chunks[current].y - tri->y; + tpi->chunks[current].x = x * cos(escapement) - y * sin(escapement); /* coordinate transformation */ + tpi->chunks[current].y = x * sin(escapement) + y * cos(escapement); + + fsp = &(fti->fonts[idx]); +/* Careful! face bbox does NOT scale with FT_Set_Char_Size +printf("Face idx:%d bbox: xMax/Min:%ld,%ld yMax/Min:%ld,%ld UpEM:%d asc/des:%d,%d height:%d size:%lf\n", + idx, fsp->face->bbox.xMax,fsp->face->bbox.xMin,fsp->face->bbox.yMax,fsp->face->bbox.yMin, + fsp->face->units_per_EM,fsp->face->ascender,fsp->face->descender,fsp->face->height,fsp->fsize); +*/ + + text32 = U_Utf8ToUtf32le((char *) tsp->string,0,NULL); + if(!text32){ // LATIN1 encoded >128 are generally not valid UTF, so the first will fail + text32 = U_Latin1ToUtf32le((char *) tsp->string,0,NULL); + if(!text32)return(5); + } + fsp->spcadv = 0.0; + /* baseline advance is independent of character orientation */ + for(xe=0.0, prev=0, tptr=text32; *tptr; tptr++){ + status = TR_getadvance(fsp, *tptr, (tri->use_kern ? prev: 0), tri->load_flags, tri->kern_mode, &ymin, &ymax); + if(status>=0){ + xe += ((double) status)/64.0; + if(*tptr==' ')fsp->spcadv = ((double) status)/64.0; + } + else { return(6); } + prev=*tptr; + } + + free(text32); + + /* get the advance on a space if it has not already been set */ + if(fsp->spcadv==0.0){ + status = TR_getadvance(fsp,' ',0, tri->load_flags, tri->kern_mode, NULL, NULL); + if(status>=0){ fsp->spcadv = ((double) status)/64.0; } + else { return(7); } + } + + if(tri->load_flags & FT_LOAD_NO_SCALE){ + xe *= tsp->fs/32.0; + fsp->spcadv *= tsp->fs/32.0; + } + + /* now place the rectangle using ALN information */ + if( taln & ALIHORI & ALILEFT ){ + bsp.xll = tpi->chunks[current].x; + bsp.xur = tpi->chunks[current].x + xe; + } + else if( taln & ALIHORI & ALICENTER){ + bsp.xll = tpi->chunks[current].x - xe/2.0; + bsp.xur = tpi->chunks[current].x + xe/2.0; + } + else{ /* taln & ALIHORI & ALIRIGHT */ + bsp.xll = tpi->chunks[current].x - xe; + bsp.xur = tpi->chunks[current].x; + } + + asc = ((double) (ymax))/64.0; + dsc = ((double) (ymin))/64.0; /* This is negative */ +/* This did not work very well because the ascender/descender went well beyond the actual characters, causing + overlaps on lines that did not actually overlap (vertically). + asc = ((double) (fsp->face->ascender) )/64.0; + dsc = ((double) (fsp->face->descender))/64.0; +*/ + if(tri->load_flags & FT_LOAD_NO_SCALE){ + asc *= tsp->fs/32.0; + dsc *= tsp->fs/32.0; + } + + + /* From this point forward y is on the baseline, so need to correct it in chunks */ + if( taln & ALIVERT & ALITOP ){ tpi->chunks[current].y += -dsc + asc; } + else if( taln & ALIVERT & ALIBASE){ } /* no correction required */ + else{ /* taln & ALIVERT & ALIBOT */ + if(flags & TR_EMFBOT){ tpi->chunks[current].y -= 0.35 * tsp->fs; } /* compatible with EMF implementations */ + else { tpi->chunks[current].y += dsc; } + } + tpi->chunks[current].boff = -dsc; + + /* since y is always on the baseline, the lower left and upper right are easy */ + bsp.yll = tpi->chunks[current].y - dsc; + bsp.yur = tpi->chunks[current].y - asc; + brinfo_insert(bri,&bsp); + tpi->chunks[current].rt_tidx = bri->used - 1; /* index of rectangle that contains it */ + + return(0); +} + +/* Font weight conversion, from fontconfig weights to SVG weights. +Anything not recognized becomes "normal" == 400. There is no interpolation because a value +that mapped to 775, for instance, most likely would not display at a weight intermediate +between 700 and 800. +*/ +int TR_weight_FC_to_SVG(int weight){ + int ret=400; + if( weight == 0){ ret = 100; } + else if(weight == 40){ ret = 200; } + else if(weight == 50){ ret = 300; } + else if(weight == 80){ ret = 400; } + else if(weight == 100){ ret = 500; } + else if(weight == 180){ ret = 600; } + else if(weight == 200){ ret = 700; } + else if(weight == 205){ ret = 800; } + else if(weight == 210){ ret = 900; } + else { ret = 400; } + return(ret); +} + +/* Set the padding that will be added to rectangles before checking for overlaps. + Method is set for L->R, or R->L text, not correct for vertical text. +*/ +void TR_rt_pad_set(RT_PAD *rt_pad, double up, double down, double left, double right){ + rt_pad->up = up; + rt_pad->down = down; + rt_pad->left = left; + rt_pad->right = right; +} + +/* Convert from analyzed complexes to SVG format, stored in the "out" buffer of the tri. +*/ +void TR_layout_2_svg(TR_INFO *tri){ + double x = tri->x; + double y = tri->y; + double dx,dy; + double lastx = 0.0; + double lasty = 0.0; + double qsp; + double esc; + double recenter; /* horizontal offset to set things up correctly for CJ and RJ text, is 0 for LJ*/ + double lineheight=1.25; + int cutat; + FT_INFO *fti=tri->fti; /* Font info storage */ + TP_INFO *tpi=tri->tpi; /* Text Info/Position Info storage */ + BR_INFO *bri=tri->bri; /* bounding Rectangle Info storage */ + CX_INFO *cxi=tri->cxi; /* Complexes deduced for this text */ + TCHUNK_SPECS *tsp; /* current text object */ + TCHUNK_SPECS *ptsp; /* previous text object in the same line as current text object, if any */ + FNT_SPECS *fsp; + CX_SPECS *csp; + int i,j,k,jdx,kdx; + int status; + char obuf[1024]; /* big enough for style and so forth */ + +#if defined(DBG_TR_PARA) || defined(DBG_TR_INPUT) /* enable debugging code, writes extra information into SVG */ + char stransform[128]; + double newx,newy; + + /* put rectangles down for each text string - debugging!!! This will not work properly for any Narrow fonts */ + for(i=cxi->phase1; i<cxi->used;i++){ /* over all complex members from phase2 == TR_PARA_* complexes */ + csp = &(cxi->cx[i]); + esc = tri->esc; + esc *= 2.0 * M_PI / 360.0; /* degrees to radians and change direction of rotation */ + for(j=0; j<csp->kids.used; j++){ /* over all members of these complexes, which are phase1 complexes */ + jdx = csp->kids.members[j]; /* index of phase1 complex (all are TR_TEXT or TR_LINE) */ + for(k=0; k<cxi->cx[jdx].kids.used; k++){ /* over all members of the phase1 complex */ + kdx = cxi->cx[jdx].kids.members[k]; /* index for text objects in tpi */ + tsp = &tpi->chunks[kdx]; + if(!j && !k){ + sprintf(stransform,"transform=\"matrix(%lf,%lf,%lf,%lf,%lf,%lf)\"\n",cos(esc),-sin(esc),sin(esc),cos(esc), + 1.25*x,1.25*y); + lastx = bri->rects[tsp->rt_tidx].xll; + lasty = bri->rects[tsp->rt_tidx].yll - tsp->boff; +#ifdef DBG_TR_PARA + TRPRINT(tri, "<rect\n"); + TRPRINT(tri, "style=\"color:#0000FF;color-interpolation:sRGB;color-interpolation-filters:linearRGB;fill:none;stroke:#000000;stroke-width:0.30000001;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate;clip-rule:nonzero\"\n"); + sprintf(obuf,"width=\"%lf\"\n", 1.25*(bri->rects[csp->rt_cidx].xur - bri->rects[csp->rt_cidx].xll)); + TRPRINT(tri, obuf); + sprintf(obuf,"height=\"%lf\"\n",1.25*(bri->rects[csp->rt_cidx].yll - bri->rects[csp->rt_cidx].yur)); + TRPRINT(tri, obuf); + sprintf(obuf,"x=\"%lf\" y=\"%lf\"\n",1.25*(bri->rects[csp->rt_cidx].xll),1.25*(bri->rects[csp->rt_cidx].yur)); + TRPRINT(tri, obuf); + TRPRINT(tri, stransform); + TRPRINT(tri, "/>\n"); +#endif /* DBG_TR_PARA */ + } +#ifdef DBG_TR_INPUT /* debugging code, this section writes the original text objects */ + newx = 1.25*(bri->rects[tsp->rt_tidx].xll); + newy = 1.25*(bri->rects[tsp->rt_tidx].yur); + TRPRINT(tri, "<rect\n"); + TRPRINT(tri, "style=\"color:#000000;color-interpolation:sRGB;color-interpolation-filters:linearRGB;fill:none;stroke:#000000;stroke-width:0.30000001;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate;clip-rule:nonzero\"\n"); + sprintf(obuf,"width=\"%lf\"\n", 1.25*(bri->rects[tsp->rt_tidx].xur - bri->rects[tsp->rt_tidx].xll)); + TRPRINT(tri, obuf); + sprintf(obuf,"height=\"%lf\"\n",1.25*(bri->rects[tsp->rt_tidx].yll - bri->rects[tsp->rt_tidx].yur)); + TRPRINT(tri, obuf); + sprintf(obuf,"x=\"%lf\" y=\"%lf\"\n",newx,newy); + TRPRINT(tri, obuf); + TRPRINT(tri, stransform); + TRPRINT(tri, "/>\n"); + + newy = 1.25*(bri->rects[tsp->rt_tidx].yll - tsp->boff); + sprintf(obuf,"<text x=\"%lf\" y=\"%lf\"\n",newx, newy ); + TRPRINT(tri, obuf); + sprintf(obuf,"xml:space=\"preserve\"\n"); + TRPRINT(tri, obuf); + TRPRINT(tri, stransform); + TRPRINT(tri, "style=\"fill:#FF0000;"); + sprintf(obuf,"font-size:%lfpx;",tsp->fs*1.25); /*IMPORTANT, if the FS is given in pt it looks like crap in browsers. As if px != 1.25 pt, maybe 96 dpi not 90?*/ + TRPRINT(tri, obuf); + sprintf(obuf,"font-style:%s;",(tsp->italics ? "italic" : "normal")); + TRPRINT(tri, obuf); + TRPRINT(tri, "font-variant:normal;"); + sprintf(obuf,"font-weight:%d;",TR_weight_FC_to_SVG(tsp->weight)); + TRPRINT(tri, obuf); + sprintf(obuf,"font-stretch:%s;",(tsp->condensed==100 ? "Normal" : "Condensed")); + TRPRINT(tri, obuf); + cutat=strcspn((char *)fti->fonts[tsp->fi_idx].fname,":"); + fti->fonts[tsp->fi_idx].fname[cutat]='\0'; + sprintf(obuf,"font-family:%s;",fti->fonts[tsp->fi_idx].fname); + TRPRINT(tri, obuf); + sprintf(obuf,"\n\">%s</text>\n",tsp->string); + TRPRINT(tri, obuf); +#endif /* DBG_TR_INPUT debugging code, original text objects */ + } + } + } +#endif /* DBG_TR_PARA and/or DBG_TR_INPUT */ + + + tsp=tpi->chunks; + /* over all complex members from phase2. Paragraphs == TR_PARA_* */ + for(i=cxi->phase1; i<cxi->used;i++){ + csp = &(cxi->cx[i]); + esc = tri->esc; + esc *= 2.0 * M_PI / 360.0; /* degrees to radians and change direction of rotation */ + + /* over all members of the present Paragraph. Each of these is a line and a phase 1 complex. + It may be either TR_TEXT or TR_LINE */ + for(j=0; j<csp->kids.used; j++){ + if(j){ + sprintf(obuf,"</tspan>"); + TRPRINT(tri, obuf); + } + jdx = csp->kids.members[j]; /* index of phase1 complex (all are TR_TEXT or TR_LINE) */ + lastx = bri->rects[jdx].xur; + lasty = bri->rects[jdx].yll - tsp->boff; + recenter = 0; /* mostly to quiet a compiler warning, should always be set below */ + + /* over all members of the present Line. These are the original text objects which were reassembled. + There will be one for TR_TEXT, more than one for TR_LINE */ + for(ptsp = NULL, k=0; k<cxi->cx[jdx].kids.used; k++){ + if(k){ + ptsp=tsp; /* previous text object in this line */ + fsp = &(fti->fonts[tpi->chunks[kdx].fi_idx]); /* font spec for previous text object */ + } + kdx = cxi->cx[jdx].kids.members[k]; /* index for text objects in tpi */ + tsp = &tpi->chunks[kdx]; + if(!k){ + switch(csp->type){ /* set up the alignment, if there is one */ + case TR_TEXT: + case TR_LINE: + /* these should never occur, this section quiets a compiler warning */ + break; + case TR_PARA_UJ: + recenter=0.0; + break; + case TR_PARA_LJ: + recenter=0.0; + break; + case TR_PARA_CJ: + recenter=(bri->rects[cxi->cx[jdx].rt_cidx].xur - bri->rects[cxi->cx[jdx].rt_cidx].xll)/2.0; + break; + case TR_PARA_RJ: + recenter=bri->rects[cxi->cx[jdx].rt_cidx].xur - bri->rects[cxi->cx[jdx].rt_cidx].xll; + break; + } + if(!j){ + TRPRINT(tri, "<text\n"); + TRPRINT(tri, "xml:space=\"preserve\"\n"); + TRPRINT(tri, "style=\""); + sprintf(obuf,"font-size:%lfpx;",tsp->fs*1.25); /*IMPORTANT, if the FS is given in pt it looks like crap in browsers. As if px != 1.25 pt, maybe 96 dpi not 90?*/ + TRPRINT(tri, obuf); + sprintf(obuf,"font-style:%s;",(tsp->italics ? "italic" : "normal")); + TRPRINT(tri, obuf); + TRPRINT(tri, "font-variant:normal;"); + sprintf(obuf,"font-weight:%d;",TR_weight_FC_to_SVG(tsp->weight)); + TRPRINT(tri, obuf); + sprintf(obuf,"font-stretch:%s;",(tsp->condensed==100 ? "Normal" : "Condensed")); + TRPRINT(tri, obuf); + if(tsp->vadvance){ lineheight = tsp->vadvance *100.0; } + else { lineheight = 125.0; } + sprintf(obuf,"line-height:%lf%%;",lineheight); + TRPRINT(tri, obuf); + TRPRINT(tri, "letter-spacing:0px;"); + TRPRINT(tri, "word-spacing:0px;"); + TRPRINT(tri, "fill:#000000;"); + TRPRINT(tri, "fill-opacity:1;"); + TRPRINT(tri, "stroke:none;"); + cutat=strcspn((char *)fti->fonts[tsp->fi_idx].fname,":"); + fti->fonts[tsp->fi_idx].fname[cutat]='\0'; + sprintf(obuf,"font-family:%s;",fti->fonts[tsp->fi_idx].fname); + TRPRINT(tri, obuf); + switch(csp->type){ /* set up the alignment, if there is one */ + case TR_TEXT: + case TR_LINE: + /* these should never occur, this section quiets a compiler warning */ + break; + case TR_PARA_UJ: + *obuf='\0'; + break; + case TR_PARA_LJ: + sprintf(obuf,"text-align:start;text-anchor:start;"); + break; + case TR_PARA_CJ: + sprintf(obuf,"text-align:center;text-anchor:middle;"); + break; + case TR_PARA_RJ: + sprintf(obuf,"text-align:end;text-anchor:end;"); + break; + } + TRPRINT(tri, obuf); + TRPRINT(tri, "\"\n"); /* End of style specification */ + sprintf(obuf,"transform=\"matrix(%lf,%lf,%lf,%lf,%lf,%lf)\"\n",cos(esc),-sin(esc),sin(esc),cos(esc),1.25*x,1.25*y); + TRPRINT(tri, obuf); + sprintf(obuf,"x=\"%lf\" y=\"%lf\"\n>",1.25*(bri->rects[kdx].xll + recenter),1.25*(bri->rects[kdx].yll - tsp->boff)); + TRPRINT(tri, obuf); + } + sprintf(obuf,"<tspan sodipodi:role=\"line\"\nx=\"%lf\" y=\"%lf\"\n>", + 1.25*(bri->rects[kdx].xll + recenter),1.25*(bri->rects[kdx].yll - tsp->boff)); + TRPRINT(tri, obuf); + } + TRPRINT(tri, "<tspan\n"); + dx = 1.25*(bri->rects[tsp->rt_tidx].xll - lastx); + dy = 1.25*(bri->rects[tsp->rt_tidx].yll - tsp->boff - lasty); + + /* Have to also take into account kerning between the last letter of the preceding rectangle + and the first letter of the current one. Assume font values are from leading retangle's font. */ + if(ptsp && tri->use_kern){ + status = TR_kern_gap(fsp, tsp, ptsp, tri->kern_mode); + if(status){ + dx += (tri->load_flags & FT_LOAD_NO_SCALE ? tsp->fs/32.0: 1.0) * ((double) status)/64.0; + } + } + + /* Sometimes a font substitution was absolutely terrible, for instance, for Arial Narrow on (most) Linux systems, + The resulting advance may be much too large so that it overruns the next text chunk. Since overlapping text on + the same line is almost never encountered, this may be used to detect the bad substitution so that a more appropriate + offset can be used. + Detect this situation as a negative dx < 1/2 a space character's width while |dy| < an entire space width. */ + qsp = 1.25 * 0.25 * fti->fonts[tsp->fi_idx].spcadv; + if((dy <=qsp && dy >= -qsp) && dx < -2*qsp){ dx=0.0; } + if(k==0){ sprintf(obuf,"dx=\"%lf\" dy=\"%lf\" ",0.0, 0.0); } + else { sprintf(obuf,"dx=\"%lf\" dy=\"%lf\" ",dx, dy); } + TRPRINT(tri, obuf); + sprintf(obuf,"style=\"fill:#%6.6X;",tsp->color); + TRPRINT(tri, obuf); + sprintf(obuf,"font-size:%lfpx;",tsp->fs*1.25); /*IMPORTANT, if the FS is given in pt it looks like crap in browsers. As if px != 1.25 pt, maybe 96 dpi not 90?*/ + TRPRINT(tri, obuf); + sprintf(obuf,"font-style:%s;",(tsp->italics ? "italic" : "normal")); + TRPRINT(tri, obuf); + TRPRINT(tri, "font-variant:normal;"); + sprintf(obuf,"font-weight:%d;",TR_weight_FC_to_SVG(tsp->weight)); + TRPRINT(tri, obuf); + sprintf(obuf,"font-stretch:%s;",(tsp->condensed==100 ? "Normal" : "Condensed")); + TRPRINT(tri, obuf); + cutat=strcspn((char *)fti->fonts[tsp->fi_idx].fname,":"); + fti->fonts[tsp->fi_idx].fname[cutat]='\0'; + sprintf(obuf,"font-family:%s;\"",fti->fonts[tsp->fi_idx].fname); + TRPRINT(tri, obuf); + TRPRINT(tri, "\n>"); + TRPRINT(tri, (char *) tsp->string); + TRPRINT(tri, "</tspan>"); + lastx=bri->rects[tsp->rt_tidx].xur; + lasty=bri->rects[tsp->rt_tidx].yll - tsp->boff; + } /* end of k loop */ + } /* end of j loop */ + TRPRINT(tri,"</tspan></text>\n"); + } /* end of i loop */ +} + +/** Attempt to figure out what the text was originally. + 1. Group text strings by overlaps (optionally allowing up to two spaces to be added) to produce larger rectangles. + Larger rectangles that are more or less sequential are LINES, otherwise they are EQN. + 2. Group sequential LINES into paragraphs (by smooth progression in position down page). + 3. Analyze the paragraphs to classify them as Left/Center/Right justified (possibly with indentation.) If + they do not fall into any of these categories break that one back down into LINES. + 4. Return the number of complex text objects. Value will be >=1 and <= number of text strings. + + Values <0 are errors +*/ +int TR_layout_analyze(TR_INFO *tri){ + int i,j; + int ok; + int cxidx; + int src_rt; + int dst_rt; + TP_INFO *tpi; + BR_INFO *bri; + CX_INFO *cxi; + FT_INFO *fti; + BRECT_SPECS bsp; + RT_PAD rt_pad_i; + RT_PAD rt_pad_j; + double ratio; + enum tr_classes type; + + if(!tri)return(-1); + if(!tri->cxi)return(-2); + if(!tri->tpi)return(-3); + if(!tri->bri)return(-4); + if(!tri->fti)return(-5); + tpi=tri->tpi; + cxi=tri->cxi; + bri=tri->bri; + fti=tri->fti; + cxi->lines = 0; + cxi->paras = 0; + cxi->phase1 = 0; + + /* Phase 1. Working sequentially, insert text. Initially as TR_TEXT and then try to extend to TR_LINE by checking + overlaps. When done the complexes will contain a mix of TR_LINE and TR_TEXT. */ + + for(i=0; i<tpi->used; i++){ + memcpy(&bsp,&(bri->rects[tpi->chunks[i].rt_tidx]),sizeof(BRECT_SPECS)); /* Must make a copy as next call may reallocate rects! */ + (void) brinfo_insert(bri,&bsp); + dst_rt = bri->used-1; + (void) cxinfo_insert(cxi, i, dst_rt, TR_TEXT); + cxidx = cxi->used-1; + /* for the leading text: pad with no leading and two trailing spaces */ + TR_rt_pad_set(&rt_pad_i,tri->qe, tri->qe, 0.0, tri->qe + 2.0 * fti->fonts[tpi->chunks[i].fi_idx].spcadv); + + for(j=i+1; j<tpi->used; j++){ + /* Reject font size changes of greater than 50%, these almost certainly not continuous text. These happen + in math formulas, for instance, where a sum or integral is much larger than the other symbols. */ + ratio = (double)(tpi->chunks[j].fs)/(double)(tpi->chunks[i].fs); + if(ratio >2.0 || ratio <0.5)break; + + /* for the trailing text: pad with one leading and no trailing spaces */ + TR_rt_pad_set(&rt_pad_j,tri->qe, tri->qe, 1.0 * fti->fonts[tpi->chunks[j].fi_idx].spcadv, 0.0); + src_rt = tpi->chunks[j].rt_tidx; + if(!brinfo_overlap(bri, + dst_rt, /* index into bri for dst */ + src_rt, /* index into bri for src */ + &rt_pad_i,&rt_pad_j)){ + (void) cxinfo_append(cxi,j,TR_LINE); + (void) brinfo_merge(bri,dst_rt,src_rt); + TR_rt_pad_set(&rt_pad_i, tri->qe, tri->qe, 0.0, tri->qe + 2.0 * fti->fonts[tpi->chunks[j].fi_idx].spcadv); + } + else { /* either alignment ge*/ + break; + } + } + i=j-1; /* start up after the last merged entry (there may not be any) */ + if(cxi->cx[cxidx].type == TR_LINE)cxi->lines++; + } + cxi->phase1 = cxi->used; /* total complexes defined in this phase, all TR_LINE or TR_TEXT */ + + /* Phase 2, try to group sequential lines. There may be "lines" that are still TR_TEXT, as in: + + ... this is a sentence that wraps by one + word. + + And some paragrahs might be single word lines (+ = bullet in the following) + + +verbs + +nouns + +adjectives + + Everything starts out as TR_PARA_UJ and if the next one can be lined up, the type changes to + an aligned paragraph and complexes are appended to the existing one. + */ + + for(i=0; i < cxi->phase1; i++){ + type = TR_PARA_UJ; /* any paragraph alignment will be acceptable */ + memcpy(&bsp,&(bri->rects[cxi->cx[i].rt_cidx]),sizeof(BRECT_SPECS)); /* Must make a copy as next call may reallocate rects! */ + (void) brinfo_insert(bri,&bsp); + dst_rt = bri->used-1; + (void) cxinfo_insert(cxi, i, dst_rt, type); + cxi->paras++; + ok = 1; + for(j=i+1; ok && (j < cxi->phase1); j++){ + type = brinfo_pp_alignment(bri, cxi->cx[i].rt_cidx, cxi->cx[j].rt_cidx, 3*tri->qe, type); + switch (type){ + case TR_PARA_UJ: /* paragraph type was set and j line does not fit, or no paragraph alignment matched */ + ok = 0; /* force exit from j loop */ + j--; /* this will increment at loop bottom */ + break; + case TR_PARA_LJ: + case TR_PARA_CJ: + case TR_PARA_RJ: + /* two successive lines have been identified (possible following others already in the paragraph */ + if(TR_check_set_vadvance(tri,j,i)){ /* check for compatibility with vadvance if set, set it if it isn't. */ + ok = 0; /* force exit from j loop */ + j--; /* this will increment at loop bottom */ + } + else { + src_rt = cxi->cx[j].rt_cidx; + (void) cxinfo_append(cxi, j, type); + (void) brinfo_merge(bri, dst_rt, src_rt); + } + break; + default: + return(-6); /* programming error */ + } + } + if(j>=cxi->phase1)break; + i=j-1; + } + +/* When debugging + cxinfo_dump(tri); +*/ + + return(cxi->used); +} + + + +#if TEST +#define MAXLINE 2048 /* big enough for testing */ +enum OP_TYPES {OPCOM,OPOOPS,OPFONT,OPESC,OPORI,OPXY,OPFS,OPTEXT,OPALN,OPLDIR,OPMUL,OPITA,OPWGT,OPCND,OPCLR,OPFLAGS,OPEMIT,OPDONE}; + +int parseit(char *buffer,char **data){ + int pre; + pre = strcspn(buffer,":"); + if(!pre)return(OPOOPS); + *data=&buffer[pre+1]; + buffer[pre]='\0'; + if(*buffer=='#' )return(OPCOM ); + if(0==strcmp("FONT",buffer))return(OPFONT); + if(0==strcmp("ESC" ,buffer))return(OPESC ); + if(0==strcmp("ORI", buffer))return(OPORI ); + if(0==strcmp("XY", buffer))return(OPXY ); + if(0==strcmp("FS", buffer))return(OPFS ); + if(0==strcmp("TEXT",buffer))return(OPTEXT); + if(0==strcmp("ALN", buffer))return(OPALN ); + if(0==strcmp("LDIR",buffer))return(OPLDIR); + if(0==strcmp("MUL", buffer))return(OPMUL ); + if(0==strcmp("ITA", buffer))return(OPITA ); + if(0==strcmp("WGT", buffer))return(OPWGT ); + if(0==strcmp("CND", buffer))return(OPCND ); + if(0==strcmp("CLR", buffer))return(OPCLR ); + if(0==strcmp("FLAG",buffer))return(OPFLAGS); + if(0==strcmp("EMIT",buffer))return(OPEMIT); + if(0==strcmp("DONE",buffer))return(OPDONE); + return(OPOOPS); +} + +void boom(char *string,int lineno){ + fprintf(stderr,"Fatal error at line %d %s\n",lineno,string); + exit(EXIT_FAILURE); +} + + +void init_as_svg(TR_INFO *tri){ + TRPRINT(tri,"<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n"); + TRPRINT(tri,"<!-- Created with Inkscape (http://www.inkscape.org/) -->\n"); + TRPRINT(tri,"\n"); + TRPRINT(tri,"<svg\n"); + TRPRINT(tri," xmlns:dc=\"http://purl.org/dc/elements/1.1/\"\n"); + TRPRINT(tri," xmlns:cc=\"http://creativecommons.org/ns#\"\n"); + TRPRINT(tri," xmlns:rdf=\"http://www.w3.org/1999/02/22-rdf-syntax-ns#\"\n"); + TRPRINT(tri," xmlns:svg=\"http://www.w3.org/2000/svg\"\n"); + TRPRINT(tri," xmlns=\"http://www.w3.org/2000/svg\"\n"); + TRPRINT(tri," xmlns:sodipodi=\"http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd\"\n"); + TRPRINT(tri," xmlns:inkscape=\"http://www.inkscape.org/namespaces/inkscape\"\n"); + TRPRINT(tri," width=\"900\"\n"); + TRPRINT(tri," height=\"675\"\n"); + TRPRINT(tri," id=\"svg4122\"\n"); + TRPRINT(tri," version=\"1.1\"\n"); + TRPRINT(tri," inkscape:version=\"0.48+devel r11679 custom\"\n"); + TRPRINT(tri," sodipodi:docname=\"simplest_text.svg\">\n"); + TRPRINT(tri," <defs\n"); + TRPRINT(tri," id=\"defs4124\" />\n"); + TRPRINT(tri," <sodipodi:namedview\n"); + TRPRINT(tri," id=\"base\"\n"); + TRPRINT(tri," pagecolor=\"#ffffff\"\n"); + TRPRINT(tri," bordercolor=\"#666666\"\n"); + TRPRINT(tri," borderopacity=\"1.0\"\n"); + TRPRINT(tri," inkscape:pageopacity=\"0.0\"\n"); + TRPRINT(tri," inkscape:pageshadow=\"2\"\n"); + TRPRINT(tri," inkscape:zoom=\"0.98994949\"\n"); + TRPRINT(tri," inkscape:cx=\"309.88761\"\n"); + TRPRINT(tri," inkscape:cy=\"482.63995\"\n"); + TRPRINT(tri," inkscape:document-units=\"px\"\n"); + TRPRINT(tri," inkscape:current-layer=\"layer1\"\n"); + TRPRINT(tri," showgrid=\"false\"\n"); + TRPRINT(tri," width=\"0px\"\n"); + TRPRINT(tri," height=\"0px\"\n"); + TRPRINT(tri," fit-margin-top=\"0\"\n"); + TRPRINT(tri," fit-margin-left=\"0\"\n"); + TRPRINT(tri," fit-margin-right=\"0\"\n"); + TRPRINT(tri," fit-margin-bottom=\"0\"\n"); + TRPRINT(tri," units=\"in\"\n"); + TRPRINT(tri," inkscape:window-width=\"1200\"\n"); + TRPRINT(tri," inkscape:window-height=\"675\"\n"); + TRPRINT(tri," inkscape:window-x=\"26\"\n"); + TRPRINT(tri," inkscape:window-y=\"51\"\n"); + TRPRINT(tri," inkscape:window-maximized=\"0\" />\n"); + TRPRINT(tri," <metadata\n"); + TRPRINT(tri," id=\"metadata4127\">\n"); + TRPRINT(tri," <rdf:RDF>\n"); + TRPRINT(tri," <cc:Work\n"); + TRPRINT(tri," rdf:about=\"\">\n"); + TRPRINT(tri," <dc:format>image/svg+xml</dc:format>\n"); + TRPRINT(tri," <dc:type\n"); + TRPRINT(tri," rdf:resource=\"http://purl.org/dc/dcmitype/StillImage\" />\n"); + TRPRINT(tri," <dc:title></dc:title>\n"); + TRPRINT(tri," </cc:Work>\n"); + TRPRINT(tri," </rdf:RDF>\n"); + TRPRINT(tri," </metadata>\n"); + TRPRINT(tri," <g\n"); + TRPRINT(tri," inkscape:label=\"Layer 1\"\n"); + TRPRINT(tri," inkscape:groupmode=\"layer\"\n"); + TRPRINT(tri," id=\"layer1\"\n"); + TRPRINT(tri," transform=\"translate(0,0)\">\n"); + TRPRINT(tri,"\n"); +} + + +void flush_as_svg(TR_INFO *tri, FILE *fp){ + fwrite(tri->out,tri->outused,1,fp); +} + +FILE *close_as_svg(TR_INFO *tri, FILE *fp){ + TRPRINT(tri, " </g>\n"); + TRPRINT(tri, "</svg>\n"); + flush_as_svg(tri,fp); + fclose(fp); + return(NULL); +} + + +int main(int argc, char *argv[]){ + char *data; + char inbuf[MAXLINE]; + FILE *fpi = NULL; + FILE *fpo = NULL; + int op; + double fact = 1.0; /* input units to points */ + double escapement = 0.0; /* degrees */ + int lineno = 0; + int ok = 1; + int status; + TCHUNK_SPECS tsp; + TR_INFO *tri=NULL; + int flags=0; + char *infile; + + infile=malloc(strlen(argv[1])+1); + strcpy(infile,argv[1]); + + if(argc < 2 || !(fpi = fopen(infile,"r"))){ + printf("Usage: text_reassemble input_file\n"); + printf(" Test program reads an input file containing lines like:\n"); + printf(" FONT:(font for next text)\n"); + printf(" ESC:(escapement angle degrees of text line, up from X axis)\n"); + printf(" ORI:(angle degrees of character orientation, up from X axis)\n"); + printf(" FS:(font size, units)\n"); + printf(" XY:(x,y) X 0 is at left, N is at right, Y 0 is at top, N is at bottom, as page is viewed.\n"); + printf(" TEXT:(UTF8 text)\n"); + printf(" ALN:combination of {LCR}{BLT} = Text is placed on {X,Y} at Left/Center/Right of text, at Bottom,baseLine,Top of text.\n"); + printf(" LDIR:{LR|RL|TB) Left to Right, Right to Left, and Top to Bottom \n"); + printf(" MUL:(float, multiplicative factor to convert FS,XY units to points).\n"); + printf(" ITA:(Italics, 0=normal, 100=italics, 110=oblique).\n"); + printf(" WGT:(Weight, 0-215: 80=normal, 200=bold, 215=ultrablack, 0=thin)).\n"); + printf(" CND:(Condensed 50-200: 100=normal, 50=ultracondensed, 75=condensed, 200=expanded).\n"); + printf(" CLR:(RGBA color, HEX) \n"); + printf(" FLAG: Special processing options. 1 EMF compatible text alignment.\n"); + printf(" EMIT:(Process everything up to this point, then start clean for remaining input).\n"); + printf(" DONE:(no more input, process it).\n"); + printf(" # comment\n"); + printf("\n"); + printf(" The output is a summary of how the pieces are to be assembled into complex text.\n"); + printf("\n"); + printf(" egrep pattern: '^LOAD:|^FONT:|^ESC:|^ORI:|^FS:|^XY:|^TEXT:|^ALN:|^LDIR:|^MUL:|^ITA:|^WGT:|^CND:|^CLR:|^FLAG:|^EMIT:^DONE:'\n"); + exit(EXIT_FAILURE); + } + + tri = trinfo_init(tri); /* If it loops the trinfo_clear at the end will reset tri to the proper state, do NOT call trinfo_init twice! */ + +#ifdef DBG_LOOP + int ldx; + for(ldx=0;ldx<5;ldx++){ + if(fpi)fclose(fpi); + fpi = fopen(infile,"r"); +#endif + tsp.string = NULL; + tsp.ori = 0.0; /* degrees */ + tsp.fs = 12.0; /* font size */ + tsp.x = 0.0; + tsp.y = 0.0; + tsp.boff = 0.0; /* offset to baseline from LL corner of bounding rectangle, changes with fs and taln*/ + tsp.vadvance = 0.0; /* meaningful only when a complex contains two or more lines */ + tsp.taln = ALILEFT + ALIBASE; + tsp.ldir = LDIR_LR; + tsp.color = 0; /* RGBA Black */ + tsp.italics = 0; + tsp.weight = 80; + tsp.condensed = 100; + tsp.co = 0; + tsp.fi_idx = -1; /* set to an invalid */ + /* no need to set rt_tidx */ + + if(!tri){ + fprintf(stderr,"Fatal error, could not initialize data structures\n"); + exit(EXIT_FAILURE); + } + (void) trinfo_load_ft_opts(tri, 1, + FT_LOAD_NO_SCALE | FT_LOAD_NO_HINTING | FT_LOAD_NO_BITMAP, + FT_KERNING_UNSCALED); + + fpo=fopen("dump.svg","wb"); + init_as_svg(tri); + + while(ok){ + lineno++; + if(!fgets(inbuf,MAXLINE,fpi))boom("Unexpected end of file - no DONE:",lineno); + inbuf[strlen(inbuf)-1]='\0'; /* step on the EOL character */ + op = parseit(inbuf,&data); + switch(op){ + case OPCOM: /* ignore comments*/ + break; + case OPFONT: + /* If the font name includes "Narrow" condensed may not have been set */ + if(0<= TR_findcasesub(data, "Narrow")){ + tsp.co=1; + } + else { + tsp.co=0; + } + if(trinfo_load_fontname(tri, (uint8_t *) data, &tsp))boom("Font load failed",lineno); + break; + case OPESC: + if(1 != sscanf(data,"%lf",&escapement))boom("Invalid ESC:",lineno); + break; + case OPORI: + if(1 != sscanf(data,"%lf",&tsp.ori))boom("Invalid ORI:",lineno); + break; + case OPFS: + if(1 != sscanf(data,"%lf",&tsp.fs) || tsp.fs <= 0.0)boom("Invalid FS:",lineno); + tsp.fs *= fact; + break; + case OPXY: + if(2 != sscanf(data,"%lf,%lf",&tsp.x,&tsp.y) )boom("Invalid XY:",lineno); + tsp.x *= fact; + tsp.y *= fact; + break; + case OPTEXT: + tsp.string = (uint8_t *) U_strdup(data); + /* FreeType parameters match inkscape*/ + status = trinfo_load_textrec(tri, &tsp, escapement,flags); + if(status==-1){ // change of escapement, emit what we have and reset + TR_layout_analyze(tri); + TR_layout_2_svg(tri); + flush_as_svg(tri, fpo); + tri = trinfo_clear(tri); + if(trinfo_load_textrec(tri, &tsp, escapement,flags)){ boom("Text load failed",lineno); } + } + else if(status){ boom("Text load failed",lineno); } + break; + case OPALN: + tsp.taln=0; + switch (*data++){ + case 'L': tsp.taln |= ALILEFT; break; + case 'C': tsp.taln |= ALICENTER; break; + case 'R': tsp.taln |= ALIRIGHT; break; + default: boom("Invalid ALN:",lineno); + } + switch (*data++){ + case 'T': tsp.taln |= ALITOP; break; + case 'L': tsp.taln |= ALIBASE; break; + case 'B': tsp.taln |= ALIBOT; break; + default: boom("Invalid ALN:",lineno); + } + break; + case OPLDIR: + tsp.ldir=0; + if(0==strcmp("LR",data)){ tsp.ldir=LDIR_LR; break;} + if(0==strcmp("RL",data)){ tsp.ldir=LDIR_RL; break;} + if(0==strcmp("TB",data)){ tsp.ldir=LDIR_TB; break;} + boom("Invalid LDIR:",lineno); + break; + case OPMUL: + if(1 != sscanf(data,"%lf",&fact) || fact <= 0.0)boom("Invalid MUL:",lineno); + (void) trinfo_load_qe(tri,fact); + break; + case OPITA: + if(1 != sscanf(data,"%d",&tsp.italics) || tsp.italics < 0 || tsp.italics>110)boom("Invalid ITA:",lineno); + break; + case OPWGT: + if(1 != sscanf(data,"%d",&tsp.weight) || tsp.weight < 0 || tsp.weight > 215)boom("Invalid WGT:",lineno); + break; + case OPCND: + if(1 != sscanf(data,"%d",&tsp.condensed) || tsp.condensed < 50 || tsp.condensed > 200)boom("Invalid CND:",lineno); + break; + case OPCLR: + if(1 != sscanf(data,"%x",&tsp.color) )boom("Invalid CLR:",lineno); + break; + case OPFLAGS: + if(1 != sscanf(data,"%d",&flags) )boom("Invalid FLAG:",lineno); + break; + case OPEMIT: + TR_layout_analyze(tri); + TR_layout_2_svg(tri); + flush_as_svg(tri, fpo); + tri = trinfo_clear(tri); + break; + case OPDONE: + TR_layout_analyze(tri); + TR_layout_2_svg(tri); + flush_as_svg(tri, fpo); + tri = trinfo_clear(tri); + ok = 0; + break; + case OPOOPS: + default: + boom("Input line cannot be parsed",lineno); + break; + } + + } + + if(fpo){ + fpo=close_as_svg(tri, fpo); + } + + +#ifdef DBG_LOOP + tri = trinfo_clear(tri); + ok = 1; + } +#endif /* DBG_LOOP */ + + fclose(fpi); + tri = trinfo_release(tri); + free(infile); + + exit(EXIT_SUCCESS); +} +#endif /* TEST */ + +#ifdef __cplusplus +} +#endif diff --git a/src/extension/internal/text_reassemble.h b/src/extension/internal/text_reassemble.h new file mode 100644 index 000000000..04c63940f --- /dev/null +++ b/src/extension/internal/text_reassemble.h @@ -0,0 +1,248 @@ +/* text_reassemble.h +version 0.0.2 2012-12-07 +Copyright 2012, Caltech and David Mathog + +See text_reassemble.c for notes + +*/ + +#ifdef __cplusplus +extern "C" { +#endif + + +#include <stdlib.h> +#include <stdio.h> +#include <math.h> +#include <stdint.h> +#include <ctype.h> +#include <fontconfig/fontconfig.h> +#include <ft2build.h> +#include <iconv.h> +#include FT_FREETYPE_H +#include FT_GLYPH_H + +#define TEREMIN(A,B) (A < B ? A : B) +#define TEREMAX(A,B) (A > B ? A : B) + +#ifndef M_PI +# define M_PI 3.14159265358979323846 /* pi */ +#endif +#define ALLOCINFO_CHUNK 32 +#define ALLOCOUT_CHUNK 8192 +#define TRPRINT trinfo_append_out + +/* text alignment types */ +#define ALILEFT 0x01 +#define ALICENTER 0x02 +#define ALIRIGHT 0x04 +#define ALIHORI 0x07 +#define ALITOP 0x08 +#define ALIBASE 0x10 +#define ALIBOT 0x20 +#define ALIVERT 0x38 + +/* language direction types */ +#define LDIR_LR 0x00 +#define LDIR_RL 0x01 +#define LDIR_TB 0x02 + +/* Flags */ +#define TR_EMFBOT 0x01 /* use an approximation compatible with EMF file's "BOTTOM" text orientation, which is not the "bottom" for Freetype fonts */ + +/* complex classification types + TR_TEXT simple text object + TR_LINE linear assembly of TR_TEXTS + TR_PARA_UJ sequential assembly of TR_LINE into a paragraph, with unknown justification properties + TR_PARA_LJ ..., left justified + TR_PARA_CJ ..., center justified + TR_PARA_RJ ..., right justified +*/ +enum tr_classes {TR_TEXT,TR_LINE,TR_PARA_UJ,TR_PARA_LJ,TR_PARA_CJ,TR_PARA_RJ}; + +/* Fontinfo structure, values related to fonts */ +typedef struct { + FT_Face face; /* font face structures (FT_FACE is a pointer!) */ + uint8_t *file; /* pointers to font paths to files */ + uint8_t *fname; /* pointers to font names */ + FcPattern *fpat; /* must hang onto this or faces operations break */ + double spcadv; /* advance equal to a space, in points */ + double fsize; /* face size in points */ +} FNT_SPECS; + +typedef struct { + FT_Library library; /* Fontconfig handle */ + FNT_SPECS *fonts; /* Array of fontinfo structures */ + int space; /* storage slots allocated */ + int used; /* storage slots in use */ +} FT_INFO; + +typedef struct { + uint8_t *string; /* UTF-8 text */ + double ori; /* Orientation, angle of characters with respect to baseline in degrees */ + double fs; /* font size of text */ + double x; /* x coord relative to tri x,y, in points */ + double y; /* y coord */ + double boff; /* Y LL corner - boff finds baseline */ + double vadvance; /* Line spacing typically 1.25 or 1.2, only set on the first text + element in a complex */ + uint32_t color; /* RGBA */ + int taln; /* text alignment with respect to x,y */ + int ldir; /* language diretion LDIR_* */ + int italics; /* italics, as in FontConfig */ + int weight; /* weight, as in FontConfig */ + int condensed; /* condensed, as in FontConfig */ + int co; /* condensed override, if set Font name included narrow */ + int rt_tidx; /* index of rectangle that contains it */ + int fi_idx; /* index of the font it uses */ +} TCHUNK_SPECS; + +/* Text Info/Position Info structure, values related to text properties and string geometry + Coordinates here are INTERNAL, after offset/rotate using values in TR_INFO. +*/ +typedef struct { + TCHUNK_SPECS *chunks; /* text chunks */ + int space; /* storage slots allocated */ + int used; /* storage slots in use */ +} TP_INFO; + +/* Bounding Rectangle(s) structure + Coordinates here are INTERNAL, after offset/rotate using values in TR_INFO. +*/ +typedef struct { + double xll; /* x rectangle lower left corner */ + double yll; /* y " */ + double xur; /* x upper right corner */ + double yur; /* y " */ + double xbearing; /* x bearing of the leftmost character */ +} BRECT_SPECS; + +typedef struct { + BRECT_SPECS *rects; /* bounding rectangles */ + int space; /* storage slots allocated */ + int used; /* storage slots in use */ +} BR_INFO; + +typedef struct { + int *members; /* array of immediate children (for TR_PARA_* these are indicies + for TR_TEXT or TR_LINE complexes also in cxi. For TR_TEXT + and TR_LINE these are indices to the actual text in tpi.) */ + int space; /* storage slots allocated */ + int used; /* storage slots in use */ +} CHILD_SPECS; + +/* Complex info structure, values related to the assembly of complex text from smaller pieces */ +typedef struct { + int rt_cidx; /* index of rectangle that contains all members */ + enum tr_classes type; /* classification of the complex */ + CHILD_SPECS kids; /* immediate child nodes of this complex, for type TR_TEXT the + idx refers to the tpi data. otherwise, cxi data */ +} CX_SPECS; + +typedef struct { + CX_SPECS *cx; /* complexes */ + int space; /* storage slots allocated */ + int used; /* storage slots in use */ + int phase1; /* Number of complexes (lines + text fragments) entered in phase 1 */ + int lines; /* Number of lines in phase 1 */ + int paras; /* Number of complexes (paras) entered in phase 2 */ +} CX_INFO; + +/* Text reassemble, overall structure */ +typedef struct { + FT_INFO *fti; /* Font info storage */ + TP_INFO *tpi; /* Text Info/Position Info storage */ + BR_INFO *bri; /* Bounding Rectangle Info storage */ + CX_INFO *cxi; /* Complex Info storage */ + uint8_t *out; /* buffer to hold formatted output */ + double qe; /* quantization error in points. */ + double esc; /* escapement angle in DEGREES */ + double x; /* coordinates of first text, in points */ + double y; + int dirty; /* 1 if text records are loaded */ + int use_kern; /* 1 if kerning is used, 0 if not */ + int load_flags; /* FT_LOAD_NO_SCALE or FT_LOAD_TARGET_NORMAL */ + int kern_mode; /* FT_KERNING_DEFAULT, FT_KERNING_UNFITTED, or FT_KERNING_UNSCALED */ + int outspace; /* storage in output buffer allocated */ + int outused; /* storage in output buffer in use */ +} TR_INFO; + +/* padding added to rectangles before overlap test */ +typedef struct { + double up; /* to top */ + double down; /* to bottom */ + double left; /* to left */ + double right; /* to right */ +} RT_PAD; + +/* iconv() has a funny cast on some older systems, on most recent ones + it is just char **. This tries to work around the issue. If you build this + on another funky system this code may need to be modified, or define ICONV_CAST + on the compile line(but it may be tricky). +*/ +#ifdef SOL8 +#define ICONV_CAST (const char **) +#endif //SOL8 +#if !defined(ICONV_CAST) +#define ICONV_CAST (char **) +#endif //ICONV_CAST + +/* Prototypes */ +int TR_findcasesub(char *string, char *sub); +int TR_getadvance(FNT_SPECS *fsp, uint32_t wc, uint32_t pc, int load_flags, int kern_mode, int *ymin, int *ymax); +int TR_getkern2(FNT_SPECS *fsp, uint32_t wc, uint32_t pc, int kern_mode); +int TR_kern_gap(FNT_SPECS *fsp, TCHUNK_SPECS *tsp, TCHUNK_SPECS *ptsp, int kern_mode); +void TR_rt_pad_set(RT_PAD *rt_pad, double up, double down, double left, double right); +double TR_baseline(TR_INFO *tri, int src, double *AscMax, double *DscMax); +int TR_check_set_vadvance(TR_INFO *tri, int src, int lines); +int TR_layout_analyze(TR_INFO *tri); +void TR_layout_2_svg(TR_INFO *tri); +int TR_weight_FC_to_SVG(int weight); + +FT_INFO *ftinfo_init(void); +int ftinfo_make_insertable(FT_INFO *fti); +int ftinfo_insert(FT_INFO *fti, FNT_SPECS *fsp); +FT_INFO *ftinfo_release(FT_INFO *fti); +FT_INFO *ftinfo_clear(FT_INFO *fti); + +int csp_make_insertable(CHILD_SPECS *csp); +int csp_insert(CHILD_SPECS *csp, int src); +int csp_merge(CHILD_SPECS *dst, CHILD_SPECS *src); +void csp_release(CHILD_SPECS *csp); +#define csp_clear csp_release /* since the CHILD_SPECS area itself is not deleted, clear == reset */ + +CX_INFO *cxinfo_init(void); +int cxinfo_make_insertable(CX_INFO *cxi); +int cxinfo_insert(CX_INFO *cxi, int src, int src_rt_idx, enum tr_classes type); +int cxinfo_append(CX_INFO *cxi, int src, enum tr_classes type); +int cxinfo_merge(CX_INFO *cxi, int dst, int src, enum tr_classes type); +CX_INFO *cxinfo_release(CX_INFO *cxi); +void cxinfo_dump(TR_INFO *tri); + +TP_INFO *tpinfo_init(void); +int tpinfo_make_insertable(TP_INFO *tpi); +int tpinfo_insert(TP_INFO *tpi, TCHUNK_SPECS *tsp); +TP_INFO *tpinfo_release(TP_INFO *tpi); + +BR_INFO *brinfo_init(void); +int brinfo_make_insertable(BR_INFO *bri); +int brinfo_insert(BR_INFO *bri, BRECT_SPECS *element); +int brinfo_merge(BR_INFO *bri, int dst, int src); +enum tr_classes + brinfo_pp_alignment(BR_INFO *bri, int dst, int src, double slop, enum tr_classes type); +int brinfo_overlap(BR_INFO *bri, int dst, int src, RT_PAD *rp_dst, RT_PAD *rp_src); +BR_INFO *brinfo_release(BR_INFO *bri); + +TR_INFO *trinfo_init(TR_INFO *tri); +TR_INFO *trinfo_release(TR_INFO *tri); +TR_INFO *trinfo_release_except_FC(TR_INFO *tri); +TR_INFO *trinfo_clear(TR_INFO *tri); +int trinfo_load_fontname(TR_INFO *tri, uint8_t *fontname, TCHUNK_SPECS *tsp); +int trinfo_load_qe(TR_INFO *tri, double qe); +int trinfo_load_ft_opts(TR_INFO *tri, int use_kern, int load_flags, int kern_mode); +int trinfo_load_textrec(TR_INFO *tri, TCHUNK_SPECS *tsp, double escapement, int flags); +int trinfo_append_out(TR_INFO *tri, char *src); + +#ifdef __cplusplus +} +#endif diff --git a/src/extension/internal/uemf.c b/src/extension/internal/uemf.c index 785bb475e..39d44555c 100644 --- a/src/extension/internal/uemf.c +++ b/src/extension/internal/uemf.c @@ -14,8 +14,8 @@ /* File: uemf.c -Version: 0.0.10 -Date: 28-NOV-2012 +Version: 0.0.11 +Date: 07-DEC-2012 Author: David Mathog, Biology Division, Caltech email: mathog@caltech.edu Copyright: 2012 David Mathog and California Institute of Technology (Caltech) @@ -101,17 +101,6 @@ definitions are not needed in end user code, so they are here rather than in uem ((B *) A)->cbBitsMask = 0;\ } -/* iconv() has a funny cast on some older systems, on most recent ones - it is just char **. This tries to work around the issue. If you build this - on another funky system this code may need to be modified, or define ICONV_CAST - on the compile line(but it may be tricky). -*/ -#ifdef SOL8 -#define ICONV_CAST (const char **) -#endif //SOL8 -#if !defined(ICONV_CAST) -#define ICONV_CAST (char **) -#endif //ICONV_CAST //! @endcond /* ********************************************************************************************** @@ -134,59 +123,6 @@ int memprobe( } /** - \brief Dump a UTF8 string. Not for use in production code. - \param src string to examine -*/ -void wchar8show( - const char *src - ){ - printf("char show\n"); - size_t srclen = 0; - while(*src){ printf("%d %d %x\n",(int) srclen,*src,*src); srclen++; src++; } -} - -/** - \brief Dump a UTF16 string. Not for use in production code. - \param src string to examine -*/ -void wchar16show( - const uint16_t *src - ){ - printf("uint16_t show\n"); - size_t srclen = 0; - while(*src){ printf("%d %d %x\n",(int) srclen,*src,*src); srclen++; src++; } -} - -/** - \brief Dump a UTF32 string. Not for use in production code. -*/ -void wchar32show( - const uint32_t *src - ){ - printf("uint32_t show\n"); - size_t srclen = 0; - while(*src){ printf("%d %d %x\n",(int) srclen,*src,*src); srclen++; src++; } -} - -/** - \brief Dump a wchar_t string. Not for use in production code. - \param src string to examine -*/ -void wchartshow( - const wchar_t *src - ){ - uint32_t val; - printf("wchar_t show\n"); - size_t srclen = 0; - while(*src){ - val = *src; // because *src is wchar_t is not strictly an integer type, can cause warnings on next line - printf("%d %d %x\n",(int) srclen,val,val); - srclen++; - src++; - } -} - -/** \brief Dump the eht structure. Not for use in production code. \param string Text to output before dumping eht structure \param handle Handle @@ -212,350 +148,11 @@ void dumpeht( } /* ********************************************************************************************** -These functions are used for character type conversions, Image conversions, and other -utility operations +These functions are used for Image conversions and other +utility operations. Character type conversions are in uemf_utf.c *********************************************************************************************** */ /** - \brief Find the number of (storage) characters in a 16 bit character string, not including terminator. - \param src string to examine -*/ -size_t wchar16len( - const uint16_t *src - ){ - size_t srclen = 0; - while(*src){ srclen++; src++; } - return(srclen); -} - -/** - \brief Find the number of (storage) characters in a 32 bit character string, not including terminator. - \param src string to examine -*/ -size_t wchar32len( - const uint32_t *src - ){ - size_t srclen = 0; - while(*src){ srclen++; src++; } - return(srclen); -} - -/** - \brief Strncpy for wchar16 (UTF16). - \param dst destination (already allocated) - \param src source - \param nchars number of characters to copy -*/ -void wchar16strncpy( - uint16_t *dst, - const uint16_t *src, - size_t nchars - ){ - for(;nchars;nchars--,dst++,src++){ - *dst = *src; - if(!*src)break; - } -} - -/** - \brief Fill the output string with N characters, if the input string is shorter than N, pad with nulls. - \param dst destination (already allocated) - \param src source - \param nchars number of characters to copy - -*/ -void wchar16strncpypad( - uint16_t *dst, - const uint16_t *src, - size_t nchars - ){ - for(;*src && nchars;nchars--,dst++,src++){ *dst = *src; } - for(;nchars;nchars--,dst++){ *dst = 0; } // Pad the remainder -} - -/* For the following converstion functions, remember that iconv() modifies ALL of its parameters, - so save a pointer to the destination buffer!!!! - It isn't clear that terminators are being - copied properly, so be sure allocated space is a bit larger and cleared. -*/ - -/** - \brief Convert a UTF32LE string to a UTF16LE string. - \returns pointer to new string or NULL if it fails - \param src wchar_t string to convert - \param max number of characters to convert, if 0, until terminator - \param len number of characters in new string, NOT including terminator -*/ -uint16_t *U_Utf32leToUtf16le( - const uint32_t *src, - size_t max, - size_t *len - ){ - char *dst,*dst2; - size_t srclen,dstlen,status; - - if(max){ srclen = 4*max; } - else { srclen = 4 + 4*wchar32len(src); } //include terminator, length in BYTES - - dstlen = 2 + srclen; // this will always work, but may waste space - dst2 = dst = calloc(dstlen,1); // so there will be at least one terminator - if(!dst)return(NULL); - iconv_t conv = iconv_open("UTF-16LE", "UTF-32LE"); - status = iconv(conv, ICONV_CAST &src, &srclen, &dst, &dstlen); - iconv_close(conv); - if(status == (size_t) -1)return(NULL); - if(len)*len=wchar16len((uint16_t *)dst2); - return((uint16_t *)dst2); -} - -/** - \brief Convert a UTF16LE string to a UTF32LE string. - \return pointer to new string or NULL if it fails - \param src UTF16LE string to convert - \param max number of characters to convert, if 0, until terminator - \param len number of characters in new string, NOT including terminator -*/ -uint32_t *U_Utf16leToUtf32le( - const uint16_t *src, - size_t max, - size_t *len - ){ - char *dst,*dst2; - char *src2 = (char *) src; - size_t srclen,dstlen,status; - if(max){ srclen = 2*max; } - else { srclen = 2*wchar16len(src)+2; } // include terminator, length in BYTES - dstlen = 2*(2 + srclen); // This should always work - dst2 = dst = calloc(dstlen,1); - if(!dst)return(NULL); - iconv_t conv = iconv_open("UTF-32LE", "UTF-16LE"); - if ( conv == (iconv_t)-1)return(NULL); - status = iconv(conv, ICONV_CAST &src2, &srclen, &dst, &dstlen); - iconv_close(conv); - if(status == (size_t) -1)return(NULL); - if(len)*len=wchar32len((uint32_t *)dst2); - return((uint32_t *) dst2); -} - -/** - \brief Convert a Latin1 string to a UTF32LE string. - \return pointer to new string or NULL if it fails - \param src Latin1 string to convert - \param max number of characters to convert, if 0, until terminator - \param len number of characters in new string, NOT including terminator - - - U_EMR_EXTTEXTOUTA records are "8 bit ASCII". In theory that is ASCII in an 8 - bit character, but numerous applications store Latin1 in them, and some - _may_ store UTF-8 in them. Since very vew Latin1 strings are valid UTF-8 strings, - call U_Utf8ToUtf32le first, and if it fails, then call this function. -*/ -uint32_t *U_Latin1ToUtf32le( - const char *src, - size_t max, - size_t *len - ){ - char *dst,*dst2; - char *src2 = (char *) src; - size_t srclen,dstlen,status; - if(max){ srclen = max; } - else { srclen = strlen(src)+1; } // include terminator, length in BYTES - dstlen = sizeof(uint32_t)*(1 + srclen); // This should always work but might waste some space - dst2 = dst = calloc(dstlen,1); - if(!dst)return(NULL); - iconv_t conv = iconv_open("UTF-32LE", "LATIN1"); - if ( conv == (iconv_t) -1)return(NULL); - status = iconv(conv, ICONV_CAST &src2, &srclen, &dst, &dstlen); - iconv_close(conv); - if(status == (size_t) -1)return(NULL); - if(len)*len=wchar32len((uint32_t *)dst2); - return((uint32_t *) dst2); -} - -/** - \brief Convert a UTF8 string to a UTF32LE string. - \return pointer to new string or NULL if it fails - \param src UTF8 string to convert - \param max number of characters to convert, if 0, until terminator - \param len number of characters in new string, NOT including terminator -*/ -uint32_t *U_Utf8ToUtf32le( - const char *src, - size_t max, - size_t *len - ){ - char *dst,*dst2; - char *src2 = (char *) src; - size_t srclen,dstlen,status; - if(max){ srclen = max; } - else { srclen = strlen(src)+1; } // include terminator, length in BYTES - dstlen = sizeof(uint32_t)*(1 + srclen); // This should always work but might waste some space - dst2 = dst = calloc(dstlen,1); - if(!dst)return(NULL); - iconv_t conv = iconv_open("UTF-32LE", "UTF-8"); - if ( conv == (iconv_t) -1)return(NULL); - status = iconv(conv, ICONV_CAST &src2, &srclen, &dst, &dstlen); - iconv_close(conv); - if(status == (size_t) -1)return(NULL); - if(len)*len=wchar32len((uint32_t *)dst2); - return((uint32_t *) dst2); -} - -/** - \brief Convert a UTF32LE string to a UTF8 string. - \return pointer to new string or NULL if it fails - \param src wchar_t string to convert - \param max number of characters to convert, if 0, until terminator - \param len number of characters in new string, NOT including terminator -*/ -char *U_Utf32leToUtf8( - const uint32_t *src, - size_t max, - size_t *len - ){ - char *dst,*dst2; - char *src2 = (char *) src; - size_t srclen,dstlen,status; - if(max){ srclen = 4*max; } - else { srclen = 4*(1 + wchar32len(src)); } //include terminator, length in BYTES - dstlen = 1 + srclen; // This should always work but might waste some space - dst2 = dst = calloc(dstlen,1); - if(!dst)return(NULL); - iconv_t conv = iconv_open("UTF-8", "UTF-32LE"); - if ( conv == (iconv_t)-1)return(NULL); - status = iconv(conv, ICONV_CAST &src2, &srclen, &dst, &dstlen); - iconv_close(conv); - if(status == (size_t) -1)return(NULL); - if(len)*len=strlen(dst2); - return(dst2); -} - -/** - \brief Convert a UTF-8 string to a UTF16-LE string. - \return pointer to new string or NULL if it fails - \param src UTF8 string to convert - \param max number of characters to convert, if 0, until terminator - \param len number of characters in new string, NOT including terminator -*/ -uint16_t *U_Utf8ToUtf16le( - const char *src, - size_t max, - size_t *len - ){ - char *dst,*dst2; - size_t srclen,dstlen,status; - iconv_t conv; - - if(max){ srclen = max; } - else { srclen = strlen(src)+1; } // include terminator, length in BYTES - dstlen = 2 * (1 + srclen); // this will always work, but may waste space - dst2 = dst =calloc(dstlen,1); // so there will always be a terminator - if(!dst)return(NULL); - conv = iconv_open("UTF-16LE", "UTF-8"); - if (conv == (iconv_t) -1)return(NULL); - status = iconv(conv, ICONV_CAST &src, &srclen, &dst, &dstlen); - iconv_close(conv); - if(status == (size_t) -1)return(NULL); - if(len)*len=wchar16len((uint16_t *)dst2); - return((uint16_t *)dst2); -} - -/** - \brief Convert a UTF16LE string to a UTF8 string. - \return pointer to new UTF8 string or NULL if it fails - \param src UTF16LE string to convert - \param max number of characters to convert, if 0, until terminator - \param len number of characters in new string, NOT including terminator -*/ -char *U_Utf16leToUtf8( - const uint16_t *src, - size_t max, - size_t *len - ){ - char *dst, *dst2; - char *ret=NULL; - size_t srclen,dstlen,status; - if(max){ srclen = 2*max; } - else { srclen = 2*(1 +wchar16len(src)); } //include terminator, length in BYTES - dstlen = 1 + 2*srclen; // this will always work, but may waste space - // worst case is all glyphs (==max) need 4 UTF-8 encoded bytes + terminator. - dst2 = dst = (char *) calloc(dstlen,1); - if(!dst)return(NULL); - iconv_t conv = iconv_open("UTF-8", "UTF-16LE"); - status = iconv(conv, ICONV_CAST &src, &srclen, &dst, &dstlen); - iconv_close(conv); - if(status != (size_t) -1){ - if(len)*len=strlen(dst2); - ret=U_strdup(dst2); // make a string of exactly the right size - } - free(dst2); // free the one which was probably too big - return(ret); -} - -/** - \brief Put a single 16 bit character into UTF-16LE form. - - Used in conjunction with U_Utf16leEdit(), because the character - representation would otherwise be dependent on machine Endianness. - - \return UTF16LE representation of the character. - \param src 16 bit character - -*/ -uint16_t U_Utf16le(const uint16_t src){ - uint16_t dst=src; -#if U_BYTE_SWAP - U_swap2(&dst,1); -#endif - return(dst); -} - -/** - \brief Single character replacement in a UTF-16LE string. - - Used solely for the Description field which contains - embedded nulls, which makes it difficult to manipulate. Use some other character and then swap it. - - \return number of substitutions, or -1 if src is not defined - \param src UTF16LE string to edit - \param find character to replace - \param replace replacestitute character - -*/ -int U_Utf16leEdit( - uint16_t *src, - uint16_t find, - uint16_t replace - ){ - int count=0; - if(!src)return(-1); - while(*src){ - if(*src == find){ *src = replace; count++; } - src++; - } - return(count); -} - -/** - \brief strdup for when strict C99 compliance is enforced - \returns duplicate string or NULL on error - \param s string to duplicate -*/ -char *U_strdup(const char *s){ - char *news=NULL; - size_t slen; - if(s){ - slen = strlen(s) + 1; //include the terminator! - news = malloc(slen); - if(news){ - memcpy(news,s,slen); - } - } - return(news); - -} - -/** \brief Make up an approximate dx array to pass to emrtext_set(), based on character height and weight. Take abs. value of character height, get width by multiplying by 0.6, and correct weight @@ -601,132 +198,132 @@ uint32_t emr_properties(uint32_t type){ if(!table){ table = (uint32_t *) malloc(sizeof(uint32_t)*(1 + U_EMR_MAX)); if(!table)return(result); - // 0x40 0x20 0x10 0x08 0x04 0x02 0x01 - // Path properties (U_DRAW_*) ALTERS ONLYTO VISIBLE - // PATH FORCE CLOSED NOTEMPTY + // 0x80 0x40 0x20 0x10 0x08 0x04 0x02 0x01 + // Path properties (U_DRAW_*) TEXT ALTERS ONLYTO VISIBLE + // PATH FORCE CLOSED NOTEMPTY table[ 0] = 0x00; //!< Does not map to any EMR record - table[ 1] = 0x00; //!< U_EMRHEADER 0 0 0 0 0 0 0 - table[ 2] = 0x03; //!< U_EMRPOLYBEZIER 0 0 0 0 0 1 1 - table[ 3] = 0x07; //!< U_EMRPOLYGON 0 0 0 0 1 1 1 - table[ 4] = 0x03; //!< U_EMRPOLYLINE 0 0 0 0 0 1 1 - table[ 5] = 0x0B; //!< U_EMRPOLYBEZIERTO 0 0 0 1 0 1 1 - table[ 6] = 0x0B; //!< U_EMRPOLYLINETO 0 0 0 1 0 1 1 - table[ 7] = 0x03; //!< U_EMRPOLYPOLYLINE 0 0 0 0 0 1 1 - table[ 8] = 0x07; //!< U_EMRPOLYPOLYGON 0 0 0 0 1 1 1 - table[ 9] = 0x20; //!< U_EMRSETWINDOWEXTEX 0 1 0 0 0 0 0 - table[ 10] = 0x20; //!< U_EMRSETWINDOWORGEX 0 1 0 0 0 0 0 - table[ 11] = 0x20; //!< U_EMRSETVIEWPORTEXTEX 0 1 0 0 0 0 0 - table[ 12] = 0x20; //!< U_EMRSETVIEWPORTORGEX 0 1 0 0 0 0 0 - table[ 13] = 0x20; //!< U_EMRSETBRUSHORGEX 0 1 0 0 0 0 0 - table[ 14] = 0x02; //!< U_EMREOF 0 1 0 0 0 0 0 Force out any pending draw - table[ 15] = 0x02; //!< U_EMRSETPIXELV 0 0 0 0 0 1 0 - table[ 16] = 0x20; //!< U_EMRSETMAPPERFLAGS 0 1 0 0 0 0 0 - table[ 17] = 0x20; //!< U_EMRSETMAPMODE 0 1 0 0 0 0 0 - table[ 18] = 0x20; //!< U_EMRSETBKMODE 0 1 0 0 0 0 0 - table[ 19] = 0x20; //!< U_EMRSETPOLYFILLMODE 0 1 0 0 0 0 0 - table[ 20] = 0x20; //!< U_EMRSETROP2 0 1 0 0 0 0 0 - table[ 21] = 0x20; //!< U_EMRSETSTRETCHBLTMODE 0 1 0 0 0 0 0 - table[ 22] = 0x20; //!< U_EMRSETTEXTALIGN 0 1 0 0 0 0 0 - table[ 23] = 0x20; //!< U_EMRSETCOLORADJUSTMENT 0 1 0 0 0 0 0 - table[ 24] = 0x20; //!< U_EMRSETTEXTCOLOR 0 1 0 0 0 0 0 - table[ 25] = 0x20; //!< U_EMRSETBKCOLOR 0 1 0 0 0 0 0 - table[ 26] = 0x20; //!< U_EMROFFSETCLIPRGN 0 1 0 0 0 0 0 - table[ 27] = 0x09; //!< U_EMRMOVETOEX 0 0 0 1 0 0 1 - table[ 28] = 0x20; //!< U_EMRSETMETARGN 0 1 0 0 0 0 0 - table[ 29] = 0x20; //!< U_EMREXCLUDECLIPRECT 0 1 0 0 0 0 0 - table[ 30] = 0x20; //!< U_EMRINTERSECTCLIPRECT 0 1 0 0 0 0 0 - table[ 31] = 0x20; //!< U_EMRSCALEVIEWPORTEXTEX 0 1 0 0 0 0 0 - table[ 32] = 0x20; //!< U_EMRSCALEWINDOWEXTEX 0 1 0 0 0 0 0 - table[ 33] = 0x20; //!< U_EMRSAVEDC 0 1 0 0 0 0 0 - table[ 34] = 0x20; //!< U_EMRRESTOREDC 0 1 0 0 0 0 0 - table[ 35] = 0x20; //!< U_EMRSETWORLDTRANSFORM 0 1 0 0 0 0 0 - table[ 36] = 0x20; //!< U_EMRMODIFYWORLDTRANSFORM 0 1 0 0 0 0 0 - table[ 37] = 0x20; //!< U_EMRSELECTOBJECT 0 1 0 0 0 0 0 - table[ 38] = 0x20; //!< U_EMRCREATEPEN 0 1 0 0 0 0 0 - table[ 39] = 0x20; //!< U_EMRCREATEBRUSHINDIRECT 0 1 0 0 0 0 0 - table[ 40] = 0x20; //!< U_EMRDELETEOBJECT 0 1 0 0 0 0 0 - table[ 41] = 0x03; //!< U_EMRANGLEARC 0 0 0 0 0 1 1 - table[ 42] = 0x07; //!< U_EMRELLIPSE 0 0 0 0 1 1 1 - table[ 43] = 0x07; //!< U_EMRRECTANGLE 0 0 0 0 1 1 1 - table[ 44] = 0x07; //!< U_EMRROUNDRECT 0 0 0 0 1 1 1 - table[ 45] = 0x03; //!< U_EMRARC 0 0 0 0 0 1 1 - table[ 46] = 0x07; //!< U_EMRCHORD 0 0 0 0 1 1 1 - table[ 47] = 0x07; //!< U_EMRPIE 0 0 0 0 1 1 1 - table[ 48] = 0x20; //!< U_EMRSELECTPALETTE 0 1 0 0 0 0 0 - table[ 49] = 0x20; //!< U_EMRCREATEPALETTE 0 1 0 0 0 0 0 - table[ 50] = 0x20; //!< U_EMRSETPALETTEENTRIES 0 1 0 0 0 0 0 - table[ 51] = 0x20; //!< U_EMRRESIZEPALETTE 0 1 0 0 0 0 0 - table[ 52] = 0x20; //!< U_EMRREALIZEPALETTE 0 1 0 0 0 0 0 - table[ 53] = 0x02; //!< U_EMREXTFLOODFILL 0 0 0 0 0 1 0 - table[ 54] = 0x0B; //!< U_EMRLINETO 0 0 0 1 0 1 1 - table[ 55] = 0x0B; //!< U_EMRARCTO 0 0 0 1 0 1 1 - table[ 56] = 0x03; //!< U_EMRPOLYDRAW 0 0 0 0 0 1 1 - table[ 57] = 0x20; //!< U_EMRSETARCDIRECTION 0 1 0 0 0 0 0 - table[ 58] = 0x20; //!< U_EMRSETMITERLIMIT 0 1 0 0 0 0 0 - table[ 59] = 0x60; //!< U_EMRBEGINPATH 1 1 0 0 0 0 0 - table[ 60] = 0x00; //!< U_EMRENDPATH 0 0 0 0 0 0 0 - table[ 61] = 0x04; //!< U_EMRCLOSEFIGURE 0 0 0 0 1 0 0 - table[ 62] = 0x14; //!< U_EMRFILLPATH 0 0 1 0 1 0 0 - table[ 63] = 0x14; //!< U_EMRSTROKEANDFILLPATH 0 0 1 0 1 0 0 - table[ 64] = 0x10; //!< U_EMRSTROKEPATH 0 0 1 0 0 0 0 - table[ 65] = 0x20; //!< U_EMRFLATTENPATH 0 1 0 0 0 0 0 - table[ 66] = 0x20; //!< U_EMRWIDENPATH 0 1 0 0 0 0 0 - table[ 67] = 0x20; //!< U_EMRSELECTCLIPPATH 0 1 0 0 0 0 0 - table[ 68] = 0x20; //!< U_EMRABORTPATH 0 1 0 0 0 0 0 - table[ 69] = 0x20; //!< U_EMRUNDEF69 0 1 0 0 0 0 0 - table[ 70] = 0x00; //!< U_EMRCOMMENT 0 0 0 0 0 0 0 - table[ 71] = 0x02; //!< U_EMRFILLRGN 0 0 0 0 0 1 0 - table[ 72] = 0x02; //!< U_EMRFRAMERGN 0 0 0 0 0 1 0 - table[ 73] = 0x02; //!< U_EMRINVERTRGN 0 0 0 0 0 1 0 - table[ 74] = 0x02; //!< U_EMRPAINTRGN 0 0 0 0 0 1 0 - table[ 75] = 0x20; //!< U_EMREXTSELECTCLIPRGN 0 1 0 0 0 0 0 - table[ 76] = 0x02; //!< U_EMRBITBLT 0 0 0 0 0 1 0 - table[ 77] = 0x02; //!< U_EMRSTRETCHBLT 0 0 0 0 0 1 0 - table[ 78] = 0x02; //!< U_EMRMASKBLT 0 0 0 0 0 1 0 - table[ 79] = 0x02; //!< U_EMRPLGBLT 0 0 0 0 0 1 0 - table[ 80] = 0x20; //!< U_EMRSETDIBITSTODEVICE 0 1 0 0 0 0 0 - table[ 81] = 0x20; //!< U_EMRSTRETCHDIBITS 0 1 0 0 0 0 0 - table[ 82] = 0x20; //!< U_EMREXTCREATEFONTINDIRECTW 0 1 0 0 0 0 0 - table[ 83] = 0x02; //!< U_EMREXTTEXTOUTA 0 0 0 0 0 1 0 - table[ 84] = 0x02; //!< U_EMREXTTEXTOUTW 0 0 0 0 0 1 0 - table[ 85] = 0x03; //!< U_EMRPOLYBEZIER16 0 0 0 0 0 1 1 - table[ 86] = 0x03; //!< U_EMRPOLYGON16 0 0 0 0 0 1 1 - table[ 87] = 0x03; //!< U_EMRPOLYLINE16 0 0 0 0 0 1 1 - table[ 88] = 0x0B; //!< U_EMRPOLYBEZIERTO16 0 0 0 1 0 1 1 - table[ 89] = 0x0B; //!< U_EMRPOLYLINETO16 0 0 0 1 0 1 1 - table[ 90] = 0x03; //!< U_EMRPOLYPOLYLINE16 0 0 0 0 0 1 1 - table[ 91] = 0x07; //!< U_EMRPOLYPOLYGON16 0 0 0 0 1 1 1 - table[ 92] = 0x03; //!< U_EMRPOLYDRAW16 0 0 0 0 0 1 1 - table[ 93] = 0x00; //!< U_EMRCREATEMONOBRUSH 0 0 0 0 0 0 0 Not selected yet, so no change in drawing conditions - table[ 94] = 0x00; //!< U_EMRCREATEDIBPATTERNBRUSHPT 0 0 0 0 0 0 0 " - table[ 95] = 0x00; //!< U_EMREXTCREATEPEN 0 0 0 0 0 0 0 " - table[ 96] = 0x02; //!< U_EMRPOLYTEXTOUTA 0 0 0 0 0 1 0 - table[ 97] = 0x02; //!< U_EMRPOLYTEXTOUTW 0 0 0 0 0 1 0 - table[ 98] = 0x20; //!< U_EMRSETICMMODE 0 1 0 0 0 0 0 - table[ 99] = 0x20; //!< U_EMRCREATECOLORSPACE 0 1 0 0 0 0 0 - table[100] = 0x20; //!< U_EMRSETCOLORSPACE 0 1 0 0 0 0 0 - table[101] = 0x20; //!< U_EMRDELETECOLORSPACE 0 1 0 0 0 0 0 - table[102] = 0x20; //!< U_EMRGLSRECORD 0 1 0 0 0 0 0 - table[103] = 0x20; //!< U_EMRGLSBOUNDEDRECORD 0 1 0 0 0 0 0 - table[104] = 0x20; //!< U_EMRPIXELFORMAT 0 1 0 0 0 0 0 - table[105] = 0x20; //!< U_EMRDRAWESCAPE 0 1 0 0 0 0 0 - table[106] = 0x20; //!< U_EMREXTESCAPE 0 1 0 0 0 0 0 - table[107] = 0x20; //!< U_EMRUNDEF107 0 1 0 0 0 0 0 - table[108] = 0x02; //!< U_EMRSMALLTEXTOUT 0 0 0 0 0 1 0 - table[109] = 0x20; //!< U_EMRFORCEUFIMAPPING 0 1 0 0 0 0 0 - table[110] = 0x20; //!< U_EMRNAMEDESCAPE 0 1 0 0 0 0 0 - table[111] = 0x20; //!< U_EMRCOLORCORRECTPALETTE 0 1 0 0 0 0 0 - table[112] = 0x20; //!< U_EMRSETICMPROFILEA 0 1 0 0 0 0 0 - table[113] = 0x20; //!< U_EMRSETICMPROFILEW 0 1 0 0 0 0 0 - table[114] = 0x02; //!< U_EMRALPHABLEND 0 0 0 0 0 1 0 - table[115] = 0x20; //!< U_EMRSETLAYOUT 0 1 0 0 0 0 0 - table[116] = 0x02; //!< U_EMRTRANSPARENTBLT 0 0 0 0 0 1 0 - table[117] = 0x20; //!< U_EMRUNDEF117 0 1 0 0 0 0 0 - table[118] = 0x02; //!< U_EMRGRADIENTFILL 0 0 0 0 0 1 0 - table[119] = 0x20; //!< U_EMRSETLINKEDUFIS 0 1 0 0 0 0 0 - table[120] = 0x20; //!< U_EMRSETTEXTJUSTIFICATION 0 1 0 0 0 0 0 - table[121] = 0x20; //!< U_EMRCOLORMATCHTOTARGETW 0 1 0 0 0 0 0 - table[122] = 0x20; //!< U_EMRCREATECOLORSPACEW 0 1 0 0 0 0 0 + table[ 1] = 0x80; //!< U_EMRHEADER 1 0 0 0 0 0 0 0 + table[ 2] = 0x83; //!< U_EMRPOLYBEZIER 1 0 0 0 0 0 1 1 + table[ 3] = 0x87; //!< U_EMRPOLYGON 1 0 0 0 0 1 1 1 + table[ 4] = 0x83; //!< U_EMRPOLYLINE 1 0 0 0 0 0 1 1 + table[ 5] = 0x8B; //!< U_EMRPOLYBEZIERTO 1 0 0 0 1 0 1 1 + table[ 6] = 0x8B; //!< U_EMRPOLYLINETO 1 0 0 0 1 0 1 1 + table[ 7] = 0x83; //!< U_EMRPOLYPOLYLINE 1 0 0 0 0 0 1 1 + table[ 8] = 0x87; //!< U_EMRPOLYPOLYGON 1 0 0 0 0 1 1 1 + table[ 9] = 0xA0; //!< U_EMRSETWINDOWEXTEX 1 0 1 0 0 0 0 0 + table[ 10] = 0xA0; //!< U_EMRSETWINDOWORGEX 1 0 1 0 0 0 0 0 + table[ 11] = 0xA0; //!< U_EMRSETVIEWPORTEXTEX 1 0 1 0 0 0 0 0 + table[ 12] = 0xA0; //!< U_EMRSETVIEWPORTORGEX 1 0 1 0 0 0 0 0 + table[ 13] = 0xA0; //!< U_EMRSETBRUSHORGEX 1 0 1 0 0 0 0 0 + table[ 14] = 0x82; //!< U_EMREOF 1 0 1 0 0 0 0 0 Force out any pending draw + table[ 15] = 0x82; //!< U_EMRSETPIXELV 1 0 0 0 0 0 1 0 + table[ 16] = 0xA0; //!< U_EMRSETMAPPERFLAGS 1 0 1 0 0 0 0 0 + table[ 17] = 0xA0; //!< U_EMRSETMAPMODE 1 0 1 0 0 0 0 0 + table[ 18] = 0x20; //!< U_EMRSETBKMODE 0 0 1 0 0 0 0 0 + table[ 19] = 0xA0; //!< U_EMRSETPOLYFILLMODE 1 0 1 0 0 0 0 0 + table[ 20] = 0xA0; //!< U_EMRSETROP2 1 0 1 0 0 0 0 0 + table[ 21] = 0xA0; //!< U_EMRSETSTRETCHBLTMODE 1 0 1 0 0 0 0 0 + table[ 22] = 0x20; //!< U_EMRSETTEXTALIGN 0 0 1 0 0 0 0 0 + table[ 23] = 0xA0; //!< U_EMRSETCOLORADJUSTMENT 1 0 1 0 0 0 0 0 + table[ 24] = 0x20; //!< U_EMRSETTEXTCOLOR 0 0 1 0 0 0 0 0 + table[ 25] = 0x20; //!< U_EMRSETBKCOLOR 0 0 1 0 0 0 0 0 + table[ 26] = 0xA0; //!< U_EMROFFSETCLIPRGN 1 0 1 0 0 0 0 0 + table[ 27] = 0x89; //!< U_EMRMOVETOEX 1 0 0 0 1 0 0 1 + table[ 28] = 0xA0; //!< U_EMRSETMETARGN 1 0 1 0 0 0 0 0 + table[ 29] = 0xA0; //!< U_EMREXCLUDECLIPRECT 1 0 1 0 0 0 0 0 + table[ 30] = 0xA0; //!< U_EMRINTERSECTCLIPRECT 1 0 1 0 0 0 0 0 + table[ 31] = 0xA0; //!< U_EMRSCALEVIEWPORTEXTEX 1 0 1 0 0 0 0 0 + table[ 32] = 0xA0; //!< U_EMRSCALEWINDOWEXTEX 1 0 1 0 0 0 0 0 + table[ 33] = 0xA0; //!< U_EMRSAVEDC 1 0 1 0 0 0 0 0 + table[ 34] = 0xA0; //!< U_EMRRESTOREDC 1 0 1 0 0 0 0 0 + table[ 35] = 0xA0; //!< U_EMRSETWORLDTRANSFORM 1 0 1 0 0 0 0 0 + table[ 36] = 0xA0; //!< U_EMRMODIFYWORLDTRANSFORM 1 0 1 0 0 0 0 0 + table[ 37] = 0x20; //!< U_EMRSELECTOBJECT 0 0 1 0 0 0 0 0 + table[ 38] = 0x20; //!< U_EMRCREATEPEN 0 0 1 0 0 0 0 0 + table[ 39] = 0x20; //!< U_EMRCREATEBRUSHINDIRECT 0 0 1 0 0 0 0 0 + table[ 40] = 0x20; //!< U_EMRDELETEOBJECT 0 0 1 0 0 0 0 0 + table[ 41] = 0x83; //!< U_EMRANGLEARC 1 0 0 0 0 0 1 1 + table[ 42] = 0x87; //!< U_EMRELLIPSE 1 0 0 0 0 1 1 1 + table[ 43] = 0x87; //!< U_EMRRECTANGLE 1 0 0 0 0 1 1 1 + table[ 44] = 0x87; //!< U_EMRROUNDRECT 1 0 0 0 0 1 1 1 + table[ 45] = 0x83; //!< U_EMRARC 1 0 0 0 0 0 1 1 + table[ 46] = 0x87; //!< U_EMRCHORD 1 0 0 0 0 1 1 1 + table[ 47] = 0x87; //!< U_EMRPIE 1 0 0 0 0 1 1 1 + table[ 48] = 0xA0; //!< U_EMRSELECTPALETTE 1 0 1 0 0 0 0 0 + table[ 49] = 0xA0; //!< U_EMRCREATEPALETTE 1 0 1 0 0 0 0 0 + table[ 50] = 0xA0; //!< U_EMRSETPALETTEENTRIES 1 0 1 0 0 0 0 0 + table[ 51] = 0xA0; //!< U_EMRRESIZEPALETTE 1 0 1 0 0 0 0 0 + table[ 52] = 0xA0; //!< U_EMRREALIZEPALETTE 1 0 1 0 0 0 0 0 + table[ 53] = 0x82; //!< U_EMREXTFLOODFILL 1 0 0 0 0 0 1 0 + table[ 54] = 0x8B; //!< U_EMRLINETO 1 0 0 0 1 0 1 1 + table[ 55] = 0x8B; //!< U_EMRARCTO 1 0 0 0 1 0 1 1 + table[ 56] = 0x83; //!< U_EMRPOLYDRAW 1 0 0 0 0 0 1 1 + table[ 57] = 0xA0; //!< U_EMRSETARCDIRECTION 1 0 1 0 0 0 0 0 + table[ 58] = 0xA0; //!< U_EMRSETMITERLIMIT 1 0 1 0 0 0 0 0 + table[ 59] = 0xE0; //!< U_EMRBEGINPATH 1 1 1 0 0 0 0 0 + table[ 60] = 0x80; //!< U_EMRENDPATH 1 0 0 0 0 0 0 0 + table[ 61] = 0x84; //!< U_EMRCLOSEFIGURE 1 0 0 0 0 1 0 0 + table[ 62] = 0x94; //!< U_EMRFILLPATH 1 0 0 1 0 1 0 0 + table[ 63] = 0x94; //!< U_EMRSTROKEANDFILLPATH 1 0 0 1 0 1 0 0 + table[ 64] = 0x90; //!< U_EMRSTROKEPATH 1 0 0 1 0 0 0 0 + table[ 65] = 0xA0; //!< U_EMRFLATTENPATH 1 0 1 0 0 0 0 0 + table[ 66] = 0xA0; //!< U_EMRWIDENPATH 1 0 1 0 0 0 0 0 + table[ 67] = 0xA0; //!< U_EMRSELECTCLIPPATH 1 0 1 0 0 0 0 0 + table[ 68] = 0xA0; //!< U_EMRABORTPATH 1 0 1 0 0 0 0 0 + table[ 69] = 0xA0; //!< U_EMRUNDEF69 1 0 1 0 0 0 0 0 + table[ 70] = 0x00; //!< U_EMRCOMMENT 0 0 0 0 0 0 0 0 + table[ 71] = 0x82; //!< U_EMRFILLRGN 1 0 0 0 0 0 1 0 + table[ 72] = 0x82; //!< U_EMRFRAMERGN 1 0 0 0 0 0 1 0 + table[ 73] = 0x82; //!< U_EMRINVERTRGN 1 0 0 0 0 0 1 0 + table[ 74] = 0x82; //!< U_EMRPAINTRGN 1 0 0 0 0 0 1 0 + table[ 75] = 0xA0; //!< U_EMREXTSELECTCLIPRGN 1 0 1 0 0 0 0 0 + table[ 76] = 0x82; //!< U_EMRBITBLT 1 0 0 0 0 0 1 0 + table[ 77] = 0x82; //!< U_EMRSTRETCHBLT 1 0 0 0 0 0 1 0 + table[ 78] = 0x82; //!< U_EMRMASKBLT 1 0 0 0 0 0 1 0 + table[ 79] = 0x82; //!< U_EMRPLGBLT 1 0 0 0 0 0 1 0 + table[ 80] = 0xA0; //!< U_EMRSETDIBITSTODEVICE 1 0 1 0 0 0 0 0 + table[ 81] = 0xA0; //!< U_EMRSTRETCHDIBITS 1 0 1 0 0 0 0 0 + table[ 82] = 0x20; //!< U_EMREXTCREATEFONTINDIRECTW 0 0 1 0 0 0 0 0 + table[ 83] = 0x02; //!< U_EMREXTTEXTOUTA 0 0 0 0 0 0 1 0 + table[ 84] = 0x02; //!< U_EMREXTTEXTOUTW 0 0 0 0 0 0 1 0 + table[ 85] = 0x83; //!< U_EMRPOLYBEZIER16 1 0 0 0 0 0 1 1 + table[ 86] = 0x83; //!< U_EMRPOLYGON16 1 0 0 0 0 0 1 1 + table[ 87] = 0x83; //!< U_EMRPOLYLINE16 1 0 0 0 0 0 1 1 + table[ 88] = 0x8B; //!< U_EMRPOLYBEZIERTO16 1 0 0 0 1 0 1 1 + table[ 89] = 0x8B; //!< U_EMRPOLYLINETO16 1 0 0 0 1 0 1 1 + table[ 90] = 0x83; //!< U_EMRPOLYPOLYLINE16 1 0 0 0 0 0 1 1 + table[ 91] = 0x87; //!< U_EMRPOLYPOLYGON16 1 0 0 0 0 1 1 1 + table[ 92] = 0x83; //!< U_EMRPOLYDRAW16 1 0 0 0 0 0 1 1 + table[ 93] = 0x80; //!< U_EMRCREATEMONOBRUSH 1 0 0 0 0 0 0 0 Not selected yet, so no change in drawing conditions + table[ 94] = 0x80; //!< U_EMRCREATEDIBPATTERNBRUSHPT 1 0 0 0 0 0 0 0 " + table[ 95] = 0x00; //!< U_EMREXTCREATEPEN 0 0 0 0 0 0 0 0 " + table[ 96] = 0x02; //!< U_EMRPOLYTEXTOUTA 0 0 0 0 0 0 1 0 + table[ 97] = 0x02; //!< U_EMRPOLYTEXTOUTW 0 0 0 0 0 0 1 0 + table[ 98] = 0xA0; //!< U_EMRSETICMMODE 1 0 1 0 0 0 0 0 + table[ 99] = 0xA0; //!< U_EMRCREATECOLORSPACE 1 0 1 0 0 0 0 0 + table[100] = 0xA0; //!< U_EMRSETCOLORSPACE 1 0 1 0 0 0 0 0 + table[101] = 0xA0; //!< U_EMRDELETECOLORSPACE 1 0 1 0 0 0 0 0 + table[102] = 0xA0; //!< U_EMRGLSRECORD 1 0 1 0 0 0 0 0 + table[103] = 0xA0; //!< U_EMRGLSBOUNDEDRECORD 1 0 1 0 0 0 0 0 + table[104] = 0xA0; //!< U_EMRPIXELFORMAT 1 0 1 0 0 0 0 0 + table[105] = 0xA0; //!< U_EMRDRAWESCAPE 1 0 1 0 0 0 0 0 + table[106] = 0xA0; //!< U_EMREXTESCAPE 1 0 1 0 0 0 0 0 + table[107] = 0xA0; //!< U_EMRUNDEF107 1 0 1 0 0 0 0 0 + table[108] = 0x02; //!< U_EMRSMALLTEXTOUT 0 0 0 0 0 0 1 0 + table[109] = 0xA0; //!< U_EMRFORCEUFIMAPPING 1 0 1 0 0 0 0 0 + table[110] = 0xA0; //!< U_EMRNAMEDESCAPE 1 0 1 0 0 0 0 0 + table[111] = 0xA0; //!< U_EMRCOLORCORRECTPALETTE 1 0 1 0 0 0 0 0 + table[112] = 0xA0; //!< U_EMRSETICMPROFILEA 1 0 1 0 0 0 0 0 + table[113] = 0xA0; //!< U_EMRSETICMPROFILEW 1 0 1 0 0 0 0 0 + table[114] = 0x82; //!< U_EMRALPHABLEND 1 0 0 0 0 0 1 0 + table[115] = 0xA0; //!< U_EMRSETLAYOUT 1 0 1 0 0 0 0 0 + table[116] = 0x82; //!< U_EMRTRANSPARENTBLT 1 0 0 0 0 0 1 0 + table[117] = 0xA0; //!< U_EMRUNDEF117 1 0 1 0 0 0 0 0 + table[118] = 0x82; //!< U_EMRGRADIENTFILL 1 0 0 0 0 0 1 0 + table[119] = 0xA0; //!< U_EMRSETLINKEDUFIS 1 0 1 0 0 0 0 0 + table[120] = 0x20; //!< U_EMRSETTEXTJUSTIFICATION 0 0 1 0 0 0 0 0 + table[121] = 0xA0; //!< U_EMRCOLORMATCHTOTARGETW 1 0 1 0 0 0 0 0 + table[122] = 0xA0; //!< U_EMRCREATECOLORSPACEW 1 0 1 0 0 0 0 0 } result = table[type]; } diff --git a/src/extension/internal/uemf.h b/src/extension/internal/uemf.h index 776ad64d7..34823a8e9 100644 --- a/src/extension/internal/uemf.h +++ b/src/extension/internal/uemf.h @@ -13,8 +13,8 @@ /* File: uemf.h -Version: 0.0.9 -Date: 26-OCT-2012 +Version: 0.0.10 +Date: 04-DEC-2012 Author: David Mathog, Biology Division, Caltech email: mathog@caltech.edu Copyright: 2012 David Mathog and California Institute of Technology (Caltech) @@ -29,6 +29,7 @@ extern "C" { #include <stdint.h> #include <stdbool.h> +#include "uemf_utf.h" #include "uemf_endian.h" @@ -203,6 +204,7 @@ extern "C" { #define U_DRAW_FORCE 0x10 //!< Path MUST be drawn #define U_DRAW_ALTERS 0x20 //!< Alters draw parameters (pen, brush, coordinates...) #define U_DRAW_PATH 0x40 //!< An explicit path is being used (with a BEGIN and END) +#define U_DRAW_TEXT 0x80 //!< Current record forces all pending text to be drawn first. /** @} */ /** \defgroup U_EMRSETARCDIRECTION_Qualifiers ArcDirection Enumeration @@ -2678,21 +2680,7 @@ void wchartshow(const wchar_t *src); void dumpeht(char *string, unsigned int *handle, EMFHANDLES *eht); -size_t wchar16len(const uint16_t *src); -size_t wchar32len(const uint32_t *src); -void wchar16strncpy(uint16_t *dst, const uint16_t *src, size_t nchars); -void wchar16strncpypad(uint16_t *dst, const uint16_t *src, size_t nchars); -uint16_t *U_Utf8ToUtf16le(const char *src, size_t max, size_t *len); -uint32_t *U_Utf8ToUtf32le(const char *src, size_t max, size_t *len); -uint32_t *U_Latin1ToUtf32le(const char *src, size_t max, size_t *len); -uint16_t *U_Utf32leToUtf16le(const uint32_t *src, size_t max, size_t *len); -char *U_Utf32leToUtf8(const uint32_t *src, size_t max, size_t *len); -uint32_t *U_Utf16leToUtf32le(const uint16_t *src, size_t max, size_t *len); -char *U_Utf16leToUtf8(const uint16_t *src, size_t max, size_t *len); -uint16_t U_Utf16le(const uint16_t src); -int U_Utf16leEdit(uint16_t *src, uint16_t old, uint16_t sub); uint32_t *dx_set(int32_t height, uint32_t weight, uint32_t members); -char *U_strdup(const char *s); uint32_t emr_properties(uint32_t type); int emr_arc_points(PU_ENHMETARECORD record, int *f1, int f2, PU_PAIRF center, PU_PAIRF start, PU_PAIRF end, PU_PAIRF size); int RGBA_to_DIB( char **px, uint32_t *cbPx, PU_RGBQUAD *ct, int *numCt, diff --git a/src/extension/internal/uemf_utf.c b/src/extension/internal/uemf_utf.c new file mode 100644 index 000000000..028dcb966 --- /dev/null +++ b/src/extension/internal/uemf_utf.c @@ -0,0 +1,455 @@ +/** + @file uemf_utf.c Functions for manipulating UTF and various types of text. + + + Compile with "U_VALGRIND" defined defined to enable code which lets valgrind check each record for + uninitialized data. + + Compile with "SOL8" defined for Solaris 8 or 9 (Sparc). +*/ + +/* +File: uemf_utf.c +Version: 0.0.1 +Date: 04-DEC-2012 +Author: David Mathog, Biology Division, Caltech +email: mathog@caltech.edu +Copyright: 2012 David Mathog and California Institute of Technology (Caltech) +*/ + +#ifdef __cplusplus +extern "C" { +#endif + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <iconv.h> +#include <wchar.h> +#include <errno.h> +#include <string.h> +#include <limits.h> // for INT_MAX, INT_MIN +#include <math.h> // for U_ROUND() +#include "uemf_utf.h" + +/* ******************************************************************************************** */ + +//! @cond +/* iconv() has a funny cast on some older systems, on most recent ones + it is just char **. This tries to work around the issue. If you build this + on another funky system this code may need to be modified, or define ICONV_CAST + on the compile line(but it may be tricky). +*/ +#ifdef SOL8 +#define ICONV_CAST (const char **) +#endif //SOL8 +#if !defined(ICONV_CAST) +#define ICONV_CAST (char **) +#endif //ICONV_CAST +//! @endcond + +/* ********************************************************************************************** +These functions are used for development and debugging and should be be includied in production code. +*********************************************************************************************** */ + +/** + \brief Dump a UTF8 string. Not for use in production code. + \param src string to examine +*/ +void wchar8show( + const char *src + ){ + printf("char show\n"); + size_t srclen = 0; + while(*src){ printf("%d %d %x\n",(int) srclen,*src,*src); srclen++; src++; } +} + +/** + \brief Dump a UTF16 string. Not for use in production code. + \param src string to examine +*/ +void wchar16show( + const uint16_t *src + ){ + printf("uint16_t show\n"); + size_t srclen = 0; + while(*src){ printf("%d %d %x\n",(int) srclen,*src,*src); srclen++; src++; } +} + +/** + \brief Dump a UTF32 string. Not for use in production code. +*/ +void wchar32show( + const uint32_t *src + ){ + printf("uint32_t show\n"); + size_t srclen = 0; + while(*src){ printf("%d %d %x\n",(int) srclen,*src,*src); srclen++; src++; } +} + +/** + \brief Dump a wchar_t string. Not for use in production code. + \param src string to examine +*/ +void wchartshow( + const wchar_t *src + ){ + uint32_t val; + printf("wchar_t show\n"); + size_t srclen = 0; + while(*src){ + val = *src; // because *src is wchar_t is not strictly an integer type, can cause warnings on next line + printf("%d %d %x\n",(int) srclen,val,val); + srclen++; + src++; + } +} + +/* ********************************************************************************************** +These functions are used for character type conversions, Image conversions, and other +utility operations +*********************************************************************************************** */ + +/** + \brief Find the number of (storage) characters in a 16 bit character string, not including terminator. + \param src string to examine +*/ +size_t wchar16len( + const uint16_t *src + ){ + size_t srclen = 0; + while(*src){ srclen++; src++; } + return(srclen); +} + +/** + \brief Find the number of (storage) characters in a 32 bit character string, not including terminator. + \param src string to examine +*/ +size_t wchar32len( + const uint32_t *src + ){ + size_t srclen = 0; + while(*src){ srclen++; src++; } + return(srclen); +} + +/** + \brief Strncpy for wchar16 (UTF16). + \param dst destination (already allocated) + \param src source + \param nchars number of characters to copy +*/ +void wchar16strncpy( + uint16_t *dst, + const uint16_t *src, + size_t nchars + ){ + for(;nchars;nchars--,dst++,src++){ + *dst = *src; + if(!*src)break; + } +} + +/** + \brief Fill the output string with N characters, if the input string is shorter than N, pad with nulls. + \param dst destination (already allocated) + \param src source + \param nchars number of characters to copy + +*/ +void wchar16strncpypad( + uint16_t *dst, + const uint16_t *src, + size_t nchars + ){ + for(;*src && nchars;nchars--,dst++,src++){ *dst = *src; } + for(;nchars;nchars--,dst++){ *dst = 0; } // Pad the remainder +} + +/* For the following converstion functions, remember that iconv() modifies ALL of its parameters, + so save a pointer to the destination buffer!!!! + It isn't clear that terminators are being + copied properly, so be sure allocated space is a bit larger and cleared. +*/ + +/** + \brief Convert a UTF32LE string to a UTF16LE string. + \returns pointer to new string or NULL if it fails + \param src wchar_t string to convert + \param max number of characters to convert, if 0, until terminator + \param len number of characters in new string, NOT including terminator +*/ +uint16_t *U_Utf32leToUtf16le( + const uint32_t *src, + size_t max, + size_t *len + ){ + char *dst,*dst2; + size_t srclen,dstlen,status; + + if(max){ srclen = 4*max; } + else { srclen = 4 + 4*wchar32len(src); } //include terminator, length in BYTES + + dstlen = 2 + srclen; // this will always work, but may waste space + dst2 = dst = calloc(dstlen,1); // so there will be at least one terminator + if(!dst)return(NULL); + iconv_t conv = iconv_open("UTF-16LE", "UTF-32LE"); + status = iconv(conv, ICONV_CAST &src, &srclen, &dst, &dstlen); + iconv_close(conv); + if(status == (size_t) -1)return(NULL); + if(len)*len=wchar16len((uint16_t *)dst2); + return((uint16_t *)dst2); +} + +/** + \brief Convert a UTF16LE string to a UTF32LE string. + \return pointer to new string or NULL if it fails + \param src UTF16LE string to convert + \param max number of characters to convert, if 0, until terminator + \param len number of characters in new string, NOT including terminator +*/ +uint32_t *U_Utf16leToUtf32le( + const uint16_t *src, + size_t max, + size_t *len + ){ + char *dst,*dst2; + char *src2 = (char *) src; + size_t srclen,dstlen,status; + if(max){ srclen = 2*max; } + else { srclen = 2*wchar16len(src)+2; } // include terminator, length in BYTES + dstlen = 2*(2 + srclen); // This should always work + dst2 = dst = calloc(dstlen,1); + if(!dst)return(NULL); + iconv_t conv = iconv_open("UTF-32LE", "UTF-16LE"); + if ( conv == (iconv_t)-1)return(NULL); + status = iconv(conv, ICONV_CAST &src2, &srclen, &dst, &dstlen); + iconv_close(conv); + if(status == (size_t) -1)return(NULL); + if(len)*len=wchar32len((uint32_t *)dst2); + return((uint32_t *) dst2); +} + +/** + \brief Convert a Latin1 string to a UTF32LE string. + \return pointer to new string or NULL if it fails + \param src Latin1 string to convert + \param max number of characters to convert, if 0, until terminator + \param len number of characters in new string, NOT including terminator + + + U_EMR_EXTTEXTOUTA records are "8 bit ASCII". In theory that is ASCII in an 8 + bit character, but numerous applications store Latin1 in them, and some + _may_ store UTF-8 in them. Since very vew Latin1 strings are valid UTF-8 strings, + call U_Utf8ToUtf32le first, and if it fails, then call this function. +*/ +uint32_t *U_Latin1ToUtf32le( + const char *src, + size_t max, + size_t *len + ){ + char *dst,*dst2; + char *src2 = (char *) src; + size_t srclen,dstlen,status; + if(max){ srclen = max; } + else { srclen = strlen(src)+1; } // include terminator, length in BYTES + dstlen = sizeof(uint32_t)*(1 + srclen); // This should always work but might waste some space + dst2 = dst = calloc(dstlen,1); + if(!dst)return(NULL); + iconv_t conv = iconv_open("UTF-32LE", "LATIN1"); + if ( conv == (iconv_t) -1)return(NULL); + status = iconv(conv, ICONV_CAST &src2, &srclen, &dst, &dstlen); + iconv_close(conv); + if(status == (size_t) -1)return(NULL); + if(len)*len=wchar32len((uint32_t *)dst2); + return((uint32_t *) dst2); +} + +/** + \brief Convert a UTF8 string to a UTF32LE string. + \return pointer to new string or NULL if it fails + \param src UTF8 string to convert + \param max number of characters to convert, if 0, until terminator + \param len number of characters in new string, NOT including terminator +*/ +uint32_t *U_Utf8ToUtf32le( + const char *src, + size_t max, + size_t *len + ){ + char *dst,*dst2; + char *src2 = (char *) src; + size_t srclen,dstlen,status; + if(max){ srclen = max; } + else { srclen = strlen(src)+1; } // include terminator, length in BYTES + dstlen = sizeof(uint32_t)*(1 + srclen); // This should always work but might waste some space + dst2 = dst = calloc(dstlen,1); + if(!dst)return(NULL); + iconv_t conv = iconv_open("UTF-32LE", "UTF-8"); + if ( conv == (iconv_t) -1)return(NULL); + status = iconv(conv, ICONV_CAST &src2, &srclen, &dst, &dstlen); + iconv_close(conv); + if(status == (size_t) -1)return(NULL); + if(len)*len=wchar32len((uint32_t *)dst2); + return((uint32_t *) dst2); +} + +/** + \brief Convert a UTF32LE string to a UTF8 string. + \return pointer to new string or NULL if it fails + \param src wchar_t string to convert + \param max number of characters to convert, if 0, until terminator + \param len number of characters in new string, NOT including terminator +*/ +char *U_Utf32leToUtf8( + const uint32_t *src, + size_t max, + size_t *len + ){ + char *dst,*dst2; + char *src2 = (char *) src; + size_t srclen,dstlen,status; + if(max){ srclen = 4*max; } + else { srclen = 4*(1 + wchar32len(src)); } //include terminator, length in BYTES + dstlen = 1 + srclen; // This should always work but might waste some space + dst2 = dst = calloc(dstlen,1); + if(!dst)return(NULL); + iconv_t conv = iconv_open("UTF-8", "UTF-32LE"); + if ( conv == (iconv_t)-1)return(NULL); + status = iconv(conv, ICONV_CAST &src2, &srclen, &dst, &dstlen); + iconv_close(conv); + if(status == (size_t) -1)return(NULL); + if(len)*len=strlen(dst2); + return(dst2); +} + +/** + \brief Convert a UTF-8 string to a UTF16-LE string. + \return pointer to new string or NULL if it fails + \param src UTF8 string to convert + \param max number of characters to convert, if 0, until terminator + \param len number of characters in new string, NOT including terminator +*/ +uint16_t *U_Utf8ToUtf16le( + const char *src, + size_t max, + size_t *len + ){ + char *dst,*dst2; + size_t srclen,dstlen,status; + iconv_t conv; + + if(max){ srclen = max; } + else { srclen = strlen(src)+1; } // include terminator, length in BYTES + dstlen = 2 * (1 + srclen); // this will always work, but may waste space + dst2 = dst =calloc(dstlen,1); // so there will always be a terminator + if(!dst)return(NULL); + conv = iconv_open("UTF-16LE", "UTF-8"); + if (conv == (iconv_t) -1)return(NULL); + status = iconv(conv, ICONV_CAST &src, &srclen, &dst, &dstlen); + iconv_close(conv); + if(status == (size_t) -1)return(NULL); + if(len)*len=wchar16len((uint16_t *)dst2); + return((uint16_t *)dst2); +} + +/** + \brief Convert a UTF16LE string to a UTF8 string. + \return pointer to new UTF8 string or NULL if it fails + \param src UTF16LE string to convert + \param max number of characters to convert, if 0, until terminator + \param len number of characters in new string, NOT including terminator +*/ +char *U_Utf16leToUtf8( + const uint16_t *src, + size_t max, + size_t *len + ){ + char *dst, *dst2; + char *ret=NULL; + size_t srclen,dstlen,status; + if(max){ srclen = 2*max; } + else { srclen = 2*(1 +wchar16len(src)); } //include terminator, length in BYTES + dstlen = 1 + 2*srclen; // this will always work, but may waste space + // worst case is all glyphs (==max) need 4 UTF-8 encoded bytes + terminator. + dst2 = dst = (char *) calloc(dstlen,1); + if(!dst)return(NULL); + iconv_t conv = iconv_open("UTF-8", "UTF-16LE"); + status = iconv(conv, ICONV_CAST &src, &srclen, &dst, &dstlen); + iconv_close(conv); + if(status != (size_t) -1){ + if(len)*len=strlen(dst2); + ret=U_strdup(dst2); // make a string of exactly the right size + } + free(dst2); // free the one which was probably too big + return(ret); +} + +/** + \brief Put a single 16 bit character into UTF-16LE form. + + Used in conjunction with U_Utf16leEdit(), because the character + representation would otherwise be dependent on machine Endianness. + + \return UTF16LE representation of the character. + \param src 16 bit character + +*/ +uint16_t U_Utf16le(const uint16_t src){ + uint16_t dst=src; +#if U_BYTE_SWAP + U_swap2(&dst,1); +#endif + return(dst); +} + +/** + \brief Single character replacement in a UTF-16LE string. + + Used solely for the Description field which contains + embedded nulls, which makes it difficult to manipulate. Use some other character and then swap it. + + \return number of substitutions, or -1 if src is not defined + \param src UTF16LE string to edit + \param find character to replace + \param replace replacestitute character + +*/ +int U_Utf16leEdit( + uint16_t *src, + uint16_t find, + uint16_t replace + ){ + int count=0; + if(!src)return(-1); + while(*src){ + if(*src == find){ *src = replace; count++; } + src++; + } + return(count); +} + +/** + \brief strdup for when strict C99 compliance is enforced + \returns duplicate string or NULL on error + \param s string to duplicate +*/ +char *U_strdup(const char *s){ + char *news=NULL; + size_t slen; + if(s){ + slen = strlen(s) + 1; //include the terminator! + news = malloc(slen); + if(news){ + memcpy(news,s,slen); + } + } + return(news); + +} + + +#ifdef __cplusplus +} +#endif diff --git a/src/extension/internal/uemf_utf.h b/src/extension/internal/uemf_utf.h new file mode 100644 index 000000000..24ad37f36 --- /dev/null +++ b/src/extension/internal/uemf_utf.h @@ -0,0 +1,49 @@ +/** + @file uemf_utf.h for manipulating UTF and various types of text. + +*/ + +/* +File: uemf_utf.h +Version: 0.0.1 +Date: 04-DEC-2012 +Author: David Mathog, Biology Division, Caltech +email: mathog@caltech.edu +Copyright: 2012 David Mathog and California Institute of Technology (Caltech) +*/ + +#ifndef _UEMF_UTF_ +#define _UEMF_UTF_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include <stdint.h> +#include <stdbool.h> + +void wchar8show(const char *src); +void wchar16show(const uint16_t *src); +void wchar32show(const uint32_t *src); +void wchartshow(const wchar_t *src); + +size_t wchar16len(const uint16_t *src); +size_t wchar32len(const uint32_t *src); +void wchar16strncpy(uint16_t *dst, const uint16_t *src, size_t nchars); +void wchar16strncpypad(uint16_t *dst, const uint16_t *src, size_t nchars); +uint16_t *U_Utf8ToUtf16le( const char *src, size_t max, size_t *len ); +uint32_t *U_Utf8ToUtf32le( const char *src, size_t max, size_t *len ); +uint32_t *U_Latin1ToUtf32le( const char *src, size_t max, size_t *len ); +uint16_t *U_Utf32leToUtf16le( const uint32_t *src, size_t max, size_t *len ); +char *U_Utf32leToUtf8( const uint32_t *src, size_t max, size_t *len ); +uint32_t *U_Utf16leToUtf32le( const uint16_t *src, size_t max, size_t *len ); +char *U_Utf16leToUtf8( const uint16_t *src, size_t max, size_t *len ); +uint16_t U_Utf16le(const uint16_t src); +int U_Utf16leEdit( uint16_t *src, uint16_t find, uint16_t replace ); +char *U_strdup(const char *s); + +#ifdef __cplusplus +} +#endif + +#endif /* _UEMF_UTF_ */ |
