summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorDavid Mathog <>2012-12-13 04:10:54 +0000
committer~suv <suv-sf@users.sourceforge.net>2012-12-13 04:10:54 +0000
commit583d0895ad53ea5257099ab997527ed44649ff71 (patch)
treedb30691df44aedf61f097109912efe74353d798a /src
parentmerge from trunk (r11929) (diff)
downloadinkscape-583d0895ad53ea5257099ab997527ed44649ff71.tar.gz
inkscape-583d0895ad53ea5257099ab997527ed44649ff71.zip
preliminary release of the EMF import text reassembly feature.
(based on libTERE (TExt REassembly), not yet published) libTERE examines all the text in an EMF file, which consists only of little chunks in different formats, and attempts to reassemble it into an editable SVG text object, with color, different fonts and so forth. (only tested with English) (bzr r11668.1.45)
Diffstat (limited to 'src')
-rw-r--r--src/extension/internal/Makefile_insert4
-rw-r--r--src/extension/internal/emf-inout.cpp184
-rw-r--r--src/extension/internal/text_reassemble.c1948
-rw-r--r--src/extension/internal/text_reassemble.h248
-rw-r--r--src/extension/internal/uemf.c661
-rw-r--r--src/extension/internal/uemf.h20
-rw-r--r--src/extension/internal/uemf_utf.c455
-rw-r--r--src/extension/internal/uemf_utf.h49
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_ */