diff options
| author | David Mathog <> | 2013-06-19 17:20:33 +0000 |
|---|---|---|
| committer | ~suv <suv-sf@users.sourceforge.net> | 2013-06-19 17:20:33 +0000 |
| commit | d19db89e2e22d4e09c539bd42823d511aebb669f (patch) | |
| tree | f5322f76b16b0e4c97895ce2b805ea7375e44142 /src/extension/internal | |
| parent | merge from trunk (r12337) (diff) | |
| download | inkscape-d19db89e2e22d4e09c539bd42823d511aebb669f.tar.gz inkscape-d19db89e2e22d4e09c539bd42823d511aebb669f.zip | |
changes_2013_05_22a.patch:
1. Resolves issue of bug #988601 message 170 (Support of 'Unset' styles in EMF export).
2. Implements CSS 3 (and CSS 2) text-decoration support.
Note that it does not yet provide any method of adding these features - at present it just shows whatever is in the SVG. This new code is also used to display EMF/WMF strike-through and underline text decorations when these files are read in. Those decorations may also be written out to EMF/WMF. Other text decoration features, like overline, or dotted lines, are dropped. For SVG text-decoration -line, -style, -color are all implemented. CSS3 provides two ways to represent the same state, this code uses the compound text-decoration method rather than the 3 fields method. Also it leaves out keywords that are not needed and would break backwards compatibility. For instance:
text-decoration: underline solid
is valid, but would break CSS2. Solid is the default, so that sort of case is written as:
text-decoration: underline
If the state is CSS3 specific all of the needed fields are of course include, like
text-decoration: underline wavy red
3. It incorporates the fix for bug 1181326 (Text edit mishandles span of just colored spaces)
4. It incorporates further changes to text editing so that style can be changed on spans consisting of only spaces when text decorations are present in the span.
5. It incorporates code to disable text decorations when text so marked is mapped onto a path.
6. Fixed more bugs in Hebrew language support than I can remember. Hebrew language export/import to EMF now works quite well. (See the examples in libTERE v 0.7.) WMF does not support unicode and for all intents and purposes Inkscape has no way to read or write Hebrew to it. Some of more important things that now work that didn't (or didn't always): Hebrew diacritical marks, R/L/center justification, and bidirectional text. The Hebrew fonts "Ezra SIL" and "EZRA SIL SR" should be installed before viewing the libTERE examples, otherwise font substitutions will cause some text shifts.
7. Implemented font failover in Text Reassemble, which makes the process more robust. (Again, see the examples in libTERE. )
(bzr r11668.1.71)
Diffstat (limited to 'src/extension/internal')
| -rw-r--r-- | src/extension/internal/emf-inout.cpp | 23 | ||||
| -rw-r--r-- | src/extension/internal/emf-print.cpp | 50 | ||||
| -rw-r--r-- | src/extension/internal/emf-print.h | 3 | ||||
| -rw-r--r-- | src/extension/internal/text_reassemble.c | 1215 | ||||
| -rw-r--r-- | src/extension/internal/text_reassemble.h | 125 | ||||
| -rw-r--r-- | src/extension/internal/wmf-inout.cpp | 44 | ||||
| -rw-r--r-- | src/extension/internal/wmf-print.cpp | 41 | ||||
| -rw-r--r-- | src/extension/internal/wmf-print.h | 3 |
8 files changed, 1104 insertions, 400 deletions
diff --git a/src/extension/internal/emf-inout.cpp b/src/extension/internal/emf-inout.cpp index aaa817c61..c14393cc2 100644 --- a/src/extension/internal/emf-inout.cpp +++ b/src/extension/internal/emf-inout.cpp @@ -1400,8 +1400,10 @@ Emf::select_font(PEMF_CALLBACK_DATA d, int index) pEmr->elfw.elfLogFont.lfWeight == U_FW_EXTRABOLD ? SP_CSS_FONT_WEIGHT_BOLDER : U_FW_NORMAL; d->dc[d->level].style.font_style.value = (pEmr->elfw.elfLogFont.lfItalic ? SP_CSS_FONT_STYLE_ITALIC : SP_CSS_FONT_STYLE_NORMAL); - d->dc[d->level].style.text_decoration.underline = pEmr->elfw.elfLogFont.lfUnderline; - d->dc[d->level].style.text_decoration.line_through = pEmr->elfw.elfLogFont.lfStrikeOut; + d->dc[d->level].style.text_decoration_line.underline = pEmr->elfw.elfLogFont.lfUnderline; + d->dc[d->level].style.text_decoration_line.line_through = pEmr->elfw.elfLogFont.lfStrikeOut; + d->dc[d->level].style.text_decoration_line.set = true; + d->dc[d->level].style.text_decoration_line.inherit = false; // malformed EMF with empty filename may exist, ignore font change if encountered char *ctmp = U_Utf16leToUtf8((uint16_t *) (pEmr->elfw.elfLogFont.lfFaceName), U_LF_FACESIZE, NULL); if(ctmp){ @@ -2988,6 +2990,7 @@ std::cout << "BEFORE DRAW" y1 = pEmr->emrtext.ptlReference.y; cChars = 0; } + uint32_t fOptions = pEmr->emrtext.fOptions; if (d->dc[d->level].textAlign & U_TA_UPDATECP) { x1 = d->dc[d->level].cur.x; @@ -3078,6 +3081,10 @@ std::cout << "BEFORE DRAW" case SP_CSS_FONT_WEIGHT_BOLDER: tsp.weight = FC_WEIGHT_EXTRABOLD ; break; default: tsp.weight = FC_WEIGHT_NORMAL ; break; } + // EMF only supports two types of text decoration + tsp.decoration = TXTDECOR_NONE; + if(d->dc[d->level].style.text_decoration_line.underline){ tsp.decoration |= TXTDECOR_UNDER; } + if(d->dc[d->level].style.text_decoration_line.line_through){ tsp.decoration |= TXTDECOR_STRIKE;} // EMF textalignment is a bit strange: 0x6 is center, 0x2 is right, 0x0 is left, the value 0x4 is also drawn left tsp.taln = ((d->dc[d->level].textAlign & U_TA_CENTER) == U_TA_CENTER) ? ALICENTER : @@ -3086,13 +3093,19 @@ std::cout << "BEFORE DRAW" 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 + + // language direction can be encoded two ways, U_TA_RTLREADING is preferred + if( (fOptions & U_ETO_RTLREADING) || (d->dc[d->level].textAlign & U_TA_RTLREADING) ){ tsp.ldir = LDIR_RL; } + else{ tsp.ldir = LDIR_LR; } + 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.ori += 180.0 * current_rotation(d)/ M_PI; // radians to degrees 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); + char *fontspec = TR_construct_fontspec(&tsp, d->dc[d->level].font_name); + tsp.fi_idx = ftinfo_load_fontname(d->tri->fti,fontspec); + free(fontspec); // 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; } @@ -3101,7 +3114,7 @@ std::cout << "BEFORE DRAW" 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); + TR_layout_2_svg(d->tri); ts << d->tri->out; *(d->outsvg) += ts.str().c_str(); d->tri = trinfo_clear(d->tri); diff --git a/src/extension/internal/emf-print.cpp b/src/extension/internal/emf-print.cpp index 2a9f734d1..7440b5380 100644 --- a/src/extension/internal/emf-print.cpp +++ b/src/extension/internal/emf-print.cpp @@ -212,7 +212,7 @@ void PrintEmf::search_short_fflist(const char *fontname, double *f1, double *f2, search_long_fflist(fontname, f1, f2, f3); } -void PrintEmf::smuggle_adxky_out(const char *string, uint32_t **adx, double *ky, int *ndx, float scale){ +void PrintEmf::smuggle_adxkyrtl_out(const char *string, uint32_t **adx, double *ky, int *rtl, int *ndx, float scale){ float fdx; int i; uint32_t *ladx; @@ -233,6 +233,8 @@ void PrintEmf::smuggle_adxky_out(const char *string, uint32_t **adx, double *ky, cptr++; // skip 2nd fake terminator sscanf(cptr,"%7f",&fdx); *ky=fdx; + cptr += 7; // advance over ky and its space + sscanf(cptr,"%07d",rtl); } /* convert an 0RGB color to EMF U_COLORREF. @@ -313,6 +315,7 @@ unsigned int PrintEmf::begin (Inkscape::Extension::Print *mod, SPDocument *doc) // initialize a few global variables hbrush = hbrushOld = hpen = 0; + htextalignment = U_TA_BASELINE | U_TA_LEFT; use_stroke = use_fill = simple_shape = usebk = false; Inkscape::XML::Node *nv = sp_repr_lookup_name (doc->rroot, "sodipodi:namedview"); @@ -418,33 +421,34 @@ unsigned int PrintEmf::begin (Inkscape::Extension::Print *mod, SPDocument *doc) rec = U_EMRSETBKMODE_set(U_TRANSPARENT); if(!rec || emf_append((PU_ENHMETARECORD)rec, et, U_REC_FREE)){ - g_error("Fatal programming error in PrintEmf::text at U_EMRSETBKMODE_set"); + g_error("Fatal programming error in PrintEmf::begin at U_EMRSETBKMODE_set"); } hpolyfillmode=U_WINDING; rec = U_EMRSETPOLYFILLMODE_set(U_WINDING); if(!rec || emf_append((PU_ENHMETARECORD)rec, et, U_REC_FREE)){ - g_error("Fatal programming error in PrintEmf::text at U_EMRSETPOLYFILLMODE_set"); + g_error("Fatal programming error in PrintEmf::begin at U_EMRSETPOLYFILLMODE_set"); } - // Text alignment: (Never changes) + // Text alignment: (only changed if RTL text is encountered ) // - (x,y) coordinates received by this filter are those of the point where the text // actually starts, and already takes into account the text object's alignment; // - for this reason, the EMF text alignment must always be TA_BASELINE|TA_LEFT. + htextalignment = U_TA_BASELINE | U_TA_LEFT; rec = U_EMRSETTEXTALIGN_set(U_TA_BASELINE | U_TA_LEFT); if(!rec || emf_append((PU_ENHMETARECORD)rec, et, U_REC_FREE)){ - g_error("Fatal programming error in PrintEmf::text at U_EMRSETTEXTALIGN_set"); + g_error("Fatal programming error in PrintEmf::begin at U_EMRSETTEXTALIGN_set"); } htextcolor_rgb[0] = htextcolor_rgb[1] = htextcolor_rgb[2] = 0.0; rec = U_EMRSETTEXTCOLOR_set(U_RGB(0,0,0)); if(!rec || emf_append((PU_ENHMETARECORD)rec, et, U_REC_FREE)){ - g_error("Fatal programming error in PrintEmf::text at U_EMRSETTEXTCOLOR_set"); + g_error("Fatal programming error in PrintEmf::begin at U_EMRSETTEXTCOLOR_set"); } rec = U_EMRSETROP2_set(U_R2_COPYPEN); if(!rec || emf_append((PU_ENHMETARECORD)rec, et, U_REC_FREE)){ - g_error("Fatal programming error in PrintEmf::text at U_EMRSETROP2_set"); + g_error("Fatal programming error in PrintEmf::begin at U_EMRSETROP2_set"); } /* miterlimit is set with eah pen, so no need to check for it changes as in WMF */ @@ -1415,8 +1419,8 @@ unsigned int PrintEmf::fill( } } if ( - (style->stroke.noneSet || style->stroke_width.computed == 0.0) || - (style->stroke_dash.n_dash && style->stroke_dash.dash && FixPPTDashLine) || + (style->stroke.isNone() || style->stroke.noneSet || style->stroke_width.computed == 0.0) || + (style->stroke_dash.n_dash && style->stroke_dash.dash && FixPPTDashLine) || !all_closed ){ print_pathv(pathv, fill_transform); // do any fills. side effect: clears fill_pathv @@ -1977,10 +1981,21 @@ unsigned int PrintEmf::text(Inkscape::Extension::Print * /*mod*/, char const *te double ky; // the dx array is smuggled in like: text<nul>w1 w2 w3 ...wn<nul><nul>, where the widths are floats 7 characters wide, including the space - int ndx; + int ndx, rtl; uint32_t *adx; - smuggle_adxky_out(text, &adx, &ky, &ndx, PX2WORLD * std::min(tf.expansionX(),tf.expansionY())); // side effect: free() adx + smuggle_adxkyrtl_out(text, &adx, &ky, &rtl, &ndx, PX2WORLD * std::min(tf.expansionX(),tf.expansionY())); // side effect: free() adx + uint32_t textalignment; + if(rtl > 0){ textalignment = U_TA_BASELINE | U_TA_LEFT; } + else { textalignment = U_TA_BASELINE | U_TA_RIGHT | U_TA_RTLREADING; } + if(textalignment != htextalignment){ + htextalignment = textalignment; + rec = U_EMRSETTEXTALIGN_set(textalignment); + if(!rec || emf_append((PU_ENHMETARECORD)rec, et, U_REC_FREE)){ + g_error("Fatal programming error in PrintEmf::text at U_EMRSETTEXTALIGN_set"); + } + } + char *text2 = strdup(text); // because U_Utf8ToUtf16le calls iconv which does not like a const char * uint16_t *unicode_text = U_Utf8ToUtf16le( text2, 0, NULL ); free(text2); @@ -2043,8 +2058,8 @@ unsigned int PrintEmf::text(Inkscape::Extension::Print * /*mod*/, char const *te round(rot), transweight(style->font_weight.computed), (style->font_style.computed == SP_CSS_FONT_STYLE_ITALIC), - style->text_decoration.underline, - style->text_decoration.line_through, + style->text_decoration_line.underline, + style->text_decoration_line.line_through, U_DEFAULT_CHARSET, U_OUT_DEFAULT_PRECIS, U_CLIP_DEFAULT_PRECIS, @@ -2109,6 +2124,7 @@ unsigned int PrintEmf::text(Inkscape::Extension::Print * /*mod*/, char const *te int32_t const xpos = (int32_t) round(p2[Geom::X]); int32_t const ypos = (int32_t) round(p2[Geom::Y]); + // The number of characters in the string is a bit fuzzy. ndx, the number of entries in adx is // the number of VISIBLE characters, since some may combine from the UTF (8 originally, // now 16) encoding. Conversely strlen() or wchar16len() would give the absolute number of @@ -2117,7 +2133,13 @@ unsigned int PrintEmf::text(Inkscape::Extension::Print * /*mod*/, char const *te // This is currently being smuggled in from caller as part of text, works // MUCH better than the fallback hack below // uint32_t *adx = dx_set(textheight, U_FW_NORMAL, slen); // dx is needed, this makes one up - char *rec2 = emrtext_set( (U_POINTL) {xpos, ypos}, ndx, 2, unicode_text, U_ETO_NONE, U_RCL_DEF, adx); + char *rec2; + if(rtl>0){ + rec2 = emrtext_set( (U_POINTL) {xpos, ypos}, ndx, 2, unicode_text, U_ETO_NONE, U_RCL_DEF, adx); + } + else { // RTL text, U_TA_RTLREADING should be enough, but set this one too just in case + rec2 = emrtext_set( (U_POINTL) {xpos, ypos}, ndx, 2, unicode_text, U_ETO_RTLREADING, U_RCL_DEF, adx); + } free(unicode_text); free(adx); rec = U_EMREXTTEXTOUTW_set(U_RCL_DEF,U_GM_COMPATIBLE,1.0,1.0,(PU_EMRTEXT)rec2); diff --git a/src/extension/internal/emf-print.h b/src/extension/internal/emf-print.h index 6cfe5e46d..d42ba8a58 100644 --- a/src/extension/internal/emf-print.h +++ b/src/extension/internal/emf-print.h @@ -38,6 +38,7 @@ class PrintEmf : public Inkscape::Extension::Implementation::Implementation double _height; U_RECTL rc; + uint32_t htextalignment; uint32_t hbrush, hbrushOld, hpen, hpenOld; uint32_t hpolyfillmode; // used to minimize redundant records that set this float htextcolor_rgb[3]; // used to minimize redundant records that set this @@ -93,7 +94,7 @@ protected: static void read_system_fflist(void); static void search_long_fflist(const char *fontname, double *f1, double *f2, double *f3); static void search_short_fflist(const char *fontname, double *f1, double *f2, double *f3); - static void smuggle_adxky_out(const char *string, uint32_t **adx, double *ky, int *ndx, float scale); + static void smuggle_adxkyrtl_out(const char *string, uint32_t **adx, double *ky, int *rtl, int *ndx, float scale); U_COLORREF gethexcolor(uint32_t color); uint32_t transweight(const unsigned int inkweight); diff --git a/src/extension/internal/text_reassemble.c b/src/extension/internal/text_reassemble.c index 2518795b6..d16c060de 100644 --- a/src/extension/internal/text_reassemble.c +++ b/src/extension/internal/text_reassemble.c @@ -67,8 +67,8 @@ Optional compiler switches for development: File: text_reassemble.c -Version: 0.0.6 -Date: 12-MAR-2013 +Version: 0.0.10 +Date: 06-MAY-2013 Author: David Mathog, Biology Division, Caltech email: mathog@caltech.edu Copyright: 2013 David Mathog and California Institute of Technology (Caltech) @@ -80,6 +80,115 @@ extern "C" { #include "text_reassemble.h" #include "uemf_utf.h" /* For a couple of text functions. Exact copy from libUEMF. */ +#include <float.h> + +/* Code generated by make_ucd_mn_table.c using: + cat mnlist.txt | ./make_ucd_mn_table >generated.c +*/ +#include <stdint.h> +int is_mn_unicode(int test){ +#define MN_TEST_LIMIT 921600 +#define N_SPAGES 225 +#define N_CPAGES 192 +#define N_CPDATA 344 +#define C_PER_S 16 + uint8_t superpages[N_SPAGES]={ + 0x01, 0x02, 0x03, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x06, + 0x07, 0x08, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0A, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x0B}; + + uint8_t cpages[N_CPAGES]={ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, + 0x0E, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x00, 0x00, + 0x17, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x19, 0x00, 0x00, + 0x1A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1B, 0x00, 0x1C, 0x1D, 0x1E, 0x1F, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x21, 0x00, + 0x00, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x24, 0x25, 0x00, 0x00, 0x00, 0x00, 0x26, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x27, + 0x00, 0x28, 0x29, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x2A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + + uint32_t cpage_data[N_CPDATA]={ + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0x0000FFFF, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x000000F8, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xFFFE0000, 0xBFFFFFFF, 0x000000B6, 0x00000000, + 0x07FF0000, 0x00000000, 0xFFFFF800, 0x00010000, 0x00000000, 0x00000000, 0x9FC00000, 0x00003D9F, + 0x00020000, 0xFFFF0000, 0x000007FF, 0x00000000, 0x00000000, 0x0001FFC0, 0x00000000, 0x000FF800, + 0xFBC00000, 0x00003EEF, 0x0E000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x7FFFFFF0, + 0x00000007, 0x14000000, 0x00FE21FE, 0x0000000C, 0x00000002, 0x10000000, 0x0000201E, 0x0000000C, + 0x00000006, 0x10000000, 0x00023986, 0x00230000, 0x00000006, 0x10000000, 0x000021BE, 0x0000000C, + 0x00000002, 0x90000000, 0x0040201E, 0x0000000C, 0x00000004, 0x00000000, 0x00002001, 0x00000000, + 0x00000000, 0xC0000000, 0x00603DC1, 0x0000000C, 0x00000000, 0x90000000, 0x00003040, 0x0000000C, + 0x00000000, 0x00000000, 0x0000201E, 0x0000000C, 0x00000000, 0x00000000, 0x005C0400, 0x00000000, + 0x00000000, 0x07F20000, 0x00007F80, 0x00000000, 0x00000000, 0x1BF20000, 0x00003F00, 0x00000000, + 0x03000000, 0x02A00000, 0x00000000, 0x7FFE0000, 0xFEFFE0DF, 0x1FFFFFFF, 0x00000040, 0x00000000, + 0x00000000, 0x66FDE000, 0xC3000000, 0x001E0001, 0x20002064, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0xE0000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x001C0000, 0x001C0000, 0x000C0000, 0x000C0000, 0x00000000, 0x3FB00000, 0x200FFE40, 0x00000000, + 0x00003800, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000200, 0x00000000, 0x00000000, + 0x00000000, 0x0E040187, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x01800000, 0x00000000, 0x7F400000, 0x9FF81FE5, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x0000000F, 0x17D00000, 0x00000004, 0x000FF800, 0x00000003, 0x00000B3C, 0x00000000, 0x0003A340, + 0x00000000, 0x00CFF000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xFFF70000, 0x001021FD, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xFFFFFFFF, 0xF000007F, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x1FFF0000, 0x0001FFE2, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00038000, + 0x00000000, 0x00000000, 0x00000000, 0x80000000, 0x00000000, 0x00000000, 0x00000000, 0xFFFFFFFF, + 0x00000000, 0x00003C00, 0x00000000, 0x00000000, 0x06000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x3FF08000, 0x80000000, 0x00000000, 0x00000000, 0x00030000, + 0x00000844, 0x00000060, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000010, 0x0003FFFF, + 0x00000000, 0x00003FC0, 0x0003FF80, 0x00000000, 0x00000007, 0x13C80000, 0x00000000, 0x00000000, + 0x00000000, 0x00667E00, 0x00001008, 0x00000000, 0x00000000, 0xC19D0000, 0x00000002, 0x00403000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00002120, + 0x40000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x0000FFFF, 0x0000007F, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x20000000, + 0x0000F06E, 0x87000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000002, 0xFF000000, 0x0000007F, 0x00000000, 0x00000003, 0x06780000, 0x00000000, 0x00000000, + 0x00000007, 0x001FEF80, 0x00000000, 0x00000000, 0x00000003, 0x7FC00000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00BF2800, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00078000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0xF8000380, 0x00000FE7, 0x00003C00, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x0000001C, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0x0000FFFF}; + + int result=0; + + int spage_idx; + int cpage_row, cpage_column, cpage_idx; + int cd_row, cd_column, cd_idx, cd_bit; + + if(test<MN_TEST_LIMIT){ + spage_idx = test >> 12; + cpage_row = superpages[spage_idx]; + cpage_column = (test>>8) & 15; + cpage_idx = C_PER_S*cpage_row + cpage_column; + cd_row = cpages[cpage_idx]; + cd_column = test>>5 & 7; + cd_idx = 8*cd_row + cd_column; + cd_bit = test & 31; + result = cpage_data[cd_idx] & (1 << cd_bit); + } + return(result); +} + + /** \brief Find a (sub)string in a caseinvariant manner, used for locating "Narrow" in font name @@ -87,7 +196,7 @@ extern "C" { \param string Text to search \param sub Text to find */ -int TR_findcasesub(char *string, char *sub){ +int TR_findcasesub(const char *string, const char *sub){ int i,j; int match=0; for(i=0; string[i]; i++){ @@ -103,9 +212,117 @@ int TR_findcasesub(char *string, char *sub){ } /** + \brief Constrouct a fontspec from a TCHUNK_SPECS and a fontname + \return Returns NULL on error, new fontspec on success + \param tsp pointer to TCHUNK_SPECS to use for information + \param fontname Fontname to use in the new fontspec +*/ + /* construct a font name */ +char *TR_construct_fontspec(const TCHUNK_SPECS *tsp, const char *fontname){ + int newlen = 128 + strlen(fontname); /* too big, but not by much */ + char *newfs = NULL; + newfs = (char *) malloc(newlen); + sprintf(newfs,"%s:slant=%d:weight=%d:size=%lf:width=%d",fontname,tsp->italics,tsp->weight,tsp->fs,(tsp->co ? 75 : tsp->condensed)); + return(newfs); +} + + +/** + \brief Reconstrouct a fontspec by substituting a font name into an existing spec + \return Returns NULL on error, new fontspec on success + \param fontspec Original fontspec, only the name will be changed + \param fontname Fontname to substitute into the new fontspec +*/ + /* construct a font name */ +char *TR_reconstruct_fontspec(const char *fontspec, const char *fontname){ + int colon; + int newlen = strlen(fontspec) + strlen(fontname) + 1; /* too big, but not by much */ + char *newfs = NULL; + newfs = (char *) malloc(newlen); + colon = strcspn(fontspec,":"); + if(colon){ sprintf(newfs,"%s%s",fontname,&fontspec[colon]); } + return(newfs); +} + +/** + \brief Find a font in the list that has a glyph for this character, change alternate to match + \return Returns 0 if no match or an error, else returns the glyph index in the new alternate font + \param fti pointer to the FT_INFO structure, may be modified if alternate font is added + \param efsp Pointer to a Pointer to the original FNT_SPECS struct. On return contains the FNT_SPECS corresponding to the glyph_index.. + \param wc Current character (32 bit int) +*/ +int TR_find_alternate_font(FT_INFO *fti, FNT_SPECS **efsp, uint32_t wc){ + int glyph_index=0; /* this is the unknown character glyph */ + uint32_t i; + FcCharSet *cs; + FcResult result = FcResultMatch; + FcPattern *pattern, *fpat; + char *filename; + char *fontname; + char *newfontspec; + int fi_idx; + FNT_SPECS *fsp,*fsp2; + if(!fti || !efsp || !*efsp)return(0); + fsp = *efsp; + for(i=0;i<fsp->used;i++){ /* first check in alts */ + fsp2 = &fti->fonts[fsp->alts[i].fi_idx]; /* these are in order of descending previous usage */ + glyph_index = FT_Get_Char_Index( fsp2->face, wc); /* we have the face, might as well check that directly */ + if (glyph_index){ /* found a glyph for the character in this font */ + (void) fsp_alts_weight(fsp, i); + *efsp = fsp2; + return(glyph_index); + } + } + + /* it was not in alts, now go through fontset and see if it is in there */ + for(i=1; i< (unsigned int) fsp->fontset->nfont;i++){ /* already know the primary does not have this character */ + result = FcPatternGetCharSet(fsp->fontset->fonts[i], FC_CHARSET, 0, &cs); + if(result != FcResultMatch) return(0); /* some terrible problem, this should never happen */ + if (FcCharSetHasChar(cs, wc)){ /* found a glyph for the character in this font */ + glyph_index = i; + + /* Do a lot of work to find the filename corresponding to the fontset entry. + None of these should ever fail, but if one does, return 0 + */ + if( + !(pattern = FcNameParse((const FcChar8 *)&(fsp->fontspec))) || + !FcConfigSubstitute(NULL, pattern, FcMatchPattern) + )return(0); + FcDefaultSubstitute(pattern); + if( + !(fpat = FcFontRenderPrepare(NULL, pattern, fsp->fontset->fonts[i])) || + (FcPatternGetString( fpat, FC_FILE, 0, (FcChar8 **)&filename) != FcResultMatch) || + (FcPatternGetString( fsp->fontset->fonts[i], FC_FULLNAME, 0, (FcChar8 **)&fontname) != FcResultMatch) + )return(0); + + /* find the font (added from an unrelated fontset, for instance) or insert it as new */ + fi_idx = ftinfo_find_loaded_by_src(fti, (uint8_t *) filename); + if(fi_idx < 0){ + newfontspec = TR_reconstruct_fontspec((char *) fsp->fontspec, fontname); + fi_idx = ftinfo_load_fontname(fti, newfontspec); + free(newfontspec); + if(fi_idx < 0)return(0); /* This could happen if we run out of memory*/ + } + + /* add the new font index to the alts list on the (current) fsp. */ + (void) fsp_alts_insert(fsp, fi_idx); + + /* release FC's own memory related to this call that does not need to be kept around so that face will work */ + FcPatternDestroy(pattern); + + *efsp = &(fti->fonts[fi_idx]); + return(glyph_index); + } + } + + return(0); +} + +/** \brief Get the advance for the 32 bit character \return Returns -1 on error, or advance in units of 1/64th of a Point. + \param fti pointer to the FT_INFO structure, may be modified if alternate font is required \param fsp Pointer to FNT_SPECS struct. \param wc Current character (32 bit int) \param pc Previous character @@ -117,23 +334,31 @@ int TR_findcasesub(char *string, char *sub){ \param ymin If the pointer is defined, the value is adjusted if ymin of wc character is less than the current value. \param ymax If the pointer is defined, the value is adjusted if ymin of wc character is more than the current value. */ -int TR_getadvance(FNT_SPECS *fsp, uint32_t wc, uint32_t pc, int load_flags, int kern_mode, int *ymin, int *ymax){ +int TR_getadvance(FT_INFO *fti, 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; + if(is_mn_unicode(wc))return(0); /* no advance on Unicode Mn characters */ + 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); + if(!glyph_index){ /* not in primary font, check alternates */ + glyph_index = TR_find_alternate_font(fti, &fsp, wc); + } + if(glyph_index){ + 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); + } } } + /* If there was no way to determine the width, this returns the error value */ return(advance); } @@ -164,7 +389,7 @@ int TR_getkern2(FNT_SPECS *fsp, uint32_t wc, uint32_t pc, int kern_mode){ } /** - \brief 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. + \brief Get the kerning for a pair of 32 bit characters, where one is the last character in the previous text block, and the other is the first in the current text block. \return Returns 0 on error, or kerning value (which may be 0) for the pair in units of 1/64th of a point. \param fsp Pointer to FNT_SPECS struct. \param tsp current text object @@ -214,17 +439,16 @@ int TR_kern_gap(FNT_SPECS *fsp, TCHUNK_SPECS *tsp, TCHUNK_SPECS *ptsp, int kern_ */ double TR_baseline(TR_INFO *tri, int src, double *ymax, double *ymin){ double baseline=0; - double tmp; + volatile double tmp=0.0; /* This MUST be volatile */ 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; + FNT_SPECS *fsp; last = cxi->cx[src].kids.used - 1; switch (cxi->cx[src].type){ case TR_TEXT: @@ -242,19 +466,24 @@ double TR_baseline(TR_INFO *tri, int src, double *ymax, double *ymin){ } break; case TR_LINE: - for(i=last;i>=0;i--){ /* here last is the count of */ + for(i=last;i>=0;i--){ /* here last is the count of text objects in the complex */ 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); + tmp = tpi->chunks[trec].fs * (((double)fsp->face->bbox.yMax)/yheight); + /* gcc 4.6.3 had a bizarre optimization error for -O2 and -O3 where *ymax <= tmp was + not true when *ymax == tmp, as verified by examining the binary representations. + This was apparently due to retained excess precision. Making tmp volatile + forces it to be stored into a 64 bit location, dropping the extra 12 bits from + the 80 bit register. */ 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 */ + 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; @@ -266,7 +495,6 @@ double TR_baseline(TR_INFO *tri, int src, double *ymax, double *ymin){ 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; @@ -301,8 +529,8 @@ 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 ymax = DBL_MIN; + double ymin = DBL_MIN; double prevbase; double thisbase; double weight; @@ -374,10 +602,7 @@ FT_INFO *ftinfo_init(void){ int ftinfo_make_insertable(FT_INFO *fti){ int status=0; if(!fti)return(2); - if(fti->used < fti->space){ - /* already insertable */ - } - else { + if(fti->used >= fti->space){ 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)); @@ -416,22 +641,12 @@ int ftinfo_insert(FT_INFO *fti, FNT_SPECS *fsp){ \returns NULL. */ 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! */ - } + (void) ftinfo_clear(fti); + FcFini(); /* shut down FontConfig, release memory, patterns must have already been released or boom! */ return NULL; } + /** \brief Clear an FT_INFO structure. Release all Freetype memory but does not release Fontconfig. This would be called in preference to ftinfo_release() if some other part of the program needed @@ -441,13 +656,17 @@ FT_INFO *ftinfo_release(FT_INFO *fti){ \returns NULL. */ FT_INFO *ftinfo_clear(FT_INFO *fti){ - int i; + uint32_t i; + FNT_SPECS *fsp; 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 */ + fsp = &(fti->fonts[i]); + FT_Done_Face(fsp->face); /* release memory for face controlled by FreeType */ + free(fsp->file); /* release memory holding copies of paths */ + free(fsp->fontspec); /* release memory holding copies of font names */ + FcPatternDestroy(fsp->fpat); /* release memory for FontConfig fpats */ + FcFontSetDestroy(fti->fonts[i].fontset); + if(fsp->alts){ free(fsp->alts); } } free(fti->fonts); FT_Done_FreeType(fti->library); /* release all other FreeType memory */ @@ -458,6 +677,227 @@ FT_INFO *ftinfo_clear(FT_INFO *fti){ /** + \brief Find the loaded font matching fontspec + \returns index of font on success, -1 if not found + \param tri pointer to the TR_INFO structure. + \param fontspec UTF-8 description of the font, as constructed in trinfo_load_fontname +*/ + +int ftinfo_find_loaded_by_spec(const FT_INFO *fti, const uint8_t *fontspec){ + uint32_t i; + int status = -1; + /* If it is already loaded, do not load it again */ + for(i=0;i<fti->used;i++){ + if(0==strcmp((char *) fti->fonts[i].fontspec, (char *)fontspec)){ + status=i; + break; + } + } + return(status); +} + +/** + \brief Find the loaded font matching the source file + \returns index of font on success, -1 if not found + \param tri pointer to the TR_INFO structure. + \param filename UTF-8 file name for the font +*/ + +int ftinfo_find_loaded_by_src(const FT_INFO *fti, const uint8_t *filename){ + uint32_t i; + int status = -1; + /* If it is already loaded, do not load it again */ + for(i=0;i<fti->used;i++){ + if(0==strcmp((char *) fti->fonts[i].file, (char *) filename)){ + status=i; + break; + } + } + return(status); +} + + +/** + \brief Load a (new) font by name into a TR_INFO structure or find it if it is already loaded + \returns fi_idx of inserted (or found) font on success, <0 on error. + \param fti pointer to the FT_INFO structure. + \param fontname UTF-8 font name + \param fontspec UTF-8 font specification used for query string. +*/ + +int ftinfo_load_fontname(FT_INFO *fti, const char *fontspec){ + FcPattern *pattern, *fpat; + FcFontSet *fontset; + FcResult result = FcResultMatch; + char *filename; + double fd; + FNT_SPECS *fsp; + int status; + int fi_idx; + + if(!fti)return(-1); + + /* If it is already loaded, do not load it again */ + if((status = ftinfo_find_loaded_by_spec(fti, (uint8_t *) fontspec))>=0){ + return(status); + } + + ftinfo_make_insertable(fti); + fi_idx = fti->used; + + if(!(pattern = FcNameParse((const FcChar8 *)fontspec)) )return(2); + if(!FcConfigSubstitute(NULL, pattern, FcMatchPattern) )return(3); + FcDefaultSubstitute(pattern); + /* get a fontset, trimmed to only those with new glyphs as needed, so that missing glyph's may be handled */ + if(!(fontset = FcFontSort (NULL,pattern, FcTrue, NULL, &result)) + || result != FcResultMatch)return(4); + if(!(fpat = FcFontRenderPrepare(NULL, pattern, fontset->fonts[0])) )return(405); + if(FcPatternGetString( fpat, FC_FILE, 0, (FcChar8 **)&filename) != FcResultMatch)return(5); + if(FcPatternGetDouble( fpat, FC_SIZE, 0, &fd) != FcResultMatch)return(6); + + /* copy these into memory for external use */ + fsp = &(fti->fonts[fti->used]); + fsp->fontset = fontset; + fsp->alts = NULL; /* Initially no links to alternate fonts */ + fsp->space = 0; + fsp->file = (uint8_t *) U_strdup((char *) filename); + fsp->fontspec = (uint8_t *) U_strdup((char *) fontspec); + 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 current 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); } + + /* The space advance is needed in various places. Get it now, and get it in the font units, + so that it can be scaled later with the text size */ + status = TR_getadvance(fti, fsp,' ',0,FT_LOAD_NO_SCALE | FT_LOAD_NO_HINTING | FT_LOAD_NO_BITMAP, FT_KERNING_UNSCALED, NULL, NULL); + if(status < 0)return(7); + fsp->spcadv = ((double) status)/(64.0); + + 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(fi_idx); +} + +/** + \brief Dump the contents of the TR_INFO structure to stdout. For debugging purposes,not used in production code. + \param tri pointer to the TR_INFO structure. +*/ +void ftinfo_dump(const FT_INFO *fti){ + uint32_t i,j; + FNT_SPECS *fsp; + printf("fti space: %d\n",fti->space); + printf("fti used: %d\n",fti->used); + for(i=0; i< fti->used; i++){ + fsp = &(fti->fonts[i]); + printf("fti font: %6d space: %6d used: %6d spcadv %8lf fsize %8lf \n",i,fsp->space,fsp->used,fsp->spcadv,fsp->fsize); + printf(" file: %s\n",fsp->file); + printf(" fspc: %s\n",fsp->fontspec); + for(j=0;j<fsp->used;j++){ + printf(" alts: %6d fi_idx: %6d wgt: %6d\n",j,fsp->alts[j].fi_idx,fsp->alts[j].weight); + } + } + +} + +/** + \brief Make the FNT_SPECS alts structure insertable. Adds storage as needed. + \param fti pointer to the FT_INFO structure + \returns 0 on success, !0 on error. +*/ +int fsp_alts_make_insertable(FNT_SPECS *fsp){ + int status=0; + if(!fsp)return(2); + if(fsp->used >= fsp->space){ + fsp->space += ALLOCINFO_CHUNK; + if((fsp->alts = (ALT_SPECS *) realloc(fsp->alts, fsp->space * sizeof(ALT_SPECS) ))){ + memset(&fsp->alts[fsp->used],0,(fsp->space - fsp->used)*sizeof(ALT_SPECS)); + } + else { + status=1; + } + } + return(status); +} + + +/** + \brief Insert a new ALT_SPECS into the FNT_SPECS alts list. + \param fsp pointer to the FNT_SPECS structure. + \param fi_idx font index to add to the alts list + \returns 0 on success, !0 on error. +*/ +int fsp_alts_insert(FNT_SPECS *fsp, uint32_t fi_idx){ + int status=1; + ALT_SPECS alt; + if(!fsp)return(3); + alt.fi_idx = fi_idx; + alt.weight = 1; /* new ones start with this weight, it can only go up */ + if(!(status = fsp_alts_make_insertable(fsp))){ + fsp->alts[fsp->used] = alt; + fsp->used++; + } + return(status); +} + +/** + \brief Increment the weight of an alts entry by 1, readjust order if necessary + \param fsp pointer to the FNT_SPECS structure. + \param idx index of the alts entry to increment + \returns 0 on success, !0 on error. +*/ +int fsp_alts_weight(FNT_SPECS *fsp, uint32_t a_idx){ + uint32_t i; + ALT_SPECS alt; + if(!fsp)return(1); + if(!fsp->used)return(2); + if(a_idx >= fsp->used)return(3); + /* If a counter hits the limit divide all counts in half. */ + if(fsp->alts[a_idx].weight == UINT32_MAX){ + for(i=0; i<fsp->used; i++){ fsp->alts[i].weight /= 2; } + } + fsp->alts[a_idx].weight++; + for(i=a_idx; i>0; i--){ + if(fsp->alts[i-1].weight >= fsp->alts[a_idx].weight)break; + alt = fsp->alts[i-1]; + fsp->alts[i-1] = fsp->alts[a_idx]; + fsp->alts[a_idx] = alt; + } + return(0); +} + + + +/** \brief Make a CHILD_SPECS structure insertable. Adds storage as needed. \param csp pointer to the CHILD_SPECS structure \returns 0 on success, !0 on error. @@ -465,10 +905,7 @@ FT_INFO *ftinfo_clear(FT_INFO *fti){ int csp_make_insertable(CHILD_SPECS *csp){ int status=0; if(!csp)return(2); - if(csp->used < csp->space){ - /* already insertable */ - } - else { + if(csp->used >= csp->space){ 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)); @@ -505,8 +942,8 @@ int csp_insert(CHILD_SPECS *dst, int src){ \returns 0 on success, !0 on error. */ int csp_merge(CHILD_SPECS *dst, CHILD_SPECS *src){ - int i; - int status=1; + uint32_t i; + int status=1; if(!dst)return(2); if(!src)return(3); for(i=0;i<src->used;i++){ @@ -529,6 +966,15 @@ void csp_release(CHILD_SPECS *csp){ } } +/** + \brief Clear a CHILD_SPECS structure, making all allocated slots usable. Does not release associated memory. + \param csp pointer to the CHILD_SPECS structure. + \returns NULL. +*/ +void csp_clear(CHILD_SPECS *csp){ + csp->used = 0; +} + /** \brief Initialize an CX_INFO structure. Holds complexes (multiple text objects in known positions and order.) @@ -553,10 +999,7 @@ CX_INFO *cxinfo_init(void){ */ int cxinfo_make_insertable(CX_INFO *cxi){ int status=0; - if(cxi->used < cxi->space){ - /* already insertable */ - } - else { + if(cxi->used >= cxi->space){ 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)); @@ -617,19 +1060,37 @@ 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){ int status =1; if(!cxi)return(2); - if(dst < 0 || dst >= cxi->used)return(3); - if(src < 0)return(4); + if(!cxi->used)return(3); + if(dst < 0 || dst >= (int) cxi->used)return(4); + if(src < 0)return(5); cxi->cx[dst].type = type; status = csp_merge(&(cxi->cx[dst].kids), &(cxi->cx[src].kids)); return(status); } /** + \brief Trim the last complex from thelist of complexes. + \param cxi pointer to the CX_INFO structure (complexes). + \returns 0 on success, !0 on error. +*/ +int cxinfo_trim(CX_INFO *cxi){ + int status = 0; + int last ; + if(!cxi)return(1); + if(!cxi->used)return(2); + last = cxi->used - 1; + csp_clear(&(cxi->cx[last].kids)); + cxi->used--; + return(status); +} + + +/** \brief Dump the contents of the TR_INFO structure to stdout. For debugging purposes,not used in production code. \param tri pointer to the TR_INFO structure. */ -void cxinfo_dump(TR_INFO *tri){ - int i,j,k; +void cxinfo_dump(const TR_INFO *tri){ + uint32_t i,j,k; CX_INFO *cxi = tri->cxi; BR_INFO *bri = tri->bri; TP_INFO *tpi = tri->tpi; @@ -641,17 +1102,23 @@ void cxinfo_dump(TR_INFO *tri){ printf("cxi phase1: %d\n",cxi->phase1); printf("cxi lines: %d\n",cxi->lines); printf("cxi paras: %d\n",cxi->paras); + printf("cxi xy: %lf , %lf\n",tri->x,tri->y); 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] 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); + printf("cxi cx[%d] member:%3d tp_idx:%3d ldir:%d rt_tidx:%3d br (LL,UR) (%8.3lf,%8.3lf),(%8.3lf,%8.3lf) xy (%8.3lf,%8.3lf) kern (%8.3lf,%8.3lf) text:<%s> decor:%5.5x\n", + i, j, k, tpi->chunks[k].ldir, tpi->chunks[k].rt_tidx, + bsp->xll,bsp->yll,bsp->xur,bsp->yur, + tpi->chunks[k].x, tpi->chunks[k].y, + tpi->chunks[k].xkern, tpi->chunks[k].ykern, + tpi->chunks[k].string, tpi->chunks[k].decoration ); } else { /* TR_PARA_* */ printf("cxi cx[%d] member:%d cx_idx:%d\n",i, j, k); @@ -669,7 +1136,7 @@ void cxinfo_dump(TR_INFO *tri){ \returns NULL. */ CX_INFO *cxinfo_release(CX_INFO *cxi){ - int i; + uint32_t i; if(cxi){ for(i=0;i<cxi->used;i++){ csp_release(&cxi->cx[i].kids); } free(cxi->cx); @@ -721,13 +1188,16 @@ int tpinfo_make_insertable(TP_INFO *tpi){ \param tpi pointer to the TP_INFO structure \param tsp pointer to the TCHUNK_SPECS structure */ -int tpinfo_insert(TP_INFO *tpi, TCHUNK_SPECS *tsp){ +int tpinfo_insert(TP_INFO *tpi, const TCHUNK_SPECS *tsp){ int status=1; + TCHUNK_SPECS *ltsp; 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 */ + ltsp = &(tpi->chunks[tpi->used]); + memcpy(ltsp,tsp,sizeof(TCHUNK_SPECS)); + if(tsp->co)ltsp->condensed = 75; /* Narrow was set in the font name */ + ltsp->xkern = ltsp->ykern = 0.0; /* kerning will be calculated from the derived layout */ tpi->used++; } return(status); @@ -740,7 +1210,7 @@ int tpinfo_insert(TP_INFO *tpi, TCHUNK_SPECS *tsp){ \param tpi pointer to the TP_INFO structure. */ TP_INFO *tpinfo_release(TP_INFO *tpi){ - int i; + uint32_t i; if(tpi){ for(i=0;i<tpi->used;i++){ free(tpi->chunks[i].string); } @@ -787,7 +1257,7 @@ int brinfo_make_insertable(BR_INFO *bri){ \param bri pointer to the BR_INFO structure \param element pointer to the BRECT_SPECS structure */ -int brinfo_insert(BR_INFO *bri, BRECT_SPECS *element){ +int brinfo_insert(BR_INFO *bri, const BRECT_SPECS *element){ int status=1; if(!bri)return(2); if(!(status=brinfo_make_insertable(bri))){ @@ -806,8 +1276,9 @@ int brinfo_insert(BR_INFO *bri, BRECT_SPECS *element){ */ 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); + if(!bri->used)return(2); + if(dst<0 || dst >= (int) bri->used)return(3); + if(src<0 || src >= (int) bri->used)return(4); 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); @@ -841,13 +1312,14 @@ printf("bri_Merge into rect:%d (LL,UR) dst:(%lf,%lf),(%lf,%lf) src:(%lf,%lf),(%l \param rp_dst Pointer to edge padding values for dst. \param rp_src Pointer to edge padding values for src. */ -int brinfo_overlap(BR_INFO *bri, int dst, int src, RT_PAD *rp_dst, RT_PAD *rp_src){ +int brinfo_overlap(const 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 || !rp_dst || !rp_src)return(2); - if(dst<0 || dst>= bri->used)return(3); - if(src<0 || src>= bri->used)return(4); + if(!bri->used)return(3); + if(dst<0 || dst>= (int) bri->used)return(4); + if(src<0 || src>= (int) bri->used)return(5); br_dst=&bri->rects[dst]; br_src=&bri->rects[src]; if( /* Test all conditions that exclude overlap, if any are true, then no overlap */ @@ -908,6 +1380,36 @@ printf("Overlap rprect (LL,UR) dst:(%lf,%lf),(%lf,%lf) src:(%lf,%lf),(%lf,%lf)\n } /** + \brief Check for a text element upstream from the start element and in the reversed direction. + \returns 0 on success (not upstream), 1 if upstream, anything else is an error. + \param bri pointer to the BR_INFO structure + \param dst index of the destination bounding rectangle. + \param src index of the source bounding rectangle. + \param ddir direction of dst + \param sdir direction of src +*/ + +int brinfo_upstream(BR_INFO *bri, int dst, int src, int ddir, int sdir){ + int status=0; + BRECT_SPECS *br_dst; + BRECT_SPECS *br_src; + if(!bri)return(2); + if(!bri->used)return(3); + if(dst<0 || dst>= (int) bri->used)return(4); + if(src<0 || src>= (int) bri->used)return(5); + br_dst=&bri->rects[dst]; + br_src=&bri->rects[src]; + if( ddir == LDIR_RL && sdir == LDIR_LR){ + if(br_dst->xur <= (br_src->xll + br_src->xur)/2.0){ status = 1; } + } + else if( ddir == LDIR_LR && sdir == LDIR_RL){ + if((br_src->xll + br_src->xur)/2.0 <= br_dst->xll ){ status = 1; } + } + return(status); +} + + +/** \brief Try to deduce justification of a paragraph from the bounding rectangles for two successive lines. \returns one of TR_PARA_ UJ (unknown justified), LJ, CJ, or RJ (left, center, or right justified). \param bri pointer to the BR_INFO structure @@ -917,7 +1419,7 @@ printf("Overlap rprect (LL,UR) dst:(%lf,%lf),(%lf,%lf) src:(%lf,%lf),(%lf,%lf)\n \param type Preexisting justification for dst, if any. Justification of dst and src must match this or TR_PARA_UJ is returned even if dst and src have some (other) alignment. */ -enum tr_classes brinfo_pp_alignment(BR_INFO *bri, int dst, int src, double slop, enum tr_classes type){ +enum tr_classes brinfo_pp_alignment(const 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]; @@ -1073,91 +1575,6 @@ TR_INFO *trinfo_clear(TR_INFO *tri){ return(tri); } -/** - \brief Load a (new) font by name into a TR_INFO structure. - \returns index of font. If the font was already loaded return the existing index. - \param tri pointer to the TR_INFO structure. - \param fontname UTF-8 font name - \param tsp pointer to the TCHUNK_SPECS structure - used to construct a font query string. -*/ - -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); -} - /** \brief Set the quantization error value for a TR_INFO structure. @@ -1239,14 +1656,11 @@ int trinfo_load_ft_opts(TR_INFO *tri, int use_kern, int load_flags, int kern_mod \param tri pointer to a TR_INFO structure \param src Pointer to a text string. */ -int trinfo_append_out(TR_INFO *tri, char *src){ +int trinfo_append_out(TR_INFO *tri, const char *src){ size_t slen; if(!src)return(-1); slen = strlen(src); - if(tri->outused + (int) slen + 1 < tri->outspace){ - /* already insertable */ - } - else { + if(tri->outused + (int) slen + 1 >= tri->outspace){ tri->outspace += TEREMAX(ALLOCOUT_CHUNK,slen+1); if(!(tri->out = realloc(tri->out, tri->outspace )))return(-1); } @@ -1265,7 +1679,7 @@ int trinfo_append_out(TR_INFO *tri, char *src){ \param flags special processing flags: TR_EMFBOT calculate Y coordinates of ALIBOT object compatible with EMF files TA_BOTTOM alignment. */ -int trinfo_load_textrec(TR_INFO *tri, TCHUNK_SPECS *tsp, double escapement, int flags){ +int trinfo_load_textrec(TR_INFO *tri, const TCHUNK_SPECS *tsp, double escapement, int flags){ int status; double x,y,xe; @@ -1285,12 +1699,14 @@ int trinfo_load_textrec(TR_INFO *tri, TCHUNK_SPECS *tsp, double escapement, int 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); + fti = tri->fti; + tpi = tri->tpi; + bri = tri->bri; + idx = tsp->fi_idx; + taln = tsp->taln; + if(!fti->used)return(4); + if(idx <0 || idx >= (int) fti->used)return(5); + fsp = &(fti->fonts[idx]); if(!tri->dirty){ tri->x = tsp->x; @@ -1318,10 +1734,11 @@ int trinfo_load_textrec(TR_INFO *tri, TCHUNK_SPECS *tsp, double escapement, int 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, + 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); */ @@ -1330,17 +1747,22 @@ printf("Face idx:%d bbox: xMax/Min:%ld,%ld yMax/Min:%ld,%ld UpEM:%d asc/des:%d,% 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); + status = TR_getadvance(fti, 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; } + + /* Some glyphs in fonts have no vertical extent, for instance, Hebrew glyphs in Century Schoolbook L. + Use the 3/4 of the font size as a (very bad) approximation for the actual values. */ + if(ymin==0 && ymax==0){ + ymax = 0.75 * fsp->fsize * 64.0; + } + 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 @@ -1350,22 +1772,12 @@ printf("Face idx:%d bbox: xMax/Min:%ld,%ld yMax/Min:%ld,%ld UpEM:%d asc/des:%d,% */ 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); } - } /* find the font ascender descender (general one, not specific for current text) */ fasc = ((double) (fsp->face->ascender) )/64.0; fdsc = ((double) (fsp->face->descender))/64.0; - if(tri->load_flags & FT_LOAD_NO_SCALE){ - xe *= tsp->fs/32.0; - fsp->spcadv *= tsp->fs/32.0; - } + if(tri->load_flags & FT_LOAD_NO_SCALE) xe *= tsp->fs/32.0; /* now place the rectangle using ALN information */ if( taln & ALIHORI & ALILEFT ){ @@ -1380,6 +1792,7 @@ printf("Face idx:%d bbox: xMax/Min:%ld,%ld yMax/Min:%ld,%ld UpEM:%d asc/des:%d,% bsp.xll = tpi->chunks[current].x - xe; bsp.xur = tpi->chunks[current].x; } + tpi->chunks[current].ldir = tsp->ldir; if(tri->load_flags & FT_LOAD_NO_SCALE){ asc *= tsp->fs/32.0; @@ -1458,9 +1871,6 @@ 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; @@ -1470,22 +1880,26 @@ void TR_layout_2_svg(TR_INFO *tri){ 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; CX_SPECS *cline_sp; - int i,j,k,jdx,kdx; - int status; + unsigned int i,j,k,jdx,kdx; + int ldir; char obuf[1024]; /* big enough for style and so forth */ + char cbuf[16]; /* big enough for one hex color */ char stransform[128]; - double newx,newy; + double newx,newy,tmpx; + uint32_t utmp; +/* +#define DBG_TR_PARA 0 +#define DBG_TR_INPUT 1 +*/ /* The debug section below is difficult to see if usebk is anything other than BKCLR_NONE */ -#if defined(DBG_TR_PARA) || defined(DBG_TR_INPUT) /* enable debugging code, writes extra information into SVG */ +#if DBG_TR_PARA || DBG_TR_INPUT /* enable debugging code, writes extra information into SVG */ /* put rectangles down for each text string - debugging!!! This will not work properly for any Narrow fonts */ - esc = tri->esc; - esc *= 2.0 * M_PI / 360.0; /* degrees to radians and change direction of rotation */ + esc = tri->esc; + esc *= 2.0 * M_PI / 360.0; /* degrees to radians and change direction of rotation */ 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); for(i=cxi->phase1; i<cxi->used;i++){ /* over all complex members from phase2 == TR_PARA_* complexes */ csp = &(cxi->cx[i]); @@ -1494,10 +1908,11 @@ void TR_layout_2_svg(TR_INFO *tri){ 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]; + ldir = tsp->ldir; if(!j && !k){ -#ifdef DBG_TR_PARA +#if 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"); + TRPRINT(tri, "style=\"color:#0000FF;color-interpolation:sRGB;color-interpolation-filters:linearRGB;fill:none;stroke:#000000;stroke-width:0.8;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)); @@ -1508,16 +1923,16 @@ void TR_layout_2_svg(TR_INFO *tri){ 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); +#if DBG_TR_INPUT /* debugging code, this section writes the original text objects */ + newx = 1.25*(ldir == LDIR_RL ? bri->rects[tsp->rt_tidx].xur : 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"); + TRPRINT(tri, "style=\"color:#000000;color-interpolation:sRGB;color-interpolation-filters:linearRGB;fill:none;stroke:#00FF00;stroke-width:0.3;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); + sprintf(obuf,"x=\"%lf\" y=\"%lf\"\n",1.25*(bri->rects[tsp->rt_tidx].xll),newy); TRPRINT(tri, obuf); TRPRINT(tri, stransform); TRPRINT(tri, "/>\n"); @@ -1538,9 +1953,10 @@ void TR_layout_2_svg(TR_INFO *tri){ 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); + sprintf(obuf,"text-anchor:%s;",(tsp->ldir == LDIR_RL ? "end" : "start")); + TRPRINT(tri, obuf); + cutat=strcspn((char *)fti->fonts[tsp->fi_idx].fontspec,":"); + sprintf(obuf,"font-family:%.*s;",cutat,fti->fonts[tsp->fi_idx].fontspec); TRPRINT(tri, obuf); sprintf(obuf,"\n\">%s</text>\n",tsp->string); TRPRINT(tri, obuf); @@ -1552,8 +1968,8 @@ void TR_layout_2_svg(TR_INFO *tri){ if(tri->usebk){ - esc = tri->esc; - esc *= 2.0 * M_PI / 360.0; /* degrees to radians and change direction of rotation */ + esc = tri->esc; + esc *= 2.0 * M_PI / 360.0; /* degrees to radians and change direction of rotation */ 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); for(i=cxi->phase1; i<cxi->used;i++){ /* over all complex members from phase2 == TR_PARA_* complexes */ @@ -1579,6 +1995,7 @@ void TR_layout_2_svg(TR_INFO *tri){ 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]; + ldir = tsp->ldir; if(!j && !k){ if(tri->usebk == BKCLR_ALL){ TRPRINT(tri, "<rect\n"); @@ -1595,7 +2012,7 @@ void TR_layout_2_svg(TR_INFO *tri){ } } if(tri->usebk == BKCLR_FRAG){ - newx = 1.25*(bri->rects[tsp->rt_tidx].xll); + newx = 1.25*(ldir == LDIR_RL ? bri->rects[tsp->rt_tidx].xur : bri->rects[tsp->rt_tidx].xll); newy = 1.25*(bri->rects[tsp->rt_tidx].yur); TRPRINT(tri, "<rect\n"); sprintf(obuf,"style=\"color-interpolation:sRGB;color-interpolation-filters:linearRGB;fill:#%2.2X%2.2X%2.2X;;stroke:none;;stroke-dasharray:none;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate;clip-rule:nonzero\"\n",tri->bkcolor.Red,tri->bkcolor.Green,tri->bkcolor.Blue); @@ -1616,12 +2033,12 @@ void TR_layout_2_svg(TR_INFO *tri){ } - tsp=tpi->chunks; + 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 */ + 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 */ @@ -1630,37 +2047,34 @@ void TR_layout_2_svg(TR_INFO *tri){ 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 */ + jdx = csp->kids.members[j]; /* index of phase1 complex (all are TR_TEXT or TR_LINE) */ + 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 */ + for(k=0; k<cxi->cx[jdx].kids.used; k++){ + kdx = cxi->cx[jdx].kids.members[k]; /* index for text objects in tpi, for this k */ + tsp = &tpi->chunks[kdx]; /* text chunk for this k */ + ldir = tsp->ldir; /* language direction for this k */ + if(!k){ /* first iteration */ + 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; + if(ldir == LDIR_RL){ recenter = -(bri->rects[cxi->cx[jdx].rt_cidx].xur - bri->rects[cxi->cx[jdx].rt_cidx].xll); } + else { 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; + if(ldir == LDIR_RL){ recenter = -(bri->rects[cxi->cx[jdx].rt_cidx].xur - bri->rects[cxi->cx[jdx].rt_cidx].xll)/2.0; } + else { 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; + if(ldir == LDIR_RL){ recenter = 0.0; } + else { recenter = +(bri->rects[cxi->cx[jdx].rt_cidx].xur - bri->rects[cxi->cx[jdx].rt_cidx].xll); } break; } if(!j){ @@ -1685,9 +2099,8 @@ void TR_layout_2_svg(TR_INFO *tri){ 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); + cutat=strcspn((char *)fti->fonts[tsp->fi_idx].fontspec,":"); + sprintf(obuf,"font-family:%.*s;",cutat,fti->fonts[tsp->fi_idx].fontspec); TRPRINT(tri, obuf); switch(csp->type){ /* set up the alignment, if there is one */ case TR_TEXT: @@ -1695,8 +2108,6 @@ void TR_layout_2_svg(TR_INFO *tri){ /* 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; @@ -1704,42 +2115,30 @@ void TR_layout_2_svg(TR_INFO *tri){ sprintf(obuf,"text-align:center;text-anchor:middle;"); break; case TR_PARA_RJ: - sprintf(obuf,"text-align:end;text-anchor:end;"); + 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)); + tmpx = 1.25*((ldir == LDIR_RL ? bri->rects[kdx].xur : bri->rects[kdx].xll) + recenter); + sprintf(obuf,"x=\"%lf\" y=\"%lf\"\n>",tmpx,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)); + tmpx = 1.25*((ldir == LDIR_RL ? bri->rects[kdx].xur : bri->rects[kdx].xll) + recenter); + sprintf(obuf,"<tspan sodipodi:role=\"line\"\nx=\"%lf\" y=\"%lf\"\n>",tmpx,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); + TRPRINT(tri, "<tspan\n"); - /* 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; - } - } + /* Scale kerning and make any other necessary adjustments + + */ + dx = 1.25 * tsp->xkern; + dy = 1.25 * tsp->ykern; - /* 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); } + sprintf(obuf,"dx=\"%lf\" dy=\"%lf\" ",dx, dy); TRPRINT(tri, obuf); sprintf(obuf,"style=\"fill:#%2.2X%2.2X%2.2X;",tsp->color.Red,tsp->color.Green,tsp->color.Blue); TRPRINT(tri, obuf); @@ -1747,38 +2146,44 @@ void TR_layout_2_svg(TR_INFO *tri){ TRPRINT(tri, obuf); sprintf(obuf,"font-style:%s;",(tsp->italics ? "italic" : "normal")); TRPRINT(tri, obuf); - switch(tsp->decoration){ - case TXTDECOR_NONE: - case TXTDECOR_STRIKE2: - default: - break; - case TXTDECOR_UNDER: - TRPRINT(tri,"text-decoration:underline;"); - break; - case TXTDECOR_OVER: - TRPRINT(tri,"text-decoration:overline;"); - break; - case TXTDECOR_BLINK: - TRPRINT(tri,"text-decoration:blink;"); - break; - case TXTDECOR_STRIKE1: - TRPRINT(tri,"text-decoration:line-through;"); - break; + if(tsp->decoration & TXTDECOR_TMASK){ + sprintf(obuf,"text-decoration:"); + /* multiple text decoration styles may be set */ + utmp = tsp->decoration & TXTDECOR_TMASK; + if(utmp & TXTDECOR_UNDER ){ strcat(obuf,"underline"); } + if(utmp & TXTDECOR_OVER ){ strcat(obuf,"overline"); } + if(utmp & TXTDECOR_BLINK ){ strcat(obuf,"blink"); } + if(utmp & TXTDECOR_STRIKE){ strcat(obuf,"line-through");} + if(*obuf){ + /* only a single text decoration line type may be set */ + switch(tsp->decoration & TXTDECOR_LMASK){ + case TXTDECOR_SOLID: break; // "solid" is the CSS 3 default, omitting it remains CSS 2 compatible + case TXTDECOR_DOUBLE: strcat(obuf," double"); break; // these are all CSS3 + case TXTDECOR_DOTTED: strcat(obuf," dotted"); break; + case TXTDECOR_DASHED: strcat(obuf," dashed"); break; + case TXTDECOR_WAVY: strcat(obuf," wavy" ); break; + default: break; + } + if((tsp->decoration & TXTDECOR_CLRSET) && memcmp(&(tsp->decColor),&(tsp->color),sizeof(TRCOLORREF))){ + /* CSS 3, CSS 2 implementations may choke on it. If the specified color matches text color omit, for better CSS 2 compatitiblity. */ + sprintf(cbuf," #%2.2X%2.2X%2.2X",tsp->decColor.Red,tsp->decColor.Green,tsp->decColor.Blue); + strcat(obuf,cbuf); + } + } + strcat(obuf,";"); + 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); + cutat=strcspn((char *)fti->fonts[tsp->fi_idx].fontspec,":"); + sprintf(obuf,"font-family:%.*s;\"",cutat,fti->fonts[tsp->fi_idx].fontspec); 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"); @@ -1800,7 +2205,7 @@ void TR_layout_2_svg(TR_INFO *tri){ \param tri pointer to the TR_INFO structure holding the data, which will also hold the results. */ int TR_layout_analyze(TR_INFO *tri){ - int i,j; + unsigned int i,j,k; int ok; int cxidx; int src_rt; @@ -1813,7 +2218,17 @@ int TR_layout_analyze(TR_INFO *tri){ RT_PAD rt_pad_i; RT_PAD rt_pad_j; double ratio; + double qsp,dx,dy; + double spcadv; enum tr_classes type; + TCHUNK_SPECS *tspi; + TCHUNK_SPECS *tspj; + TCHUNK_SPECS *tspRevEnd=NULL; + TCHUNK_SPECS *tspRevStart=NULL; + CX_SPECS *csp; + CHILD_SPECS *kidp; /* used with preceding complex (see below) */ + CHILD_SPECS *kidc; /* used with current complex (see below) */ + int lastldir,ldir,rev; if(!tri)return(-1); if(!tri->cxi)return(-2); @@ -1828,27 +2243,48 @@ int TR_layout_analyze(TR_INFO *tri){ cxi->paras = 0; cxi->phase1 = 0; +/* When debugging + ftinfo_dump(fti); +*/ /* 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! */ + tspi = &(tpi->chunks[i]); + memcpy(&bsp,&(bri->rects[tspi->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); + + spcadv = fti->fonts[tspi->fi_idx].spcadv * tspi->fs/32.0; /* spcadv was always FT_LOAD_NO_SCALE */ + /* for the leading text: pad with no leading and two trailing spaces, leading and trailing depend on direction */ + if(tspi->ldir == LDIR_RL){ TR_rt_pad_set(&rt_pad_i,tri->qe, tri->qe, tri->qe + 2.0 * spcadv, 0.0); } + else { TR_rt_pad_set(&rt_pad_i,tri->qe, tri->qe, 0.0, tri->qe + 2.0 * spcadv); } for(j=i+1; j<tpi->used; j++){ + tspj = &(tpi->chunks[j]); /* Reject font size changes of greater than 50%, these are 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); + ratio = (double)(tspj->fs)/(double)(tspi->fs); if(ratio >2.0 || ratio <0.5)break; + + spcadv = fti->fonts[tspj->fi_idx].spcadv * tspj->fs/32.0; /* spcadv was always FT_LOAD_NO_SCALE */ + /* for the trailing text: pad with one leading and trailing spaces (so it should work L->R and R->L) */ + TR_rt_pad_set(&rt_pad_j,tri->qe, tri->qe, spcadv, spcadv); + src_rt = tspj->rt_tidx; + + /* Reject direction changes like [1 <- Hebrew][2 -> English], that is where the direction changes AND the + next logical piece of text is "upstream" positionally of its logical predecessor. The meaning of such + a construct is at best ambiguous. The test is only applied with respect to the first text chunk. This sort + of construct may appear when a valid initial construct like [1->English][2<-Hebrew][3->English] is edited + and the leading chunk of text removed. + */ + if(brinfo_upstream(bri, + dst_rt, /* index into bri for dst */ + src_rt, /* index into bri for src */ + tspi->ldir,tspj->ldir))break; - /* for the trailing text: pad with one leading and trailing spaces (so it should work L->R and R->L */ - TR_rt_pad_set(&rt_pad_j,tri->qe, tri->qe, fti->fonts[tpi->chunks[j].fi_idx].spcadv, fti->fonts[tpi->chunks[j].fi_idx].spcadv); - 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 */ @@ -1856,17 +2292,146 @@ int TR_layout_analyze(TR_INFO *tri){ (void) cxinfo_append(cxi,j,TR_LINE); (void) brinfo_merge(bri,dst_rt,src_rt); /* for the leading text: pad with two leading and trailing spaces (so it should work L->R and R->L */ + spcadv = fti->fonts[tspj->fi_idx].spcadv * tspj->fs/32.0; /* spcadv was always FT_LOAD_NO_SCALE */ TR_rt_pad_set(&rt_pad_i, tri->qe, tri->qe, - tri->qe + 2.0 * fti->fonts[tpi->chunks[j].fi_idx].spcadv, tri->qe + 2.0 * fti->fonts[tpi->chunks[j].fi_idx].spcadv); + tri->qe + 2.0 * spcadv, tri->qe + 2.0 * spcadv); } else { /* either alignment ge*/ break; } } - i=j-1; /* start up after the last merged entry (there may not be any) */ + + /* Bidirectional text will cause complexes to not assemble in one pass. + This happens whenever a change of direction occurs with 2 or more sequential elements in + the opposite direction, + + Let + = LR and - = RL. + + Reading left to right, this happens with +-- or -++. + For instance, the sequence ++-+ ---+ would break into the two complexes shown. + Not until the last element in the second complex is added will the bounding rectangles for the complexes overlap. + + Check for this effect now if there is a preceding complex and the first element of the current complex is + reversed from the last in the preceding. */ + if(cxidx >= 1){ + kidp = &(cxi->cx[cxidx-1].kids); + kidc = &(cxi->cx[cxidx ].kids); + tspi = &(tpi->chunks[ kidp->members[kidp->used - 1] ]); /* here, the last text element in preceding complex */ + tspj = &(tpi->chunks[ kidc->members[0 ] ]); /* here, tge first text element in current complex */ + if(tspi->ldir != tspj->ldir){ + spcadv = fti->fonts[tspi->fi_idx].spcadv * tspi->fs/32.0; + if(tspi->ldir == LDIR_RL){ TR_rt_pad_set(&rt_pad_i,tri->qe, tri->qe, tri->qe + 2.0 * spcadv, 0.0); } + else { TR_rt_pad_set(&rt_pad_i,tri->qe, tri->qe, 0.0, tri->qe + 2.0 * spcadv); } + spcadv = fti->fonts[tspj->fi_idx].spcadv * tspj->fs/32.0; + TR_rt_pad_set(&rt_pad_j,tri->qe, tri->qe, spcadv, spcadv); + if(!brinfo_overlap(bri, + cxi->cx[cxidx-1].rt_cidx, /* index into rt for dst cx */ + cxi->cx[cxidx].rt_cidx, /* index into rt for src cx */ + &rt_pad_i,&rt_pad_j)){ + /* Merge the current complex into the preceding one*/ + (void) cxinfo_merge(cxi, cxidx-1, cxidx, TR_LINE); + (void) brinfo_merge(bri,cxi->cx[cxidx-1].rt_cidx,cxi->cx[cxidx].rt_cidx); /* merge the bounding boxes*/ + (void) cxinfo_trim(cxi); + cxi->lines--; /* else the normal line count value is one too high */ + /* remove the current complex */ + } + } + } + if(cxi->cx[cxidx].type == TR_LINE)cxi->lines++; + i=j-1; /* start up after the last merged entry (there may not be any) */ } cxi->phase1 = cxi->used; /* total complexes defined in this phase, all TR_LINE or TR_TEXT */ + + /* phase 1.5, calculate kerning. This is as good a place to do it as any. At this point all kern values + are zero. Each of these pieces is strictly unidirectional, but each piece can have a different direction. + The direction of the line is set by the first text element. The ends of runs of elements which are + reversed with respect to the line direction are special, everything else is simple: + Let: + == L->R, - == R->L, $ == end of text, the rules for kerning on B are: + A B others xkern + [+|$] + + [+|$] Bll - Aur + [-|$] - - [-|$] All - Bur (chs) + + - + [-|$] Bll - Aur (chs) + - + - [+|$] All - Bur + + - -...[-=C] [+|$] All - Cur (chs) + - + +...[+=C] [-|$] Cll - Aur + + chs = change sign, because dx is an absolute direction, and direction of text on RTL is in -x. + + Kerning calculations currently seems unstable for R->L if the kerning extends to the end of the line. If + the first and last characters are back in sync there are no issues. When things go south R->L left justified + text is not justified when read in. + */ + + for(i=0; i < cxi->phase1; i++){ /* over all lines */ + csp = &(cxi->cx[i]); + if(csp->kids.used < 2)continue; /* no kerning possible */ + tspi = &tpi->chunks[csp->kids.members[0]]; /* used here as last tsp. no kerning is applied to the first element */ + lastldir = ldir = tspi->ldir; + rev = 0; /* the first ldir defines forward and reverse */ + for(j=1; j<csp->kids.used; j++){ + tspj = &tpi->chunks[csp->kids.members[j]]; + ldir = tspj->ldir; + if(ldir != lastldir){ /* direction change */ + rev = !rev; /* reverse direction tracker */ + if(!rev){ /* back in original orientation */ + if(ldir == LDIR_RL){ tspj->xkern = bri->rects[tspj->rt_tidx].xur - bri->rects[tspRevStart->rt_tidx].xll; } + else { tspj->xkern = bri->rects[tspj->rt_tidx].xll - bri->rects[tspRevStart->rt_tidx].xur; } + tspj->ykern = (bri->rects[tspj->rt_tidx].yll - tspj->boff) - + (bri->rects[tspRevStart->rt_tidx].yll - tspRevStart->boff); + } + else { /* now in reversed orientation */ + tspRevStart = tspj; /* Save the beginning of this run (length >=1 ) */ + /* scan forward for the last text object in this orientation, include the first */ + for(k=j; k <csp->kids.used; k++){ + if(tpi->chunks[csp->kids.members[k]].ldir == ldir){ tspRevEnd = &tpi->chunks[csp->kids.members[k]]; } + else { break; } + } + if(lastldir == LDIR_RL){ tspj->xkern = bri->rects[tspRevEnd->rt_tidx].xur - bri->rects[tspi->rt_tidx].xll; } + else { tspj->xkern = bri->rects[tspRevEnd->rt_tidx].xll - bri->rects[tspi->rt_tidx].xur; } + tspj->ykern = (bri->rects[tspRevEnd->rt_tidx].yll - tspRevEnd->boff) - + (bri->rects[ tspi->rt_tidx].yll - tspi->boff ); + } + } + else { + if(ldir == LDIR_RL){ tspj->xkern = bri->rects[tspj->rt_tidx].xur - bri->rects[tspi->rt_tidx].xll; } + else { tspj->xkern = bri->rects[tspj->rt_tidx].xll - bri->rects[tspi->rt_tidx].xur; } + tspj->ykern = (bri->rects[tspj->rt_tidx].yll - tspj->boff) - + (bri->rects[tspi->rt_tidx].yll - tspi->boff); + } + + + /* + Sometimes a font substitution was absolutely terrible, for instance, for Arial Narrow on (most) Linux systems, + The resulting advance (xkern) 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. + The y constraints allow super and subscripts, which overlap in x but are shifted above/below in y. + */ + spcadv = fti->fonts[tspj->fi_idx].spcadv * tspj->fs/32.0; + qsp = 0.25 * spcadv; + dx = tspj->xkern; + dy = tspj->ykern; + if(dy <=qsp && dy >= -qsp){ + if(ldir==LDIR_RL){ + if(dx > 2*qsp)tspj->xkern = 0.0; + } + else { + if(dx < -2*qsp)tspj->xkern = 0.0; + } + } + + /* if x or y kern is less than the quantization error it is probably noise, set it to zero */ + if(fabs(tspj->xkern)<tri->qe)tspj->xkern = 0.0; + if(fabs(tspj->ykern)<tri->qe)tspj->ykern = 0.0; + + + tspi = tspj; + lastldir = ldir; + } + } + /* Phase 2, try to group sequential lines. There may be "lines" that are still TR_TEXT, as in: @@ -1885,10 +2450,13 @@ int TR_layout_analyze(TR_INFO *tri){ 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! */ + /* Must make a copy as next call may reallocate rects, so if we just passed a pointer to something in the structure + it would vaporize part way through the call. */ + memcpy(&bsp,&(bri->rects[cxi->cx[i].rt_cidx]),sizeof(BRECT_SPECS)); (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++){ @@ -1910,8 +2478,8 @@ int TR_layout_analyze(TR_INFO *tri){ src_rt = cxi->cx[j].rt_cidx; (void) cxinfo_append(cxi, j, type); (void) brinfo_merge(bri, dst_rt, src_rt); - } - break; + } + break; default: return(-6); /* programming error */ } @@ -1920,8 +2488,9 @@ int TR_layout_analyze(TR_INFO *tri){ i=j-1; } + /* When debugging - cxinfo_dump(tri); + cxinfo_dump(tri); */ return(cxi->used); @@ -1932,7 +2501,7 @@ int TR_layout_analyze(TR_INFO *tri){ #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,OPDEC,OPCND,OPCLR,OPBKG,OPBCLR,OPFLAGS,OPEMIT,OPDONE}; +enum OP_TYPES {OPCOM,OPOOPS,OPFONT,OPESC,OPORI,OPXY,OPFS,OPTEXT,OPALN,OPLDIR,OPMUL,OPITA,OPWGT,OPDEC,OPCND,OPBKG,OPCLR,OPDCLR,OPBCLR,OPFLAGS,OPEMIT,OPDONE}; int parseit(char *buffer,char **data){ int pre; @@ -1954,8 +2523,9 @@ int parseit(char *buffer,char **data){ if(0==strcmp("WGT", buffer))return(OPWGT ); if(0==strcmp("DEC", buffer))return(OPDEC ); if(0==strcmp("CND", buffer))return(OPCND ); - if(0==strcmp("CLR", buffer))return(OPCLR ); if(0==strcmp("BKG", buffer))return(OPBKG ); + if(0==strcmp("CLR", buffer))return(OPCLR ); + if(0==strcmp("DCLR", buffer))return(OPDCLR ); if(0==strcmp("BCLR",buffer))return(OPBCLR ); if(0==strcmp("FLAG",buffer))return(OPFLAGS); if(0==strcmp("EMIT",buffer))return(OPEMIT); @@ -2066,6 +2636,7 @@ int main(int argc, char *argv[]){ uint32_t utmp32; TRCOLORREF bkcolor; int bkmode; + char *fontspec; infile=malloc(strlen(argv[1])+1); strcpy(infile,argv[1]); @@ -2084,11 +2655,14 @@ int main(int argc, char *argv[]){ 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(" DEC:(Decorate, 00 none, 01 underline, 02 overline, 04 blink, 08 strike1, 10 strike2. SVG only supports some, and only one at a time.)\n"); + printf(" DEC:(this is a bit field. For color see DCLR\n"); + printf(" style: 000 none, 001 underline,002 overline, 004 blink, 008 strike-through\n"); + printf(" line: 000 solid, 010 double, 020 dotted, 040 dashed, 080 wavy)\n"); printf(" CND:(Condensed 50-200: 100=normal, 50=ultracondensed, 75=condensed, 200=expanded).\n"); - printf(" CLR:(Text RGB color, as 6 HEX digits, like: FF0000 (red) or 0000FF (blue)) \n"); printf(" BKG:(Background color: 0 none, 1 by input fragment, 2 by assembled line, 3 by entire assembly. Use BCLR, THEN BKG) \n"); - printf(" BCLR:(Background RGB color, as 6 HEX digits, like: FF0000 (red) or 0000FF (blue)) \n"); + printf(" CLR:(Text RGB color, as 6 HEX digits, like: FF0000 (red) or 0000FF (blue)) \n"); + printf(" DCLR:(Decoration color, specify like CLR, except 1000000 or higher disables.)\n"); + printf(" BCLR:(Background RGB color, specify like CLR.) \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"); @@ -2096,7 +2670,7 @@ int main(int argc, char *argv[]){ 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:|^BKG:|^BCLR:|^CLR:|^FLAG:|^EMIT:^DONE:'\n"); + printf(" egrep pattern: '^LOAD:|^FONT:|^ESC:|^ORI:|^FS:|^XY:|^TEXT:|^ALN:|^LDIR:|^MUL:|^ITA:|^WGT:|^DEC:|^CND:|^BKG:|^CLR:|^BCLR:|^DCLR:|^FLAG:|^EMIT:^DONE:'\n"); exit(EXIT_FAILURE); } @@ -2117,17 +2691,22 @@ int main(int argc, char *argv[]){ tsp.vadvance = 0.0; /* meaningful only when a complex contains two or more lines */ tsp.taln = ALILEFT + ALIBASE; tsp.ldir = LDIR_LR; - tsp.color.Red = 0; /* RGBA Black */ - tsp.color.Green = 0; /* RGBA Black */ - tsp.color.Blue = 0; /* RGBA Black */ - tsp.color.Reserved = 0; /* unused */ + tsp.color.Red = tsp.decColor.Red = 0; /* RGBA Black */ + tsp.color.Green = tsp.decColor.Green = 0; /* RGBA Black */ + tsp.color.Blue = tsp.decColor.Blue = 0; /* RGBA Black */ + tsp.color.Reserved = tsp.decColor.Reserved = 0; /* unused */ tsp.italics = 0; tsp.weight = 80; tsp.condensed = 100; + tsp.decoration = 0; /* none */ tsp.co = 0; tsp.fi_idx = -1; /* set to an invalid */ + tsp.rt_tidx = -1; /* set to an invalid */ + tsp.xkern = tsp.ykern = 0.0; /* no need to set rt_tidx */ + + if(!tri){ fprintf(stderr,"Fatal error, could not initialize data structures\n"); exit(EXIT_FAILURE); @@ -2155,7 +2734,9 @@ int main(int argc, char *argv[]){ else { tsp.co=0; } - if(trinfo_load_fontname(tri, (uint8_t *) data, &tsp))boom("Font load failed",lineno); + fontspec = TR_construct_fontspec(&tsp, data); + if((tsp.fi_idx = ftinfo_load_fontname(tri->fti, fontspec)) < 0 )boom("Font load failed",lineno); + free(fontspec); break; case OPESC: if(1 != sscanf(data,"%lf",&escapement))boom("Invalid ESC:",lineno); @@ -2218,11 +2799,15 @@ int main(int argc, char *argv[]){ if(1 != sscanf(data,"%d",&tsp.weight) || tsp.weight < 0 || tsp.weight > 215)boom("Invalid WGT:",lineno); break; case OPDEC: - if(1 != sscanf(data,"%X",&tsp.decoration))boom("Invalid DEC:",lineno); + if(1 != sscanf(data,"%X",(unsigned int *) &tsp.decoration))boom("Invalid DEC:",lineno); break; case OPCND: if(1 != sscanf(data,"%d",&tsp.condensed) || tsp.condensed < 50 || tsp.condensed > 200)boom("Invalid CND:",lineno); break; + case OPBKG: + if(1 != sscanf(data,"%d",&bkmode) )boom("Invalid BKG:",lineno); + (void) trinfo_load_bk(tri,bkmode,bkcolor); + break; case OPCLR: if(1 != sscanf(data,"%x",&utmp32) )boom("Invalid CLR:",lineno); tsp.color.Red = (utmp32 >> 16) & 0xFF; @@ -2230,9 +2815,19 @@ int main(int argc, char *argv[]){ tsp.color.Blue = (utmp32 >> 0) & 0xFF; tsp.color.Reserved = 0; break; - case OPBKG: - if(1 != sscanf(data,"%d",&bkmode) )boom("Invalid BKG:",lineno); - (void) trinfo_load_bk(tri,bkmode,bkcolor); + case OPDCLR: + if(1 != sscanf(data,"%x",&utmp32) )boom("Invalid DCLR:",lineno); + if(utmp32 >= 0x1000000){ + tsp.decColor.Red = tsp.decColor.Green = tsp.decColor.Blue = tsp.decColor.Reserved = 0; + tsp.decoration &= ~TXTDECOR_CLRSET; + } + else { + tsp.decColor.Red = (utmp32 >> 16) & 0xFF; + tsp.decColor.Green = (utmp32 >> 8) & 0xFF; + tsp.decColor.Blue = (utmp32 >> 0) & 0xFF; + tsp.decColor.Reserved = 0; + tsp.decoration |= TXTDECOR_CLRSET; + } break; case OPBCLR: if(1 != sscanf(data,"%x",&utmp32) )boom("Invalid BCLR:",lineno); diff --git a/src/extension/internal/text_reassemble.h b/src/extension/internal/text_reassemble.h index 950ce1310..d85b233be 100644 --- a/src/extension/internal/text_reassemble.h +++ b/src/extension/internal/text_reassemble.h @@ -4,8 +4,8 @@ See text_reassemble.c for notes File: text_reassemble.h -Version: 0.0.7 -Date: 12-FEB-2013 +Version: 0.0.12 +Date: 14-MAY-2013 Author: David Mathog, Biology Division, Caltech email: mathog@caltech.edu Copyright: 2013 David Mathog and California Institute of Technology (Caltech) @@ -56,18 +56,33 @@ extern "C" { /** \defgroup decoration options One of these values may be present in the decoration field. Unused bits may be used by end user code. - SVG output can specify up to STRIKE1. + These values are SVG specific. Other applications could use the text + decoration field for a different set of bits, so long as it provided its own + output function. @{ */ -#define TXTDECOR_NONE 0x00 /**< text is not decorated (default) */ -#define TXTDECOR_UNDER 0x01 /**< underlined */ -#define TXTDECOR_OVER 0x02 /**< overlined */ -#define TXTDECOR_BLINK 0x04 /**< blinking text */ -#define TXTDECOR_STRIKE1 0x08 /**< single strike throug */ -#define TXTDECOR_STRIKE2 0x10 /**< double strike through */ +#define TXTDECOR_NONE 0x000 /**< text is not decorated (default) */ +#define TXTDECOR_UNDER 0x001 /**< underlined */ +#define TXTDECOR_OVER 0x002 /**< overlined */ +#define TXTDECOR_BLINK 0x004 /**< blinking text */ +#define TXTDECOR_STRIKE 0x008 /**< strike through */ +#define TXTDECOR_TMASK 0x00F /**< Mask for selecting bits above */ + +#define TXTDECOR_SOLID 0x000 /**< draw as single solid line */ +#define TXTDECOR_DOUBLE 0x010 /**< draw as double solid line */ +#define TXTDECOR_DOTTED 0x020 /**< draw as single dotted line */ +#define TXTDECOR_DASHED 0x040 /**< draw as single dashed line */ +#define TXTDECOR_WAVY 0x080 /**< draw as single wavy line */ +#define TXTDECOR_LMASK 0x0F0 /**< Mask for selecting these bits */ + +#define TXTDECOR_CLRSET 0x100 /**< decoration has its own color */ + /** @} */ + + + /** \defgroup text alignment types Location of text's {X,Y} coordinate on bounding rectangle. Values are compatible with Fontconfig. @@ -113,15 +128,27 @@ enum tr_classes { /** @} */ /** + \brief alt font entries. +*/ +typedef struct { + uint32_t fi_idx; /**< index into FT_INFO fonts, for fonts added for missing glyphs */ + uint32_t weight; /**< integer weight for alt fonts, kept sorted into descending order */ +} ALT_SPECS; + +/** \brief Information for a font instance. */ typedef struct { + FcFontSet *fontset; /**< all matching fonts (for fallback on missing glyphs) */ + ALT_SPECS *alts; /**< index into FT_INFO fonts, for fonts added for missing glyphs */ + uint32_t space; /**< alts storage slots allocated */ + uint32_t used; /**< alts storage slots in use */ 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 */ + uint8_t *file; /**< pointer to font paths to files */ + uint8_t *fontspec; /**< pointer to a font specification (name:italics, etc.) */ + FcPattern *fpat; /**< current font, must hang onto this or faces operations break */ + double spcadv; /**< advance equal to a space, in points at font's face size */ + double fsize; /**< font's face size in points */ } FNT_SPECS; /** @@ -130,8 +157,8 @@ typedef struct { 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 */ + uint32_t space; /**< storage slots allocated */ + uint32_t used; /**< storage slots in use */ } FT_INFO; typedef struct { @@ -150,6 +177,8 @@ typedef struct { double fs; /**< font size of text */ double x; /**< x coordinate, relative to TR_INFO x,y, in points */ double y; /**< y coordinate, relative to TR_INFO x,y, in points */ + double xkern; /**< x kern relative to preceding text chunk in complex (if any) */ + double ykern; /**< y kern relative to preceding text chunk in complex (if any) */ 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 */ @@ -159,7 +188,8 @@ typedef struct { int italics; /**< italics, as in FontConfig */ int weight; /**< weight, as in FontConfig */ int condensed; /**< condensed, as in FontConfig */ - int decoration; /**< text decorations, ignored during assembly,used during output */ + int decoration; /**< text decorations, ignored during assembly, used during output */ + TRCOLORREF decColor; /**< text decoration color, ignored during assembly, used during output */ 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 */ @@ -171,8 +201,8 @@ typedef struct { */ typedef struct { TCHUNK_SPECS *chunks; /**< text chunks */ - int space; /**< storage slots allocated */ - int used; /**< storage slots in use */ + uint32_t space; /**< storage slots allocated */ + uint32_t used; /**< storage slots in use */ } TP_INFO; /** @@ -192,8 +222,8 @@ typedef struct { */ typedef struct { BRECT_SPECS *rects; /**< bounding rectangles */ - int space; /**< storage slots allocated */ - int used; /**< storage slots in use */ + uint32_t space; /**< storage slots allocated */ + uint32_t used; /**< storage slots in use */ } BR_INFO; /** @@ -203,8 +233,8 @@ 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 */ + uint32_t space; /**< storage slots allocated */ + uint32_t used; /**< storage slots in use */ } CHILD_SPECS; /** @@ -222,11 +252,11 @@ typedef struct { */ 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 */ + uint32_t space; /**< storage slots allocated */ + uint32_t used; /**< storage slots in use */ + uint32_t phase1; /**< Number of complexes (lines + text fragments) entered in phase 1 */ + uint32_t lines; /**< Number of lines in phase 1 */ + uint32_t paras; /**< Number of complexes (paras) entered in phase 2 */ } CX_INFO; /** @@ -246,8 +276,8 @@ typedef struct { 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 */ + uint32_t outspace; /**< storage in output buffer allocated */ + uint32_t outused; /**< storage in output buffer in use */ int usebk; /**< On output write the background color under the text */ TRCOLORREF bkcolor; /**< RGB background color */ } TR_INFO; @@ -279,8 +309,11 @@ typedef struct { /** \endcond */ /* 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_findcasesub(const char *string, const char *sub); +char *TR_construct_fontspec(const TCHUNK_SPECS *tsp, const char *fontname); +char *TR_reconstruct_fontspec(const char *fontspec, const char *fontname); +int TR_find_alternate_font(FT_INFO *fti, FNT_SPECS **efsp, uint32_t wc); +int TR_getadvance(FT_INFO *fti, 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); @@ -295,45 +328,57 @@ 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 ftinfo_find_loaded_by_spec(const FT_INFO *fti, const uint8_t *fname); +int ftinfo_find_loaded_by_src(const FT_INFO *fti, const uint8_t *filename); +int ftinfo_load_fontname(FT_INFO *fti, const char *fontspec); +void ftinfo_dump(const FT_INFO *fti); + +int fsp_alts_make_insertable(FNT_SPECS *fsp); +int fsp_alts_insert(FNT_SPECS *fsp, uint32_t fi_idx); +int fsp_alts_weight(FNT_SPECS *fsp, uint32_t a_idx); 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); +void csp_clear(CHILD_SPECS *csp); 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); +int cxinfo_trim(CX_INFO *cxi); CX_INFO *cxinfo_release(CX_INFO *cxi); -void cxinfo_dump(TR_INFO *tri); +void cxinfo_dump(const TR_INFO *tri); TP_INFO *tpinfo_init(void); int tpinfo_make_insertable(TP_INFO *tpi); -int tpinfo_insert(TP_INFO *tpi, TCHUNK_SPECS *tsp); +int tpinfo_insert(TP_INFO *tpi, const 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_insert(BR_INFO *bri, const 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); + brinfo_pp_alignment(const BR_INFO *bri, int dst, int src, double slop, enum tr_classes type); +int brinfo_overlap(const 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_bk(TR_INFO *tri, int usebk, TRCOLORREF bkcolor); 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_load_textrec(TR_INFO *tri, const TCHUNK_SPECS *tsp, double escapement, int flags); int trinfo_check_bk(TR_INFO *tri, int usebk, TRCOLORREF bkcolor); -int trinfo_append_out(TR_INFO *tri, char *src); +int trinfo_append_out(TR_INFO *tri, const char *src); + +int is_mn_unicode(int test); + #ifdef __cplusplus } diff --git a/src/extension/internal/wmf-inout.cpp b/src/extension/internal/wmf-inout.cpp index 56757d0d0..4c69c76a0 100644 --- a/src/extension/internal/wmf-inout.cpp +++ b/src/extension/internal/wmf-inout.cpp @@ -1278,8 +1278,10 @@ Wmf::select_font(PWMF_CALLBACK_DATA d, int index) font.Weight == U_FW_EXTRABOLD ? SP_CSS_FONT_WEIGHT_BOLDER : U_FW_NORMAL; d->dc[d->level].style.font_style.value = (font.Italic ? SP_CSS_FONT_STYLE_ITALIC : SP_CSS_FONT_STYLE_NORMAL); - d->dc[d->level].style.text_decoration.underline = font.Underline; - d->dc[d->level].style.text_decoration.line_through = font.StrikeOut; + d->dc[d->level].style.text_decoration_line.underline = font.Underline; + d->dc[d->level].style.text_decoration_line.line_through = font.StrikeOut; + d->dc[d->level].style.text_decoration_line.set = true; + d->dc[d->level].style.text_decoration_line.inherit = false; // malformed WMF with empty filename may exist, ignore font change if encountered if(d->dc[d->level].font_name)free(d->dc[d->level].font_name); @@ -1325,13 +1327,13 @@ Wmf::delete_object(PWMF_CALLBACK_DATA d, int index) else if(index == d->dc[d->level].active_font){ d->dc[d->level].active_font = -1; if(d->dc[d->level].font_name){ free(d->dc[d->level].font_name);} - d->dc[d->level].font_name = strdup("Arial"); // Default font, WMF spec says device can pick whatever it wants - d->dc[d->level].style.font_size.computed = 16.0; - d->dc[d->level].style.font_weight.value = SP_CSS_FONT_WEIGHT_400; - d->dc[d->level].style.font_style.value = SP_CSS_FONT_STYLE_NORMAL; - d->dc[d->level].style.text_decoration.underline = 0; - d->dc[d->level].style.text_decoration.line_through = 0; - d->dc[d->level].style.baseline_shift.value = 0; + d->dc[d->level].font_name = strdup("Arial"); // Default font, WMF spec says device can pick whatever it wants + d->dc[d->level].style.font_size.computed = 16.0; + d->dc[d->level].style.font_weight.value = SP_CSS_FONT_WEIGHT_400; + d->dc[d->level].style.font_style.value = SP_CSS_FONT_STYLE_NORMAL; + d->dc[d->level].style.text_decoration_line.underline = 0; + d->dc[d->level].style.text_decoration_line.line_through = 0; + d->dc[d->level].style.baseline_shift.value = 0; } @@ -2522,6 +2524,7 @@ std::cout << "BEFORE DRAW" dbg_str << "<!-- U_WMR_EXTTEXTOUT -->\n"; nSize = U_WMREXTTEXTOUT_get(contents, &Dst, &tlen, &Opts, &text, &dx, &rc ); } + uint32_t fOptions = Opts; double x1,y1; int cChars; @@ -2597,11 +2600,10 @@ std::cout << "BEFORE DRAW" case SP_CSS_FONT_WEIGHT_BOLDER: tsp.weight = FC_WEIGHT_EXTRABOLD ; break; default: tsp.weight = FC_WEIGHT_NORMAL ; break; } - - // Inkscape cannot display underline or strike-through at present, but enter it into the SVG in any case. - if( d->dc[d->level].style.text_decoration.underline){ tsp.decoration = TXTDECOR_UNDER; } - else if (d->dc[d->level].style.text_decoration.line_through){ tsp.decoration = TXTDECOR_STRIKE1; } - else { tsp.decoration = TXTDECOR_NONE; } + // WMF only supports two types of text decoration + tsp.decoration = TXTDECOR_NONE; + if(d->dc[d->level].style.text_decoration_line.underline){ tsp.decoration |= TXTDECOR_UNDER; } + if(d->dc[d->level].style.text_decoration_line.line_through){ tsp.decoration |= TXTDECOR_STRIKE;} // WMF textalignment is a bit strange: 0x6 is center, 0x2 is right, 0x0 is left, the value 0x4 is also drawn left tsp.taln = ((d->dc[d->level].textAlign & U_TA_CENTER) == U_TA_CENTER) ? ALICENTER : @@ -2610,13 +2612,19 @@ std::cout << "BEFORE DRAW" 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 + + // language direction can be encoded two ways, U_TA_RTLREADING is preferred + if( (fOptions & U_ETO_RTLREADING) || (d->dc[d->level].textAlign & U_TA_RTLREADING) ){ tsp.ldir = LDIR_RL; } + else{ tsp.ldir = LDIR_LR; } + 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 // There is no world transform, so ori need not be further rotated 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); + char *fontspec = TR_construct_fontspec(&tsp, d->dc[d->level].font_name); + tsp.fi_idx = ftinfo_load_fontname(d->tri->fti,fontspec); + free(fontspec); // 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; } @@ -3074,8 +3082,8 @@ Wmf::open( Inkscape::Extension::Input * /*mod*/, const gchar *uri ) d.dc[0].style.font_size.computed = 16.0; d.dc[0].style.font_weight.value = SP_CSS_FONT_WEIGHT_400; d.dc[0].style.font_style.value = SP_CSS_FONT_STYLE_NORMAL; - d.dc[0].style.text_decoration.underline = 0; - d.dc[0].style.text_decoration.line_through = 0; + d.dc[0].style.text_decoration_line.underline = 0; + d.dc[0].style.text_decoration_line.line_through = 0; d.dc[0].style.baseline_shift.value = 0; d.dc[0].textColor = U_RGB(0, 0, 0); // default foreground color (black) d.dc[0].bkColor = U_RGB(255, 255, 255); // default background color (white) diff --git a/src/extension/internal/wmf-print.cpp b/src/extension/internal/wmf-print.cpp index 5ac8d46eb..e91a74b20 100644 --- a/src/extension/internal/wmf-print.cpp +++ b/src/extension/internal/wmf-print.cpp @@ -211,7 +211,7 @@ void PrintWmf::search_short_fflist(const char *fontname, double *f1, double *f2 search_long_fflist(fontname, f1, f2, f3); } -void PrintWmf::smuggle_adxky_out(const char *string, int16_t **adx, double *ky, int *ndx, float scale){ +void PrintWmf::smuggle_adxky_out(const char *string, int16_t **adx, double *ky, int *rtl, int *ndx, float scale){ float fdx; int i; int16_t *ladx; @@ -232,6 +232,8 @@ void PrintWmf::smuggle_adxky_out(const char *string, int16_t **adx, double *ky, cptr++; // skip 2nd fake terminator sscanf(cptr,"%7f",&fdx); *ky=fdx; + cptr += 7; // advance over ky and its space + sscanf(cptr,"%07d",rtl); } /* convert an 0RGB color to EMF U_COLORREF. @@ -307,6 +309,7 @@ unsigned int PrintWmf::begin (Inkscape::Extension::Print *mod, SPDocument *doc) // initialize a few global variables hbrush = hpen = 0; + htextalignment = U_TA_BASELINE | U_TA_LEFT; use_stroke = use_fill = simple_shape = usebk = false; Inkscape::XML::Node *nv = sp_repr_lookup_name (doc->rroot, "sodipodi:namedview"); @@ -383,19 +386,19 @@ unsigned int PrintWmf::begin (Inkscape::Extension::Print *mod, SPDocument *doc) g_error("Fatal programming error in PrintWmf::begin at U_WMRSETPOLYFILLMODE"); } - // Text alignment: (Never changes) + // Text alignment: (only changed if RTL text is encountered ) // - (x,y) coordinates received by this filter are those of the point where the text // actually starts, and already takes into account the text object's alignment; // - for this reason, the WMF text alignment must always be TA_BASELINE|TA_LEFT. rec = U_WMRSETTEXTALIGN_set(U_TA_BASELINE | U_TA_LEFT); if(!rec || wmf_append((PU_METARECORD)rec, wt, U_REC_FREE)){ - g_error("Fatal programming error in PrintWmf::text at U_WMRSETTEXTALIGN_set"); + g_error("Fatal programming error in PrintWmf::begin at U_WMRSETTEXTALIGN_set"); } htextcolor_rgb[0] = htextcolor_rgb[1] = htextcolor_rgb[2] = 0.0; rec = U_WMRSETTEXTCOLOR_set(U_RGB(0,0,0)); if(!rec || wmf_append((PU_METARECORD)rec, wt, U_REC_FREE)){ - g_error("Fatal programming error in PrintWmf::text at U_WMRSETTEXTCOLOR_set"); + g_error("Fatal programming error in PrintWmf::begin at U_WMRSETTEXTCOLOR_set"); } rec = U_WMRSETROP2_set(U_R2_COPYPEN); @@ -1273,8 +1276,8 @@ unsigned int PrintWmf::fill( } } if ( - (style->stroke.noneSet || style->stroke_width.computed == 0.0) || - (style->stroke_dash.n_dash && style->stroke_dash.dash && FixPPTDashLine) || + (style->stroke.isNone() || style->stroke.noneSet || style->stroke_width.computed == 0.0) || + (style->stroke_dash.n_dash && style->stroke_dash.dash && FixPPTDashLine) || !all_closed ){ print_pathv(pathv, fill_transform); // do any fills. side effect: clears fill_pathv @@ -1767,10 +1770,21 @@ unsigned int PrintWmf::text(Inkscape::Extension::Print * /*mod*/, char const *te double ky; // the dx array is smuggled in like: text<nul>w1 w2 w3 ...wn<nul><nul>, where the widths are floats 7 characters wide, including the space - int ndx; + int ndx, rtl; int16_t *adx; - smuggle_adxky_out(text, &adx, &ky, &ndx, PX2WORLD * std::min(tf.expansionX(),tf.expansionY())); // side effect: free() adx + smuggle_adxky_out(text, &adx, &ky, &rtl, &ndx, PX2WORLD * std::min(tf.expansionX(),tf.expansionY())); // side effect: free() adx + uint32_t textalignment; + if(rtl > 0){ textalignment = U_TA_BASELINE | U_TA_LEFT; } + else { textalignment = U_TA_BASELINE | U_TA_RIGHT | U_TA_RTLREADING; } + if(textalignment != htextalignment){ + htextalignment = textalignment; + rec = U_WMRSETTEXTALIGN_set(textalignment); + if(!rec || wmf_append((PU_METARECORD)rec, wt, U_REC_FREE)){ + g_error("Fatal programming error in PrintWmf::text at U_WMRSETTEXTALIGN_set"); + } + } + char *text2 = strdup(text); // because U_Utf8ToUtf16le calls iconv which does not like a const char * uint16_t *unicode_text = U_Utf8ToUtf16le( text2, 0, NULL ); free(text2); @@ -1838,8 +1852,8 @@ unsigned int PrintWmf::text(Inkscape::Extension::Print * /*mod*/, char const *te round(rot), transweight(style->font_weight.computed), (style->font_style.computed == SP_CSS_FONT_STYLE_ITALIC), - style->text_decoration.underline, - style->text_decoration.line_through, + style->text_decoration_line.underline, + style->text_decoration_line.line_through, U_DEFAULT_CHARSET, U_OUT_DEFAULT_PRECIS, U_CLIP_DEFAULT_PRECIS, @@ -1922,7 +1936,12 @@ unsigned int PrintWmf::text(Inkscape::Extension::Print * /*mod*/, char const *te // This is currently being smuggled in from caller as part of text, works // MUCH better than the fallback hack below // uint32_t *adx = dx_set(textheight, U_FW_NORMAL, slen); // dx is needed, this makes one up - rec = U_WMREXTTEXTOUT_set((U_POINT16) {xpos, ypos}, ndx, U_ETO_NONE, latin1_text, adx, U_RCL16_DEF); + if(rtl>0){ + rec = U_WMREXTTEXTOUT_set((U_POINT16) {xpos, ypos}, ndx, U_ETO_NONE, latin1_text, adx, U_RCL16_DEF); + } + else { // RTL text, U_TA_RTLREADING should be enough, but set this one too just in case + rec = U_WMREXTTEXTOUT_set((U_POINT16) {xpos, ypos}, ndx, U_ETO_RTLREADING, latin1_text, adx, U_RCL16_DEF); + } free(latin1_text); free(adx); if(!rec || wmf_append((PU_METARECORD)rec, wt, U_REC_FREE)){ diff --git a/src/extension/internal/wmf-print.h b/src/extension/internal/wmf-print.h index 02749c9de..5fe316417 100644 --- a/src/extension/internal/wmf-print.h +++ b/src/extension/internal/wmf-print.h @@ -38,6 +38,7 @@ class PrintWmf : public Inkscape::Extension::Implementation::Implementation double _height; U_RECTL rc; + uint32_t htextalignment; uint32_t hbrush, hpen, hpenOld, hbrush_null, hpen_null; uint32_t hmiterlimit; // used to minimize redundant records that set this uint32_t hpolyfillmode; // used to minimize redundant records that set this @@ -94,7 +95,7 @@ protected: static void read_system_fflist(void); //this is not called by any other source files static void search_long_fflist(const char *fontname, double *f1, double *f2, double *f3); static void search_short_fflist(const char *fontname, double *f1, double *f2, double *f3); - static void smuggle_adxky_out(const char *string, int16_t **adx, double *ky, int *ndx, float scale); + static void smuggle_adxky_out(const char *string, int16_t **adx, double *ky, int *rtl, int *ndx, float scale); U_COLORREF gethexcolor(uint32_t color); uint32_t transweight(const unsigned int inkweight); |
