1 /* *************************************************************************
2                           gdlgstream.cpp  -  graphic stream
3                              -------------------
4     begin                : July 22 2002
5     copyright            : (C) 2002 by Marc Schellens
6     email                : m_schellens@users.sf.net
7  ***************************************************************************/
8 
9 /* *************************************************************************
10  *                                                                         *
11  *   This program is free software; you can redistribute it and/or modify  *
12  *   it under the terms of the GNU General Public License as published by  *
13  *   the Free Software Foundation; either version 2 of the License, or     *
14  *   (at your option) any later version.                                   *
15  *                                                                         *
16  ***************************************************************************/
17 
18 #include "includefirst.hpp"
19 
20 #include <iostream>
21 
22 #include "graphicsdevice.hpp"
23 #include "gdlgstream.hpp"
24 #include "initsysvar.hpp"
25 
26 using namespace std;
27 
28 // bool GDLGStream::plstreamInitCalled = false;
29 
30 // void PLPlotAbortHandler(const char *c)
31 // {
32 //   cout << "PLPlot abort handler: " << c << endl;
33 // }
34 //
35 // int PLPlotExitHandler(const char *c)
36 // {
37 //   cout << "PLPlot exit handler: " << c << endl;
38 //   return 0;
39 // }
40 //
41 // void GDLGStream::SetErrorHandlers()
42 // {
43 //   plsexit( PLPlotExitHandler);
44 //   plsabort( PLPlotAbortHandler);
45 // }
46 
Thick(DFloat thick)47 void GDLGStream::Thick(DFloat thick)
48 {
49   //note that 'cmake' may not able to find correct value of HAVE_PLPLOT_WIDTH. Please report.
50   // in the meantime, you may edit "config.h" by hand.
51 #ifdef HAVE_PLPLOT_WIDTH
52     plstream::width(static_cast<PLFLT>(thick*thickFactor));
53 #else
54     plstream::wid(static_cast<PLINT>(floor((thick*thickFactor)-0.5)));
55 #endif
56 }
57 
58 #define BLACK 0
59 #define WHITE 16777215
Color(ULong color,DLong decomposed)60 void GDLGStream::Color( ULong color, DLong decomposed) {
61     bool printer = (((*static_cast<DLongGDL*> (SysVar::D()->GetTag(SysVar::D()->Desc()->TagIndex("FLAGS"), 0)))[0] & 512) == 512);
62     bool bw = (((*static_cast<DLongGDL*> (SysVar::D()->GetTag(SysVar::D()->Desc()->TagIndex("FLAGS"), 0)))[0] & 16) == 0); //in that case,
63     //plplot postscript driver uses gray levels instead of colorindex, and 1 is black, not 0 !!!
64     if (decomposed == 0) {
65       if (printer && (color & 0xFF) == 0) { color=(bw)?WHITE:BLACK; //note that if bw other colors will be a gray value
66         GDLGStream::SetColorMap1SingleColor(color);
67         plstream::col1(1); //send specifically color ZERO = black.
68         return;
69       } else plstream::col0(color & 0xFF); //just set color index [0..255]. simple and fast.
70     } else {
71       if (printer && color == 0) color=(bw)?WHITE:BLACK;
72       GDLGStream::SetColorMap1SingleColor(color);
73       plstream::col1(1); //send specifically color ZERO = black.
74       return;
75     }
76 }
77 #undef BLACK
78 
SetColorMap1SingleColor(ULong color)79 void GDLGStream::SetColorMap1SingleColor( ULong color)
80 {
81     PLINT red[2],green[2],blue[2];
82     red[0] =red[1] = color & 0xFF;
83     green[0] = green[1] =(color >> 8)  & 0xFF;
84     blue[0]= blue[1]=(color >> 16) & 0xFF;
85     SetColorMap1(red, green, blue, 2);
86 }
87 
SetColorMap1DefaultColors(PLINT ncolors,DLong decomposed)88 void GDLGStream::SetColorMap1DefaultColors(PLINT ncolors, DLong decomposed)
89 {
90   if (decomposed == 0) { //just copy Table0 to Table1 so that scale from 0 to 1 in table 1 goes through the whole table
91     PLINT r[ctSize], g[ctSize], b[ctSize];
92     GraphicsDevice::GetCT()->Get( r, g, b);
93     SetColorMap1(r, g, b, ctSize);
94   } else {
95     PLFLT r[2], g[2], b[2], pos[2];
96     r[0] = pos[0] = 0.0;
97     r[1] = pos[1] = 1.0;
98     g[0] = g[1] = 0.0;
99     b[0] = b[1] = 0.0;
100     SetColorMap1n(ncolors);
101     SetColorMap1l(TRUE,2,pos,r, g, b, NULL);
102   }
103 }
104 
SetColorMap1Table(PLINT tableSize,BaseGDL * passed_colors,DLong decomposed)105 void GDLGStream::SetColorMap1Table( PLINT tableSize, BaseGDL *passed_colors,  DLong decomposed)
106 { //cycle on passed colors to fill tableSize.
107   DLongGDL *colors=static_cast<DLongGDL*>(passed_colors);
108   DLong n=colors->N_Elements();
109 #ifdef _MSC_VER
110   PLINT *r = (PLINT*)alloca(sizeof(PLINT)*tableSize);
111   PLINT *g = (PLINT*)alloca(sizeof(PLINT)*tableSize);
112   PLINT *b = (PLINT*)alloca(sizeof(PLINT)*tableSize);
113 #else
114   PLINT r[tableSize], g[tableSize], b[tableSize];
115 #endif
116   if (decomposed == 0) {
117     PLINT red[ctSize], green[ctSize], blue[ctSize], col;
118     GraphicsDevice::GetCT()->Get( red, green, blue);
119     for (SizeT i=0; i< tableSize; ++i) {
120       col = (*colors)[i%n]& 0xFF;
121       r[i] = red[col];
122       g[i] = green[col];
123       b[i] = blue[col];
124     }
125   } else {
126     PLINT col;
127      for (SizeT i=0; i< tableSize; ++i) {
128       col = (*colors)[i%n];
129       r[i] =  col        & 0xFF;
130       g[i] = (col >> 8)  & 0xFF;
131       b[i] = (col >> 16) & 0xFF;
132      }
133   }
134   SetColorMap1(r, g, b, tableSize);
135 }
136 
SetColorMap1Ramp(DLong decomposed,PLFLT minlight)137 void GDLGStream::SetColorMap1Ramp(DLong decomposed, PLFLT minlight)
138 { //cycle on passed colors to fill table1 with ramp.
139     PLFLT h[2], l[2], s[2], pos[2];
140     h[0] = h[1] = s[0] = s[1] = pos[0] = 0.0;
141     l[0] = minlight;
142     l[1] = pos[1] = 1.0;
143     SetColorMap1n(256);
144     SetColorMap1l(FALSE,2,pos,h, l, s, NULL);
145 }
146 #define WHITEB 255
Background(ULong color,DLong decomposed)147 void GDLGStream::Background( ULong color, DLong decomposed)
148 {
149   if ((*static_cast<DLongGDL*>(SysVar::D()->GetTag(SysVar::D()->Desc()->TagIndex("FLAGS"), 0)))[0] & 512 ) {  ;//printer like PostScript
150       GraphicsDevice::GetDevice()->SetDeviceBckColor(WHITEB, WHITEB, WHITEB );
151    return;
152   }
153   DByte r,g,b;
154   PLINT red,green,blue;
155   if (decomposed == 0) { //just an index
156     GraphicsDevice::GetCT()->Get( color & 0xFF, r, g, b);
157     red=r; green=g; blue=b;
158   } else {
159     red = color & 0xFF;
160     green = (color >> 8)  & 0xFF;
161     blue = (color >> 16) & 0xFF;
162   }
163   GraphicsDevice::GetDevice()->SetDeviceBckColor( red, green, blue);
164 }
DefaultBackground()165 void GDLGStream::DefaultBackground()
166 {
167   if ((*static_cast<DLongGDL*>(SysVar::D()->GetTag(SysVar::D()->Desc()->TagIndex("FLAGS"), 0)))[0] & 512 ) {  ;//printer like PostScript
168     GraphicsDevice::GetDevice()->SetDeviceBckColor(WHITEB, WHITEB, WHITEB );
169     return;
170   }
171   DStructGDL* pStruct=SysVar::P();   //MUST NOT BE STATIC, due to .reset
172   DLong background=(*static_cast<DLongGDL*>(pStruct->GetTag(pStruct->Desc()->TagIndex("BACKGROUND"), 0)))[0];
173   DByte r,g,b;
174   PLINT red,green,blue;
175   if (GraphicsDevice::GetDevice()->GetDecomposed() == 0) { //just an index
176     GraphicsDevice::GetCT()->Get( background & 0xFF, r, g, b);
177     red=r; green=g; blue=b;
178   } else {
179     red = background & 0xFF;
180     green = (background >> 8)  & 0xFF;
181     blue = (background >> 16) & 0xFF;
182   }
183   GraphicsDevice::GetDevice()->SetDeviceBckColor( red, green, blue);
184 }
185 #undef WHITEB
DefaultCharSize()186 void GDLGStream::DefaultCharSize() {
187   DStructGDL* d = SysVar::D();
188   DStructDesc* s = d->Desc();
189   int X_CH_SIZE = s->TagIndex("X_CH_SIZE");
190   int Y_CH_SIZE = s->TagIndex("Y_CH_SIZE");
191   int X_PX_CM = s->TagIndex("X_PX_CM");
192   int Y_PX_CM = s->TagIndex("Y_PX_CM");
193   DLong chx = (*static_cast<DLongGDL*> (d->GetTag(X_CH_SIZE, 0)))[0];
194   DLong chy = (*static_cast<DLongGDL*> (d->GetTag(Y_CH_SIZE, 0)))[0];
195   DFloat xpxcm = (*static_cast<DFloatGDL*> (d->GetTag(X_PX_CM, 0)))[0];
196   DFloat ypxcm = (*static_cast<DFloatGDL*> (d->GetTag(Y_PX_CM, 0)))[0];
197   DFloat xchsizemm = GetPlplotFudge() * chx * CM_IN_MM / xpxcm;
198   DFloat linespacingmm = GetPlplotFudge() * chy * CM_IN_MM / ypxcm;
199   schr(xchsizemm, 1.0, linespacingmm);
200 }
RenewPlplotDefaultCharsize(PLFLT newMmSize)201   void GDLGStream::RenewPlplotDefaultCharsize(PLFLT newMmSize)
202   {
203     plstream::schr(newMmSize, 1.0);
204     gdlDefaultCharInitialized=0;
205     GetPlplotDefaultCharSize();
206   }
207 
GetPlplotDefaultCharSize()208   void GDLGStream::GetPlplotDefaultCharSize()
209   {
210 
211     if (GDL_DEBUG_PLSTREAM) fprintf(stderr,"GetPlPlotDefaultCharsize()\n");
212     if (thePage.nbPages==0)   {return;}
213     //dimensions in normalized, device and millimetres
214     if (gdlDefaultCharInitialized==1) {if (GDL_DEBUG_PLSTREAM) fprintf(stderr,"     Already initialized\n"); return;}
215     theDefaultChar.scale=1.0;
216     theDefaultChar.mmsx=pls->chrht; //millimeter
217     theDefaultChar.mmsy=pls->chrht;
218     theDefaultChar.fudge=GetPlplotFudge();
219     theDefaultChar.ndsx=mm2ndx(theDefaultChar.mmsx); //normalized device
220     theDefaultChar.ndsy=mm2ndy(theDefaultChar.mmsy);
221     theDefaultChar.dsy=theDefaultChar.ndsy*thePage.height;
222     theDefaultChar.dsx=theDefaultChar.ndsx*thePage.length;
223     theDefaultChar.mmspacing=theLineSpacing_in_mm;
224     theDefaultChar.nspacing=mm2ndy(theDefaultChar.mmspacing);
225     theDefaultChar.dspacing=theDefaultChar.nspacing*thePage.height;
226     theDefaultChar.wspacing=mm2wy(theDefaultChar.mmspacing);
227 
228     theDefaultChar.wsx=mm2wx(theDefaultChar.mmsx); //world
229     theDefaultChar.wsy=mm2wy(theDefaultChar.mmsy);
230     if (GDL_DEBUG_PLSTREAM) fprintf(stderr,"             %fx%f,%f (mm)\n",theDefaultChar.mmsx   ,theDefaultChar.mmsy ,theDefaultChar.mmspacing);
231     if (GDL_DEBUG_PLSTREAM) fprintf(stderr,"             %fx%f,%f (norm)\n",theDefaultChar.ndsx ,theDefaultChar.ndsy ,theDefaultChar.nspacing);
232     if (GDL_DEBUG_PLSTREAM) fprintf(stderr,"             %fx%f,%f (dev)\n",theDefaultChar.dsx   ,theDefaultChar.dsy  ,theDefaultChar.dspacing);
233     if (GDL_DEBUG_PLSTREAM) fprintf(stderr,"             %fx%f,%f (world)\n",theDefaultChar.wsx ,theDefaultChar.wsy  ,theDefaultChar.wspacing);
234     gdlDefaultCharInitialized=1;
235   }
236 
NextPlot(bool erase)237 void GDLGStream::NextPlot( bool erase )
238 {
239   DLongGDL* pMulti = SysVar::GetPMulti();
240 
241   DLong nx = (*pMulti)[ 1];
242   DLong ny = (*pMulti)[ 2];
243   DLong nz = (*pMulti)[ 3];
244 
245   DLong dir = (*pMulti)[ 4];
246 
247   nx = (nx>0)?nx:1;
248   ny = (ny>0)?ny:1;
249   nz = (nz>0)?nz:1;
250   if (GDL_DEBUG_PLSTREAM) fprintf(stderr,"NextPlot(erase=%d)\n",erase);
251   // set subpage numbers in X and Y
252 //  plstream::ssub( nx, ny ); // ssub does not change charsize it seems
253   ssub( nx, ny );
254   DLong pMod = (*pMulti)[0] % (nx*ny);
255 
256 //  if( (*pMulti)[0] <= 0 || (*pMulti)[0] == nx*ny) // clear and restart to first subpage
257   if( pMod == 0 ) // clear and restart to first subpage
258   {
259     if( erase )
260     {
261       eop();           // overridden (for Z-buffer)
262       //get background value (*not pen 0*, we try to avoid plplot's silly behaviour).
263       //use it for bop(), then reset the pen 0 to correct value.
264 
265       PLINT red,green,blue;
266       DByte r,g,b;
267       PLINT red0,green0,blue0;
268 
269       GraphicsDevice::GetCT()->Get(0,r,g,b);red=r;green=g;blue=b;
270 
271       red0=GraphicsDevice::GetDevice()->BackgroundR();
272       green0=GraphicsDevice::GetDevice()->BackgroundG();
273       blue0=GraphicsDevice::GetDevice()->BackgroundB();
274       plstream::scolbg(red0,green0,blue0); //overwrites col[0]
275       plstream::bop(); // note: changes charsize
276       plstream::scolbg(red,green,blue); //resets col[0]
277     }
278 
279 //    plstream::adv(1); //advance to first subpage
280     adv(1); //advance to first subpage
281     (*pMulti)[0] = nx*ny*nz-1; //set PMULTI[0] to this page
282   }
283   else
284   {
285     if( dir == 0 )
286     {
287 //      plstream::adv(nx*ny - pMod + 1);
288       adv(nx*ny - pMod + 1);
289     }
290     else
291     {
292       int p = nx*ny - pMod;
293       int pp = p*nx % (nx*ny) + p/ny + 1;
294 //      plstream::adv(pp);
295       adv(pp);
296     }
297     if( erase )
298     {
299       --(*pMulti)[0];
300     }
301   }
302   // restore charsize to default for newpage
303   sizeChar(1.0);
304 }
305 
NoSub()306 void GDLGStream::NoSub()
307 {
308   if (GDL_DEBUG_PLSTREAM) fprintf(stderr,"NoSub()\n");
309   ssub( 1, 1); // changes charsize ?
310 //plstream::adv( 0);
311   adv( 0);
312 //  DefaultCharSize();
313 }
314 
315 
316 // default is a wrapper for gpage(). Is overriden by, e.g., X driver.
GetGeometry(long & xSize,long & ySize)317 void GDLGStream::GetGeometry( long& xSize, long& ySize)
318 {
319   if (GDL_DEBUG_PLSTREAM) fprintf(stderr,"GDLGStream::GetGeometry()\n");
320   PLFLT xp; PLFLT yp;
321   PLINT xleng; PLINT yleng;
322   PLINT plxoff; PLINT plyoff;
323   plstream::gpage( xp, yp, xleng, yleng, plxoff, plyoff); //for X-Window, wrapper give sizes from X11, not plplot which seems bugged.
324   // for PostScript, Page size is FIXED (720x540) and GDLPSStream::GetGeometry replies correctly
325 
326 //since the page sizes for PS and EPS images are processed by GDL after plplot finishes
327 //its work, gpage will not output correct sizes
328   DString name = (*static_cast<DStringGDL*>(
329     SysVar::D()->GetTag(SysVar::D()->Desc()->TagIndex("NAME"), 0)
330   ))[0];
331   if (name == "PS") {
332     xSize = (*static_cast<DLongGDL*>(SysVar::D()->GetTag(SysVar::D()->Desc()->TagIndex("X_SIZE"), 0)))[0];
333     ySize = (*static_cast<DLongGDL*>(SysVar::D()->GetTag(SysVar::D()->Desc()->TagIndex("Y_SIZE"), 0)))[0];
334   } else {
335   xSize = xleng;
336   ySize = yleng;
337   }
338   if (xSize<1.0||ySize<1) //plplot gives back crazy values! z-buffer for example!
339   {
340     PLFLT xmin,xmax,ymin,ymax;
341     plstream::gspa(xmin,xmax,ymin,ymax); //subpage in mm
342     xSize=min(1.0,xmax-xmin);
343     ySize=min(1.0,ymax-ymin);
344   }
345   if (GDL_DEBUG_PLSTREAM) fprintf(stderr,"    found (%ld %ld)\n", xSize, ySize);
346 
347 }
348 
349 // SA: embedded font attributes handling (IDL to plPlot syntax translation)
TranslateFormatCodes(const char * in,double * stringLength=NULL)350  std::string GDLGStream::TranslateFormatCodes(const char *in, double *stringLength=NULL)
351 {
352   bool debug = false;
353   static char errmsg[] = "No such font:   ";
354   static std::string begin_unicode="#[";
355   static std::string end_unicode="]";
356   static const size_t errmsglen = strlen(errmsg);
357   static double fact[]={1.,0.9,0.666,0.5,0.45,0.33,0.2};
358   double base=1.0;
359 
360   // TODO:
361   // - in IDL the D.FLAGS bit value ((!D.FLAGS AND 4096) EQ 0)
362   //   is designed to indicate if the device does not support extended commands
363   // - unicode substitution for non-unicode terminals results in plplot controll
364   //   sequences being printed
365   // do something about handling of !C and !S and !R
366   // - ... a look-up table instead of the long switch/case blocks ...
367 
368   size_t len = strlen(in);
369   if (stringLength) *stringLength=0;
370   // skip conversion if the string is empty
371   if (len == 0) return "";
372 
373 
374   int default_fnt = activeFontCodeNum;
375   std::string out = std::string(internalFontCodes[default_fnt]);
376 //no, take current value, initialized to 3. was:  activeFontCodeNum = default_fnt; // (current font number from the above table)
377   int curr_fnt = default_fnt; // (current font number from the above table)
378   int next_fnt = default_fnt; // (next letter font - same as curr_fnt save for the case of !G, !W and !M commands)
379   int curr_lev = 0; // (incremented with #u, decremented with #d)
380   int curr_pos = 0; // (current position in string)
381   int save_pos = 0; // (position in string used in !S/!R save/restore)
382 
383   for (size_t i = 0; i < len; i++) {
384     if (in[i] == '!' && in[i + 1] != '!')
385     {
386       size_t j = 1; // number of characters analysed (after !)
387       switch (in[i + 1])
388       {
389         case '1' : // 2-char codes begining with !1
390         {
391           switch (in[i + 2])
392           {
393             case '6' : // !16 : Cyrillic
394               base=17./15.;//approx size of a hershey char with this font.
395               j++;
396               out += internalFontCodes[activeFontCodeNum = curr_fnt = next_fnt = 10 - 48 + in[i + 2]];
397               break;
398             case '8' : // !18 : Triplex Italic
399               base=16.5/15.;//approx size of a hershey char with this font.
400               j++;
401               out += internalFontCodes[activeFontCodeNum = curr_fnt = next_fnt = 10 - 48 + in[i + 2]];
402               break;
403             case '0' : // !10 : Special characters
404             case '1' : // !11 : Gothic English
405             case '2' : // !12 : Simplex Script
406             case '3' : // !13 : Complex Script
407             case '4' : // !14 : Gothic Italian
408             case '5' : // !15 : Gothic German
409             case '7' : // !17 : Triplex Roman
410             case '9' : // !19 :
411               base=1.0;
412               j++;
413               out += internalFontCodes[activeFontCodeNum = curr_fnt = next_fnt = 10 - 48 + in[i + 2]];
414               break;
415             default : // illegal command / end of string
416               errmsg[errmsglen - 2] = in[i + 1];
417               errmsg[errmsglen - 1] = in[i + 2];
418               j++;
419               Warning(errmsg);
420           }
421           break;
422         }
423         case '2' : // 2-char codes begining with !2
424         {
425           switch (in[i + 2])
426           {
427             case '0' : // !20 : Miscellaneous
428               base=17.5/15.;//approx size of a hershey char with this font.
429               j++;
430               out += internalFontCodes[activeFontCodeNum = curr_fnt = next_fnt = 20 - 48 + in[i + 2]];
431               break;
432             default : // illegal command / end of string
433               errmsg[errmsglen - 2] = in[i + 1];
434               errmsg[errmsglen - 1] = in[i + 2];
435               j++;
436               Warning(errmsg);
437           }
438           break;
439         }
440 
441         case '7' : // complex greek
442         case '8' : // complex italic
443           base=16.5/15.; //approx size of a hershey char with these fonts.
444           out += internalFontCodes[activeFontCodeNum = next_fnt = curr_fnt = in[i + 1] - 48];
445           break;
446 
447         case '3' : // simplex roman
448         case '4' : // greek script
449         case '5' : // duplex roman
450         case '6' : // complex roman
451         case '9' : // Math/special characters
452           base=1.0;
453           out += internalFontCodes[activeFontCodeNum = next_fnt = curr_fnt = in[i + 1] - 48];
454           break;
455 
456         case 'M' : case 'm' : // one Math/special character
457           curr_fnt = 9;
458           break;
459         case 'G' : case 'g' : // one Gothic English character
460           curr_fnt = 11;
461           break;
462         case 'W' : case 'w' : // one Simplex Script character
463           curr_fnt = 12;
464           break;
465 
466         case 'C' : case 'c' : // carriage return (TODO FIXME: does not work in PostScript!)
467           out += "#[0xD]";
468           break;
469 
470         case 'X' : case 'x' : // reversion to entry font
471           out += internalFontCodes[curr_fnt = next_fnt = default_fnt];
472           break;
473 
474         case 'S' : case 's' : // save position
475           save_pos = curr_pos;
476           break;
477 
478         case 'L' : case 'l' : // 2nd level subscript
479           curr_lev--;
480           out += "#d";
481           // continues!
482         case 'B' : case 'b' : case 'D' : case 'd' : // subscript
483         case 'I' : case 'i' : // index
484           curr_lev--;
485           out += "#d";
486           break;
487         case 'R' : case 'r' : // restore position
488           for (; save_pos < curr_pos; curr_pos--) out += "#b";
489           // continues!
490         //case 'A' : case 'a' : // shift above the division line
491           //TODO: plplot seems not to support it ----> treated as upperscript for the moment
492        // case 'B' : case 'b' : // shift below the division line
493           //TODO: plplot seems not to support it ----> treated as subscript for the moment
494         case 'N' : case 'n' : // back to normal size
495           while (curr_lev != 0)
496           {
497             if (curr_lev > 0) out += "#d", curr_lev--;
498             else out += "#u", curr_lev++;
499           }
500           // assumed from examples in documentation
501           //if (in[i + 1] == 'N' || in[i + 1] == 'n') out += internalFontCodes[curr_fnt = next_fnt = default_fnt];
502           break;
503         case 'A' : case 'a' : case 'U' : case 'u' : // superscript
504         case 'E' : case 'e' : // exponent
505           curr_lev++;
506           out += "#u";
507           break;
508         case 'Z' : case 'z' : // unicode chars via "#[nnn]"
509           // first, the only two examples from IDL doc:
510           // - !Z(00B0) - degree symbol
511           // - !Z(U+0F1) - Spanish "n" with tilde (as in El ni\~no)
512           // searching for the left parenthesis
513           if (in[i + 2] != '(')
514           {
515             if (in[i + 2] == '!' && in[i + 3] == 'Z') break; // !Z!Z is valid
516             if (in[i + 2] == '\0') break; // end of string after !Z is valid
517             Warning("Error using Hershey characters: Parentheses required for !Z.");
518             goto retrn;
519           }
520           else
521           {
522             // searching for the right parenthesis
523             size_t right_par = i + 2;
524             while (in[++right_par] != ')') if (right_par == len)
525             {
526               Warning("Error using Hershey characters: Parentheses required for !Z.");
527               goto retrn;
528             }
529             size_t chars = 0;
530             while (j + i + 1 != right_par)
531             {
532               // tokenizing (plethora of other tokens is accepted by IDL!)
533               while (++j + i + 1 != right_par && in[j + i + 1] != ',') chars++;
534               if (in[j + i + 1 - chars] == 'U' && in[j + i + 2 - chars] == '+') chars-=2; // U+NNNN syntax
535               if (chars > 4)
536               {
537                 Warning("Error using Hershey characters: !Z hexadecimal value too large.");
538                 goto retrn;
539               }
540               else if (chars > 0)
541               {
542                 out += "#[0x";
543                 for (; chars > 0; chars--) out += in[j + i + 1 - chars];
544                 out += "]";
545                 curr_pos++;
546               }
547             }
548             j++; // right parenthesis
549           }
550           break;
551         case '\0' : // end of string
552         default : // unknown command
553           j--;
554           curr_pos++;
555           out += "!";
556           break;
557       }
558       i += j;
559     }
560     else
561     {
562       if (stringLength) *stringLength+=base*fact[curr_lev%7];
563       curr_pos++;
564       // handling IDL exclamation mark escape '!!'
565       if (in[i] == '!') {
566         i++;
567         if (stringLength) *stringLength+=base*fact[curr_lev%7];
568       }
569       // handling plplot number sign escape '##'
570       if
571       (
572         curr_fnt !=  9 &&
573         curr_fnt != 10 &&
574         curr_fnt != 16 &&
575         curr_fnt != 20 &&
576         in[i] == '#'
577       ) out += "##";
578       else switch (curr_fnt)
579       {
580         case 9 : // math symbols
581           switch (in[i])
582           {
583             case '!' : out += "#(2229)"; break; // vertical line
584             case '%' : out += "#(218)";  break; // degree circle
585             case 'X' : out += "#(227)";  break; // x (cross) sign
586             case 'D' :                          // as below
587             case 'd' : out += "#(2265)"; break; // partial deriv sign
588             case 'r' : out += "#(2267)"; break; // square root sign bigger
589             case 'R' : out += "#(2411)"; break; // square root sign biggest
590             case 'S' : out += "#(2267)"; break; // square root sign
591             case 'I' : out += "#(2412)"; break; // big integral sign
592             case 'i' : out += "#(2268)"; break; // small integral sign
593             case '/' : out += "#(2237)"; break; // range (dot dash dot) sign
594             case '=' : out += "#(2239)"; break; // non equal sign
595             case '6' : out += "#(2261)"; break; // arrow right
596             case '7' : out += "#(2262)"; break; // arrow up
597             case '4' : out += "#(2263)"; break; // arrow left
598             case '5' : out += "#(2264)"; break; // arrow down
599             case 'f' : out += "#(2283)"; break; // female sex sign
600             case 'm' : out += "#(2285)"; break; // male sex sign
601             case '<' : out += "#(2407)"; break; // big curly brace left
602             case '>' : out += "#(2408)"; break; // big curly brace right
603             case '#' : out += "#(737)";  break; // two vertical lines
604             case '$' : out += "#(766)";  break; // infty
605             case '&' : out += "#(2276)"; break; // paragraph
606             case 'P' : out += "#(2147)"; break; // phi
607             case 'p' :
608               out += "#fsp";
609               out += internalFontCodes[curr_fnt];    break; // p script
610             case 'q' :
611               out += "#fsq";
612               out += internalFontCodes[curr_fnt];    break; // q script
613             case ':' : out += "#(2240)"; break; // equal by definition sign
614             case '.' : out += "#(850)";  break; // filled dot
615             case 'B' : out += "#(841)";  break; // empty square
616             case 'F' :
617               out += "#fsF";
618               out += internalFontCodes[curr_fnt];    break; // F script
619             case 'J' : out += "#(2269)"; break; // closed path integral
620             case 'O' : out += "#(2277)"; break; // 'upper' cross sign
621             case 'o' : out += "#(2278)"; break; // double cross sign
622             case 'j' :
623               out += "#fsj";
624               out += internalFontCodes[curr_fnt];    break; // j italic
625             case 's' : out += "#(687)";  break; // some greek zig-zag
626             case 't' :
627               out += "#fs#(634)";        break; // theta-like greek zig-zag
628             case 'A' : out += "#(2246)"; break; // similar / tilde
629             case 'T' : out += "#(740)";  break; // three dots
630             case 'U' : out += "#(741)";  break; // spades (card sign)
631             case 'V' : out += "#(743)";  break; // diamonds (card sign)
632             case 'W' : out += "#(745)";  break; // clover sign
633             case 'u' : out += "#(742)";  break; // hearts (card sign)
634             case 'v' : out += "#(744)";  break; // clubs (card sign)
635             case 'w' : out += "#(746)";  break; // ?
636             case '_' :                          // as below
637             case 127u : out += "#(830)"; break; // '---' sign
638             case 'H' : out += "#(908)";  break; // ?
639             case '@' : out += "#(2077)"; break; // ? kappa like
640             case 'n' : out += "#(2281)"; break; // circle with dot inside
641             case 'e' : out += "#(2260)"; break; // mirrored E
642             case 'E' : out += "#(2279)"; break; // element of
643             case 'G' : out += "#(2266)"; break; // nabla
644             case 'l' : out += "#(2243)"; break; // less or equal
645             case 'b' : out += "#(2244)"; break; // grater or equal
646             case '?' : out += "#(2245)"; break; // proportional
647             case '^' :                          // as below
648             case '~' : out += "#(2247)"; break; // ^
649             case '+' : out += "#(2233)"; break; // plus minus
650             case '-' : out += "#(2234)"; break; // minus plus
651             case '(' : out += "#(2403)"; break; // big bracket left
652             case ')' : out += "#(2404)"; break; // big bracket right
653             case '[' :                          // as below
654             case '{' : out += "#(2405)"; break; // big rect. brace left
655             case ']' :                          // as below
656             case '}' : out += "#(2406)"; break; // big rect. brace right
657             case 'h' : out += "#(909)";  break; // ? police badge-like
658             case 'x' : out += "#(738)";  break; // perpendicular sign
659             case 'a' : out += "#(739)";  break; // angle
660             case 'c' : out += "#(823)";  break; // ?
661             case '0' : out += "#(2256)"; break; // set theory C-like
662             case '1' : out += "#(2257)"; break; // set theory U-like
663             case '2' : out += "#(2258)"; break; // set theory )-like
664             case '3' : out += "#(2259)"; break; // set theory ^-like
665             case 'N' : out += "#(2311)"; break; // double wave
666             case '`' : out += "'";       break; // ` -> '
667             // empty chars:
668             case '8' : case '9' : case ';' : case 'K' : case 'L' : case 'M' : case 'Q' :
669             case 'Y' : case 'Z' : case '\\' : case 'k' : case 'y' : case 'z' : case '|' :
670             case 'g' :
671             // unsupported chars:
672             case 'C' : // tick sign
673               out += " "; break;
674               break;
675             default :
676               if ((unsigned char)in[i] > 127) { out+=begin_unicode; out+=to_string((unsigned char)in[i]); out+=end_unicode; } else out.append(in, i, 1); //use unicode
677               break;
678           }
679           break;
680         case 20 : // misc symbols
681           switch (in[i])
682           {
683             case 'b' : out += "#(851)";  break;  // filled square
684             case ':' :                           // small filled triangle up
685             case 'C' : out += "#(852)";  break;  // filled triangle up
686             case 'D' : out += "#(854)";  break;  // filled triangle down
687             case '/' : out += "#(2323)"; break;  // musical sharp sign
688             case 'M' : out += "#(874)";  break;  // ?
689             case 'K' : out += "#(870)";  break;  // palm sign
690             case '^' :                           // as below
691             case '~' : out += "#(834)";  break;  // upside down triangle
692             case 'N' : out += "#(900)";  break;  // circle smallest
693             case 'n' : out += "#(901)";  break;  // circle smaller
694             case 'O' : out += "#(902)";  break;  // circle small
695             case 'o' : out += "#(903)";  break;  // circle
696             case 'P' : out += "#(904)";  break;  // circle
697             case 'p' : out += "#(905)";  break;  // circle big
698             case 'Q' : out += "#(906)";  break;  // circle bigger
699             case 'q' : out += "#(907)";  break;  // circle biggest
700             case '?' : out += "#(767)";  break;  // flash
701             case '<' : out += "#(768)";  break;  // paragraph-like
702             case 'A' : out += "#(754)";  break;  // upper semicircle filled
703             case 'G' : out += "#(862)";  break;  // two hammers
704             case 'E' : out += "#(856)";  break;  // star
705             case 'e' : out += "#(857)";  break;  // flag
706             case 'f' : out += "#(861)";  break;  // ?
707             case 'g' : out += "#(863)";  break;  // tower / look-out
708             case 'h' : out += "#(865)";  break;  // grave
709             case 'L' : out += "#(872)";  break;  // deciduous tree
710             case 'k' : out += "#(871)";  break;  // coniferous tree
711             case '"' : out += "#(2409)"; break;  // big inverted-s-like shape
712             case '$' : out += "#(2376)"; break;  // ?
713             case '%' : out += "#(2382)"; break;  // ?
714             case '`' :
715             case '\'' : out += "#(766)"; break;  // infty
716             case '(' : out += "#(2374)"; break;  // natural (music)
717             case ')' : out += "#(2375)"; break;  // flat (music)
718             case '*' : out += "#(2372)"; break;  // ? minim (music)
719             case '-' :                           // as below
720             case '+' : out += "#(2371)"; break;  // ? 2xminim length note (music)
721             case ',' : out += "#(2329)"; break;  // ? rest (music)
722             case '.' : out += "#(2380)"; break;  // treble clef
723             case '0' : out += "#(2306)"; break;  // ? Gothic-like m
724             case '1' : out += "#(2307)"; break;  // ? underlined omega
725             case '2' : out += "#(2308)"; break;  // ? Gothic-like m
726             case '3' : out += "#(2309)"; break;  // NE double arrow
727             case '4' : out += "#(2310)"; break;  // ?
728             case '5' : out += "#(2311)"; break;  // ?
729             case '6' : out += "#(2312)"; break;  // ?
730             case '7' : out += "#(2317)"; break;  // ?
731             case '8' : out += "#(2318)"; break;  // ?
732             case '9' : out += "#(2319)"; break;  // ?
733             case '=' : out += "#(2377)"; break;  // ?
734             case '>' : out += "#(831)";  break;  // ?
735             case 'V' : out += "#(2291)"; break;  // crescent
736             case 'W' : out += "#(2293)"; break;  // 8-arm star
737             case 'X' : out += "#(2295)"; break;  // ?
738             case 'Y' : out += "#(2302)"; break;  // ?
739             case 'Z' : out += "#(2304)"; break;  // 69 ;)
740             case 'r' : out += "#(2282)"; break;  // ? female sex sign like
741             case 'u' : out += "#(2290)"; break;  // ? P-like
742             case 'v' : out += "#(2292)"; break;  // ?
743             case 'w' : out += "#(2294)"; break;  // ? omega-like
744             case 'x' : out += "#(2301)"; break;  // ? fountain-like
745             case 'y' : out += "#(2303)"; break;  // ?
746             case '[' :                           // as below
747             case '{' : out += "#(2332)"; break;  // ? ladder-like
748             case ']' :                           // as below
749             case '}' : out += "#(2381)"; break;  // bass clef
750             case '_' :                           // as below
751             case 127 : out += "#(2410)"; break;  // big s-like shape
752             case '!' : out += "#(764)";  break;  // S-like
753             case '&' : out += "#(765)";  break;  // tilde-like half-infty sign
754             case 'B' : out += "#(850)";  break;  // filled dot
755             case 'F' : out += "#(860)";  break;  // anchor
756             case 'I' : out += "#(866)";  break;  // cross
757             case 'J' : out += "#(868)";  break;  // Jewish star
758             case 'R' : out += "#(735)";  break;  // square-like
759             case 'a' : out += "#(755)";  break;  // filled triangle
760             case 'i' : out += "#(867)";  break;  // small screscent
761             case 'j' : out += "#(869)";  break;  // bell
762             case ' ' : out += "#(2328)"; break; // ?
763             case '#' : out += "#(2331)"; break; // ?
764             case 'S' : out += "#(2284)"; break; // empty circel with plus sign inside
765             case 'T' : out += "#(2287)"; break; // ?
766             case 'U' : out += "#(2289)"; break; // ? greep psi-like
767             case '\\' :  // as below
768             case '|' : out += "#(833)";  break; // ? electrical ground sign-like
769             case 'c' : out += "#(853)";  break; // filled triangle left
770             case 'd' : out += "#(855)";  break; // filled triangle right
771             case '@' : out += "#(832)";  break; // ^
772             case 'H' : out += "#(864)";  break; // flower in a flowerpot
773             case 's' : out += "#(2286)"; break; // 4-like
774             case 't' : out += "#(2288)"; break; // ?
775             case ';' : out += "#(840)";  break; // empty circle
776             case 'l' : out += "#(873)";  break; // ?
777             case 'z' : out += "#(2305)"; break; // ?
778             case 'm' : out += "#(899)";  break; // smallest circle - dot
779             default :
780               if ((unsigned char)in[i] > 127) { out+=begin_unicode; out+=to_string((unsigned char)in[i]); out+=end_unicode; } else out.append(in, i, 1); //use unicode
781               break;
782           }
783           break;
784         case 16 : // Cyrillic
785           switch (in[i])
786           { //         uppercase                           lowercase
787             case 'A' : out += "#(2801)"; break; case 'a' : out += "#(2901)"; break; // [a]
788             case 'B' : out += "#(2802)"; break; case 'b' : out += "#(2902)"; break; // [b]
789             case 'C' : out += "#(2803)"; break; case 'c' : out += "#(2903)"; break; // [v]
790             case 'D' : out += "#(2804)"; break; case 'd' : out += "#(2904)"; break; // [g]
791             case 'E' : out += "#(2805)"; break; case 'e' : out += "#(2905)"; break; // [d]
792             case 'F' : out += "#(2806)"; break; case 'f' : out += "#(2906)"; break; // [ye]
793             case 'G' : out += "#(2807)"; break; case 'g' : out += "#(2907)"; break; // [zsh]
794             case 'H' : out += "#(2808)"; break; case 'h' : out += "#(2908)"; break; // [z]
795             case 'I' : out += "#(2809)"; break; case 'i' : out += "#(2909)"; break; // [i/e]
796             case 'J' : out += "#(2810)"; break; case 'j' : out += "#(2910)"; break; // [ii]
797             case 'K' : out += "#(2811)"; break; case 'k' : out += "#(2911)"; break; // [k]
798             case 'L' : out += "#(2812)"; break; case 'l' : out += "#(2912)"; break; // [l]
799             case 'M' : out += "#(2813)"; break; case 'm' : out += "#(2913)"; break; // [m]
800             case 'N' : out += "#(2814)"; break; case 'n' : out += "#(2914)"; break; // [n]
801             case 'O' : out += "#(2815)"; break; case 'o' : out += "#(2915)"; break; // [o]
802             case 'P' : out += "#(2816)"; break; case 'p' : out += "#(2916)"; break; // [p]
803             case 'Q' : out += "#(2817)"; break; case 'q' : out += "#(2917)"; break; // [r]
804             case 'R' : out += "#(2818)"; break; case 'r' : out += "#(2918)"; break; // [s]
805             case 'S' : out += "#(2819)"; break; case 's' : out += "#(2919)"; break; // [t]
806             case 'T' : out += "#(2820)"; break; case 't' : out += "#(2920)"; break; // [u/woo]
807             case 'U' : out += "#(2821)"; break; case 'u' : out += "#(2921)"; break; // [f]
808             case 'V' : out += "#(2822)"; break; case 'v' : out += "#(2922)"; break; // [h]
809             case 'W' : out += "#(2823)"; break; case 'w' : out += "#(2923)"; break; // [c]
810             case 'X' : out += "#(2824)"; break; case 'x' : out += "#(2924)"; break; // [ch]
811             case 'Y' : out += "#(2825)"; break; case 'y' : out += "#(2925)"; break; // [sh]
812             case 'Z' : out += "#(2826)"; break; case 'z' : out += "#(2926)"; break; // [shch]
813             case '#' : out += "#(2827)"; break; case '<' : out += "#(2927)"; break; // hard sign
814             case '{' : case '[' : out += "#(2828)"; break; case '>' : out += "#(2928)"; break; // [y]
815             case '}' : case ']' : out += "#(2829)"; break; case '@' : out += "#(2929)"; break; // soft sign
816             case '%' : out += "#(2830)"; break; case '\\' : case '|' : out += "#(2930)"; break; // [eh]
817             case '"' : out += "#(2831)"; break; case '^' : case '~' : out += "#(2931)"; break; // [yu]
818             case 127u : case '_' : out += "#(2832)"; break; case ';' : out += "#(2932)"; break; // [ya]
819             default :
820               if ((unsigned char)in[i] > 127) { out+=begin_unicode; out+=to_string((unsigned char)in[i]); out+=end_unicode; } else out.append(in, i, 1); //use unicode
821               break;
822           }
823           break;
824         case 4 : // greek letters
825         case 7 :
826           switch (in[i])
827           {
828             // non-equivalent letters
829             case 'C' : out += "#gG"; break; case 'c' : out += "#gg"; break;
830             case 'F' : out += "#gZ"; break; case 'f' : out += "#gz"; break;
831             case 'G' : out += "#gY"; break; case 'g' : out += "#gy"; break;
832             case 'J' : out += "#gK"; break; case 'j' : out += "#gk"; break;
833             case 'K' : out += "#gL"; break; case 'k' : out += "#gl"; break;
834             case 'L' : out += "#gM"; break; case 'l' : out += "#gm"; break;
835             case 'M' : out += "#gN"; break; case 'm' : out += "#gn"; break;
836             case 'N' : out += "#gC"; break; case 'n' : out += "#gc"; break;
837             case 'Q' : out += "#gR"; break; case 'q' : out += "#gr"; break;
838             case 'R' : out += "#gS"; break; case 'r' : out += "#gs"; break;
839             case 'S' : out += "#gT"; break; case 's' : out += "#gt"; break;
840             case 'T' : out += "#gU"; break; case 't' : out += "#gu"; break;
841             case 'U' : out += "#gF"; break; case 'u' : out += "#gf"; break;
842             case 'V' : out += "#gX"; break; case 'v' : out += "#gx"; break;
843             case 'W' : out += "#gQ"; break; case 'w' : out += "#gq"; break;
844             case 'X' : out += "#gW"; break; case 'x' : out += "#gw"; break;
845             case 'Y' :                      case 'y' : out += "#(766)"; break;
846             case '{' : out += '['; break; case '}' : out += ']'; break;
847             // equivalent letters
848             case 'A' : case 'a' :
849             case 'B' : case 'b' :
850             case 'D' : case 'd' :
851             case 'E' : case 'e' :
852             case 'H' : case 'h' :
853             case 'I' : case 'i' :
854             case 'O' : case 'o' :
855             case 'P' : case 'p' :
856               out += "#g"; out.append(in, i, 1); break;
857             default :
858               if ((unsigned char)in[i] > 127) { out+=begin_unicode; out+=to_string((unsigned char)in[i]); out+=end_unicode; } else out.append(in, i, 1); //use unicode
859               break;
860           }
861           break;
862         case 3 : // simplex roman
863           switch ((unsigned char)in[i])
864           {
865             // unsupported chars
866             case '^' : case '~' : case '\\' :
867               out += " ";
868               break;
869             // 8th bit chars
870             case 144u : out += "1"; break;
871             case 154u : out += "#(218)"; break; // degree circle
872             case 163u : out += "#(272)"; break; // pound sign
873             case 167u : out += "#(2276)"; break; // paragraph
874             case 169u : out += "#(274)"; break; // copyright sign
875             case 174u : out += "#(273)"; break; // registered sign
876             case 181u : out += "#gm"; break; // greek mu
877             case 188u : out += "#(270)"; break; // 1/4
878             case 189u : out += "#(261)"; break; // 1/2
879             case 190u : out += "#(271)"; break; // 3/4
880             case 215u : out += "#(846)"; break; // cross sign
881             case 223u : out += "#fs#gb"; out += internalFontCodes[curr_fnt]; break; // beta script
882             default :
883               if ((unsigned char)in[i] > 127) { out+=begin_unicode; out+=to_string((unsigned char)in[i]); out+=end_unicode; } else out.append(in, i, 1); //use unicode
884               break;
885           }
886           break;
887         default : // simply pass the char
888           if ((unsigned char)in[i] > 127) { out+=begin_unicode; out+=to_string((unsigned char)in[i]); out+=end_unicode; } else out.append(in, i, 1); //use unicode
889           break;
890       }
891       curr_fnt = next_fnt;
892     }
893   }
894   activeFontCodeNum = curr_fnt;
895   //if gdlGetStringLength function is available, use it to give back a better value ("X" and "I" do not have the same width in hershey format!)
896 #if PLPLOT_PRIVATE_NOT_HIDDEN
897   if (stringLength) *stringLength=gdlGetStringLength(out)/this->mmCharLength();
898 #endif
899   return out;
900 retrn:
901   activeFontCodeNum = curr_fnt;
902   if (stringLength) *stringLength=0;
903   cout << "ERROR: GDLGStream::TranslateFormatCodes(\"" << in << "\") = \"" << out << "\"" << endl;
904   return "";
905 }
906 
setSymbolSize(PLFLT scale)907 void GDLGStream::setSymbolSize( PLFLT scale )
908 {
909   if (GDL_DEBUG_PLSTREAM) fprintf(stderr,"setSymbolScale(%f)\n",scale);
910   plstream::ssym(0.0, scale);
911   theCurrentSymSize=scale;
912 }
913 
setLineSpacing(PLFLT newSpacing)914 void GDLGStream::setLineSpacing(PLFLT newSpacing)
915 {
916   theLineSpacing_in_mm=newSpacing;
917 }
getSymbolSize()918 PLFLT GDLGStream::getSymbolSize(){return theCurrentSymSize;}
mtex(const char * side,PLFLT disp,PLFLT pos,PLFLT just,const char * text)919 void GDLGStream::mtex( const char *side, PLFLT disp, PLFLT pos, PLFLT just,
920                        const char *text)
921 {
922   plstream::mtex(side,disp,pos,just,TranslateFormatCodes(text).c_str());
923 }
924 
mtex3(const char * side,PLFLT disp,PLFLT pos,PLFLT just,const char * text)925 void GDLGStream::mtex3( const char *side, PLFLT disp, PLFLT pos, PLFLT just,
926                        const char *text)
927 {
928   plstream::mtex3(side,disp,pos,just,TranslateFormatCodes(text).c_str());
929 }
ptex(PLFLT x,PLFLT y,PLFLT dx,PLFLT dy,PLFLT just,const char * text,double * stringCharLength)930 void GDLGStream::ptex( PLFLT x, PLFLT y, PLFLT dx, PLFLT dy, PLFLT just,
931                        const char *text , double *stringCharLength)
932 {
933   plstream::ptex(x,y,dx,dy,just,TranslateFormatCodes(text,stringCharLength).c_str());
934 }
935 
schr(PLFLT charwidthmm,PLFLT scale,PLFLT lineSpacingmm)936 void GDLGStream::schr( PLFLT charwidthmm, PLFLT scale , PLFLT lineSpacingmm)
937 {
938   if (GDL_DEBUG_PLSTREAM) fprintf(stderr,"schr(%f,%f,%f)\n",charwidthmm,scale,lineSpacingmm);
939   plstream::schr(charwidthmm, scale);
940   this->setLineSpacing(lineSpacingmm);
941   gdlDefaultCharInitialized=0;
942   CurrentCharSize(scale);
943 }
944 
sizeChar(PLFLT scale)945 void GDLGStream::sizeChar( PLFLT scale )
946 {
947     if (GDL_DEBUG_PLSTREAM) fprintf(stderr,"SizeChar(%f)\n",scale);
948   plstream::schr(theDefaultChar.mmsx, scale);
949 //  plstream::schr(0, scale);
950   CurrentCharSize(scale);
951 }
952 
vpor(PLFLT xmin,PLFLT xmax,PLFLT ymin,PLFLT ymax)953 void GDLGStream::vpor(PLFLT xmin, PLFLT xmax, PLFLT ymin, PLFLT ymax )
954 {
955   if (GDL_DEBUG_PLSTREAM) fprintf(stderr,"vpor(): requesting x[%f:%f],y[%f:%f] (normalized, subpage)\n",xmin,xmax,ymin,ymax);
956   //note that plplot apparently does not write the y=0 line of pixels (in device coords). IDL page is on the contrary limited to
957   // [0..1[ in both axes (normalized coordinates)
958   plstream::vpor(xmin, xmax, ymin, ymax);
959   theBox.nx1=xmin;
960   theBox.nx2=xmax;
961   theBox.ny1=ymin;
962   theBox.ny2=ymax;
963   PLFLT x1,x2,y1,y2;
964   plstream::gvpd(x1,x2,y1,y2); //retrieve NORMALIZED DEVICE coordinates of viewport
965   theBox.ndx1=x1;
966   theBox.ndx2=x2;
967   theBox.ndy1=y1;
968   theBox.ndy2=y2;
969   theBox.ondx=x1;
970   theBox.ondy=y1;
971   theBox.sndx=x2-x1;
972   theBox.sndy=y2-y1;
973 
974   theBox.initialized=true;
975   if (GDL_DEBUG_PLSTREAM) fprintf(stderr,"vpor(): got x[%f:%f],x[%f:%f] (normalized, device)\n",theBox.ndx1,theBox.ndx2,theBox.ndy1,theBox.ndy2);
976   syncPageInfo();
977 }
978 
wind(PLFLT xmin,PLFLT xmax,PLFLT ymin,PLFLT ymax)979 void GDLGStream::wind( PLFLT xmin, PLFLT xmax, PLFLT ymin, PLFLT ymax )
980 {
981   if (GDL_DEBUG_PLSTREAM) fprintf(stderr,"wind(): setting x[%f:%f],y[%f:%f] (world) \n",xmin,xmax,ymin,ymax);
982   //silly test to protect against plplot warnings ... side effects unkonwn.
983   if (xmin==xmax) {xmin=0; xmax=1;}
984   if (ymin==ymax) {ymin=0; ymax=1;}
985   plstream::wind(xmin, xmax, ymin, ymax);
986   theBox.wx1=xmin;
987   theBox.wx2=xmax;
988   theBox.wy1=ymin;
989   theBox.wy2=ymax;
990   updateBoxDeviceCoords();
991   UpdateCurrentCharWorldSize();
992 }
993 
ssub(PLINT nx,PLINT ny)994 void GDLGStream::ssub(PLINT nx, PLINT ny)
995 {
996   plstream::ssub( nx, ny ); // does not appear to change charsize.
997   // set subpage numbers in X and Y
998   thePage.nbPages=nx*ny;
999   thePage.nx=nx;
1000   thePage.ny=ny;
1001   if (GDL_DEBUG_PLSTREAM) fprintf(stderr,"ssub() %dx%d pages\n",nx,ny);
1002   thePage.curPage=1;
1003   syncPageInfo();
1004 }
1005 
adv(PLINT page)1006 void GDLGStream::adv(PLINT page)
1007 {
1008   plstream::adv(page);
1009   if (page==0) {thePage.curPage++;} else {thePage.curPage=page;}
1010   if (thePage.curPage > thePage.nbPages) thePage.curPage=1;
1011   if (GDL_DEBUG_PLSTREAM) fprintf(stderr,"adv() now at page %d\n",thePage.curPage);
1012 }
1013 //get region (3BPP data)
GetRegion(DLong & x_gdl,DLong & y_gdl,DLong & nx_gdl,DLong & ny_gdl)1014 bool GDLGStream::GetRegion(DLong& x_gdl, DLong& y_gdl, DLong& nx_gdl, DLong& ny_gdl){
1015     DByteGDL *bitmap = static_cast<DByteGDL*>(this->GetBitmapData());
1016     if (bitmap==NULL)  return false; //need to GDLDelete bitmap on exit after this line.
1017 
1018     bool error=false;
1019     DLong nx=bitmap->Dim(0);
1020     DLong ny=bitmap->Dim(1);
1021 
1022     DLong xref,xval,xinc,yref,yval,yinc,xmax11,ymin11;
1023     long x_11=0;
1024     long y_11=0;
1025     xref=0;xval=0;xinc=1;
1026     yref=0;yval=0;yinc=1;
1027 
1028     x_11=xval+(x_gdl-xref)*xinc;
1029     y_11=yval+(y_gdl-yref)*yinc;
1030     xmax11=xval+(x_gdl+nx_gdl-1-xref)*xinc;
1031     ymin11=yval+(y_gdl+ny_gdl-1-yref)*yinc;
1032     if (y_11 < 0 || y_11 > ny-1) error=true;
1033     if (x_11 < 0 || x_11 > nx-1) error=true;
1034     if (xmax11 < 0 || xmax11 > nx-1) error=true;
1035     if (ymin11 < 0 || ymin11 > ny-1) error=true;
1036     if (error) {  GDLDelete(bitmap); return false; }
1037     GraphicsDevice* actDevice = GraphicsDevice::GetDevice();
1038     unsigned char* data=actDevice->SetCopyBuffer(nx_gdl*ny_gdl*3);
1039     for ( SizeT i =0; i < nx_gdl ; ++i ) {
1040       for ( SizeT j = 0; j < ny_gdl ; ++j ) {
1041        for ( SizeT k = 0 ; k < 3 ; ++k) data[3 * (j * nx_gdl + i) + k] = (*bitmap)[3 * ((j+y_11) * nx + (i+x_11)) + k];
1042       }
1043     }
1044     GDLDelete(bitmap);
1045     return true;
1046 }
1047 
SetRegion(DLong & xs,DLong & ys,DLong & nx,DLong & ny)1048 bool GDLGStream::SetRegion(DLong& xs, DLong& ys, DLong& nx, DLong& ny){
1049   DLong pos[4]={xs,nx,ys,ny};
1050   GraphicsDevice* actDevice = GraphicsDevice::GetDevice();
1051   return this->PaintImage(actDevice->GetCopyBuffer(), nx, ny, pos, 1, 0);
1052 }
1053