summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
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_ */