Fossil SCM

New pikchr.c with improved estimates for bounding boxes on text.

drh 2020-09-18 22:55 trunk
Commit bac677f7a968d33b486f1caf794a3bebd2bc63605fa4c91b813cc87753bb73b0
1 file changed +204 -57
+204 -57
--- src/pikchr.c
+++ src/pikchr.c
@@ -4652,12 +4652,13 @@
46524652
jw = 0.0;
46534653
}
46544654
for(i=0; i<n; i++){
46554655
PToken *t = &aTxt[i];
46564656
PNum xtraFontScale = 1.0;
4657
- orig_y = y = pElem->ptAt.y;
4658
- PNum nx = x;
4657
+ orig_y = pElem->ptAt.y;
4658
+ PNum nx = 0;
4659
+ y = 0;
46594660
if( t->eCode & TP_ABOVE2 ) y += dy2 + 3*dy;
46604661
if( t->eCode & TP_ABOVE ) y += dy2 + dy;
46614662
if( t->eCode & TP_BELOW ) y -= dy2 + dy;
46624663
if( t->eCode & TP_BELOW2 ) y -= dy2 + 3*dy;
46634664
if( t->eCode & TP_BIG ) xtraFontScale *= 1.25;
@@ -4667,24 +4668,53 @@
46674668
if( t->eCode & TP_RJUST ) nx += jw;
46684669
46694670
if( pBox!=0 ){
46704671
/* If pBox is not NULL, do not draw any <text>. Instead, just expand
46714672
** pBox to include the text */
4672
- PNum cw = pik_text_length(t)*p->charWidth*xtraFontScale;
4673
+ PNum cw = pik_text_length(t)*p->charWidth*xtraFontScale*0.01;
46734674
PNum ch = p->charHeight*0.5*xtraFontScale;
4675
+ PNum x0, y0, x1, y1; /* Boundary of text relative to pElem->ptAt */
4676
+ if( t->eCode & TP_BOLD ) cw *= 1.1;
46744677
if( t->eCode & TP_RJUST ){
4675
- pik_bbox_add_xy(pBox, nx, y-ch);
4676
- pik_bbox_add_xy(pBox, nx-cw, y+ch);
4678
+ x0 = nx;
4679
+ y0 = y-ch;
4680
+ x1 = nx-cw;
4681
+ y1 = y+ch;
46774682
}else if( t->eCode & TP_LJUST ){
4678
- pik_bbox_add_xy(pBox, nx, y-ch);
4679
- pik_bbox_add_xy(pBox, nx+cw, y+ch);
4683
+ x0 = nx;
4684
+ y0 = y-ch;
4685
+ x1 = nx+cw;
4686
+ y1 = y+ch;
46804687
}else{
4681
- pik_bbox_add_xy(pBox, nx+cw/2, y+ch);
4682
- pik_bbox_add_xy(pBox, nx-cw/2, y-ch);
4688
+ x0 = nx+cw/2;
4689
+ y0 = y+ch;
4690
+ x1 = nx-cw/2;
4691
+ y1 = y-ch;
4692
+ }
4693
+ if( (t->eCode & TP_ALIGN)!=0 && pElem->nPath>=2 ){
4694
+ int n = pElem->nPath;
4695
+ PNum dx = pElem->aPath[n-1].x - pElem->aPath[0].x;
4696
+ PNum dy = pElem->aPath[n-1].y - pElem->aPath[0].y;
4697
+ if( dx!=0 || dy!=0 ){
4698
+ PNum dist = hypot(dx,dy);
4699
+ PNum t;
4700
+ dx /= dist;
4701
+ dy /= dist;
4702
+ t = dx*x0 - dy*y0;
4703
+ y0 = dy*x0 - dx*y0;
4704
+ x0 = t;
4705
+ t = dx*x1 - dy*y1;
4706
+ y1 = dy*x1 - dx*y1;
4707
+ x1 = t;
4708
+ }
46834709
}
4710
+ pik_bbox_add_xy(pBox, x+x0, orig_y+y0);
4711
+ pik_bbox_add_xy(pBox, x+x1, orig_y+y1);
46844712
continue;
46854713
}
4714
+ nx += x;
4715
+ y += orig_y;
46864716
46874717
pik_append_x(p, "<text x=\"", nx, "\"");
46884718
pik_append_y(p, " y=\"", y, "\"");
46894719
if( t->eCode & TP_RJUST ){
46904720
pik_append(p, " text-anchor=\"end\"", -1);
@@ -4709,14 +4739,16 @@
47094739
}
47104740
if( (t->eCode & TP_ALIGN)!=0 && pElem->nPath>=2 ){
47114741
int n = pElem->nPath;
47124742
PNum dx = pElem->aPath[n-1].x - pElem->aPath[0].x;
47134743
PNum dy = pElem->aPath[n-1].y - pElem->aPath[0].y;
4714
- PNum ang = atan2(dy,dx)*-180/M_PI;
4715
- pik_append_num(p, " transform=\"rotate(", ang);
4716
- pik_append_xy(p, " ", x, orig_y);
4717
- pik_append(p,")\"",2);
4744
+ if( dx!=0 || dy!=0 ){
4745
+ PNum ang = atan2(dy,dx)*-180/M_PI;
4746
+ pik_append_num(p, " transform=\"rotate(", ang);
4747
+ pik_append_xy(p, " ", x, orig_y);
4748
+ pik_append(p,")\"",2);
4749
+ }
47184750
}
47194751
pik_append(p," dominant-baseline=\"central\">",-1);
47204752
z = t->z+1;
47214753
nz = t->n-2;
47224754
while( nz>0 ){
@@ -5551,30 +5583,163 @@
55515583
else iRes = (iRes &~TP_SZMASK)|TP_SMALL; break;
55525584
}
55535585
return iRes;
55545586
}
55555587
5556
-/* Return an estimate of the actual number of displayed characters
5557
-** in a character string.
5588
+/*
5589
+** Table of scale-factor estimates for variable-width characters.
5590
+** Actual character widths vary by font. These numbers are only
5591
+** guesses. And this table only provides data for ASCII.
5592
+**
5593
+** 100 means normal width.
5594
+*/
5595
+static const unsigned char awChar[] = {
5596
+ /* Skip initial 32 control characters */
5597
+ /* ' ' */ 45,
5598
+ /* '!' */ 55,
5599
+ /* '"' */ 62,
5600
+ /* '#' */ 115,
5601
+ /* '$' */ 90,
5602
+ /* '%' */ 132,
5603
+ /* '&' */ 125,
5604
+ /* '\''*/ 40,
5605
+
5606
+ /* '(' */ 55,
5607
+ /* ')' */ 55,
5608
+ /* '*' */ 71,
5609
+ /* '+' */ 115,
5610
+ /* ',' */ 45,
5611
+ /* '-' */ 48,
5612
+ /* '.' */ 45,
5613
+ /* '/' */ 50,
5614
+
5615
+ /* '0' */ 91,
5616
+ /* '1' */ 91,
5617
+ /* '2' */ 91,
5618
+ /* '3' */ 91,
5619
+ /* '4' */ 91,
5620
+ /* '5' */ 91,
5621
+ /* '6' */ 91,
5622
+ /* '7' */ 91,
5623
+
5624
+ /* '8' */ 91,
5625
+ /* '9' */ 91,
5626
+ /* ':' */ 50,
5627
+ /* ';' */ 50,
5628
+ /* '<' */ 120,
5629
+ /* '=' */ 120,
5630
+ /* '>' */ 120,
5631
+ /* '?' */ 78,
5632
+
5633
+ /* '@' */ 142,
5634
+ /* 'A' */ 102,
5635
+ /* 'B' */ 105,
5636
+ /* 'C' */ 110,
5637
+ /* 'D' */ 115,
5638
+ /* 'E' */ 105,
5639
+ /* 'F' */ 98,
5640
+ /* 'G' */ 105,
5641
+
5642
+ /* 'H' */ 125,
5643
+ /* 'I' */ 58,
5644
+ /* 'J' */ 58,
5645
+ /* 'K' */ 107,
5646
+ /* 'L' */ 95,
5647
+ /* 'M' */ 145,
5648
+ /* 'N' */ 125,
5649
+ /* 'O' */ 115,
5650
+
5651
+ /* 'P' */ 95,
5652
+ /* 'Q' */ 115,
5653
+ /* 'R' */ 107,
5654
+ /* 'S' */ 95,
5655
+ /* 'T' */ 97,
5656
+ /* 'U' */ 118,
5657
+ /* 'V' */ 102,
5658
+ /* 'W' */ 150,
5659
+
5660
+ /* 'X' */ 100,
5661
+ /* 'Y' */ 93,
5662
+ /* 'Z' */ 100,
5663
+ /* '[' */ 58,
5664
+ /* '\\'*/ 50,
5665
+ /* ']' */ 58,
5666
+ /* '^' */ 119,
5667
+ /* '_' */ 72,
5668
+
5669
+ /* '`' */ 72,
5670
+ /* 'a' */ 86,
5671
+ /* 'b' */ 92,
5672
+ /* 'c' */ 80,
5673
+ /* 'd' */ 92,
5674
+ /* 'e' */ 85,
5675
+ /* 'f' */ 52,
5676
+ /* 'g' */ 92,
5677
+
5678
+ /* 'h' */ 92,
5679
+ /* 'i' */ 47,
5680
+ /* 'j' */ 47,
5681
+ /* 'k' */ 88,
5682
+ /* 'l' */ 48,
5683
+ /* 'm' */ 135,
5684
+ /* 'n' */ 92,
5685
+ /* 'o' */ 86,
5686
+
5687
+ /* 'p' */ 92,
5688
+ /* 'q' */ 92,
5689
+ /* 'r' */ 69,
5690
+ /* 's' */ 75,
5691
+ /* 't' */ 58,
5692
+ /* 'u' */ 92,
5693
+ /* 'v' */ 80,
5694
+ /* 'w' */ 121,
5695
+
5696
+ /* 'x' */ 81,
5697
+ /* 'y' */ 80,
5698
+ /* 'z' */ 76,
5699
+ /* '{' */ 91,
5700
+ /* '|'*/ 49,
5701
+ /* '}' */ 91,
5702
+ /* '~' */ 118,
5703
+};
5704
+
5705
+/* Return an estimate of the width of the displayed characters
5706
+** in a character string. The returned value is 100 times the
5707
+** average character width.
55585708
**
55595709
** Omit "\" used to escape characters. And count entities like
55605710
** "&lt;" as a single character. Multi-byte UTF8 characters count
55615711
** as a single character.
5712
+**
5713
+** Attempt to scale the answer by the actual characters seen. Wide
5714
+** characters count more than narrow characters. But the widths are
5715
+** only guesses.
55625716
*/
55635717
static int pik_text_length(const PToken *pToken){
55645718
int n = pToken->n;
55655719
const char *z = pToken->z;
55665720
int cnt, j;
55675721
for(j=1, cnt=0; j<n-1; j++){
5568
- if( (z[j] & 0xc0)==0xc0 ) continue;
5569
- cnt++;
5570
- if( z[j]=='\\' && z[j+1]!='&' ){
5571
- j++;
5572
- }else if( z[j]=='&' ){
5722
+ char c = z[j];
5723
+ if( c=='\\' && z[j+1]!='&' ){
5724
+ c = z[++j];
5725
+ }else if( c=='&' ){
55735726
int k;
55745727
for(k=j+1; k<j+7 && z[k]!=0 && z[k]!=';'; k++){}
55755728
if( z[k]==';' ) j = k;
5729
+ cnt += 150;
5730
+ continue;
5731
+ }
5732
+ if( (c & 0xc0)==0xc0 ){
5733
+ while( j+1<n-1 && (z[j+1]&0xc0)==0x80 ){ j++; }
5734
+ cnt += 100;
5735
+ continue;
5736
+ }
5737
+ if( c>=0x20 && c<=0x7e ){
5738
+ cnt += awChar[c-0x20];
5739
+ }else{
5740
+ cnt += 100;
55765741
}
55775742
}
55785743
return cnt;
55795744
}
55805745
@@ -5592,51 +5757,26 @@
55925757
** width is now set.
55935758
** (5) This only works for attributes that have an xFit method.
55945759
*/
55955760
static void pik_size_to_fit(Pik *p, PToken *pFit){
55965761
PElem *pElem;
5597
- int w = 0, h = 0;
5598
- int i;
5762
+ PNum w, h;
5763
+ PBox bbox;
55995764
if( p->nErr ) return;
56005765
pElem = p->cur;
56015766
56025767
if( pElem->nTxt==0 ){
56035768
pik_error(0, pFit, "no text to fit to");
56045769
return;
56055770
}
56065771
if( pElem->type->xFit==0 ) return;
5607
- if( (pElem->mProp & A_HEIGHT)==0 ){
5608
- int hasCenter = 0;
5609
- int hasSingleStack = 0;
5610
- int hasDoubleStack = 0;
5611
- pik_txt_vertical_layout(pElem);
5612
- for(i=0; i<pElem->nTxt; i++){
5613
- if( pElem->aTxt[i].eCode & TP_CENTER ){
5614
- hasCenter = 1;
5615
- }else if( pElem->aTxt[i].eCode & (TP_ABOVE2|TP_BELOW2) ){
5616
- hasDoubleStack = 1;
5617
- }else if( pElem->aTxt[i].eCode & (TP_ABOVE|TP_BELOW) ){
5618
- hasSingleStack = 1;
5619
- }
5620
- }
5621
- h = hasCenter + hasSingleStack*2 + hasDoubleStack*2;
5622
- }
5623
- if( (pElem->mProp & A_WIDTH)==0 ){
5624
- for(i=0; i<pElem->nTxt; i++){
5625
- int cnt = pik_text_length(&pElem->aTxt[i]);
5626
- if( pElem->type->eJust==0
5627
- && (pElem->aTxt[i].eCode & TP_JMASK)!=0
5628
- ){
5629
- cnt *= 2;
5630
- }
5631
- if( cnt>w ) w = cnt;
5632
- }
5633
- }
5634
- if( h>0 || w>0 ){
5635
- pik_compute_layout_settings(p);
5636
- pElem->type->xFit(p, pElem, w*p->charWidth, h*p->charHeight);
5637
- }
5772
+ pik_bbox_init(&bbox);
5773
+ pik_compute_layout_settings(p);
5774
+ pik_append_txt(p, pElem, &bbox);
5775
+ w = (bbox.ne.x - bbox.sw.x) + p->charWidth;
5776
+ h = (bbox.ne.y - bbox.sw.y) + 0.5*p->charHeight;
5777
+ pElem->type->xFit(p, pElem, w, h);
56385778
}
56395779
56405780
/* Set a local variable name to "val".
56415781
**
56425782
** The name might be a built-in variable or a color name. In either case,
@@ -6343,15 +6483,13 @@
63436483
thickness = pik_value(p,"thickness",9,0);
63446484
if( thickness<=0.01 ) thickness = 0.01;
63456485
wArrow = 0.5*pik_value(p,"arrowwid",8,0);
63466486
p->wArrow = wArrow/thickness;
63476487
p->hArrow = pik_value(p,"arrowht",7,0)/thickness;
6348
- p->rScale = 144.0*pik_value(p,"scale",5,0);
6349
- if( p->rScale<5.0 ) p->rScale = 5.0;
63506488
p->fontScale = pik_value(p,"fontscale",9,0);
63516489
if( p->fontScale<=0.0 ) p->fontScale = 1.0;
6352
- p->fontScale *= p->rScale/144.0;
6490
+ p->rScale = 144.0;
63536491
p->charWidth = pik_value(p,"charwid",7,0)*p->fontScale;
63546492
p->charHeight = pik_value(p,"charht",6,0)*p->fontScale;
63556493
p->bLayoutVars = 1;
63566494
}
63576495
@@ -6364,10 +6502,11 @@
63646502
PNum thickness; /* Stroke width */
63656503
PNum margin; /* Extra bounding box margin */
63666504
PNum leftmargin; /* Extra bounding box area on the left */
63676505
PNum w, h; /* Drawing width and height */
63686506
PNum wArrow;
6507
+ PNum pikScale; /* Value of the "scale" variable */
63696508
63706509
/* Set up rendering parameters */
63716510
pik_compute_layout_settings(p);
63726511
thickness = pik_value(p,"thickness",9,0);
63736512
if( thickness<=0.01 ) thickness = 0.01;
@@ -6397,10 +6536,18 @@
63976536
}
63986537
w = p->bbox.ne.x - p->bbox.sw.x;
63996538
h = p->bbox.ne.y - p->bbox.sw.y;
64006539
p->wSVG = (int)(p->rScale*w);
64016540
p->hSVG = (int)(p->rScale*h);
6541
+ pikScale = pik_value(p,"scale",5,0);
6542
+ if( pikScale<0.99 || pikScale>1.01 ){
6543
+ p->wSVG *= pikScale;
6544
+ p->hSVG *= pikScale;
6545
+ pik_append_num(p, " width=\"", p->wSVG);
6546
+ pik_append_num(p, "\" height=\"", p->hSVG);
6547
+ pik_append(p, "\"", 1);
6548
+ }
64026549
pik_append_dis(p, " viewBox=\"0 0 ",w,"");
64036550
pik_append_dis(p, " ",h,"\">\n");
64046551
pik_elist_render(p, pEList);
64056552
pik_append(p,"</svg>\n", -1);
64066553
}else{
@@ -7081,6 +7228,6 @@
70817228
}
70827229
return 0;
70837230
}
70847231
#endif /* PIKCHR_SHELL */
70857232
7086
-#line 7111 "pikchr.c"
7233
+#line 7258 "pikchr.c"
70877234
--- src/pikchr.c
+++ src/pikchr.c
@@ -4652,12 +4652,13 @@
4652 jw = 0.0;
4653 }
4654 for(i=0; i<n; i++){
4655 PToken *t = &aTxt[i];
4656 PNum xtraFontScale = 1.0;
4657 orig_y = y = pElem->ptAt.y;
4658 PNum nx = x;
 
4659 if( t->eCode & TP_ABOVE2 ) y += dy2 + 3*dy;
4660 if( t->eCode & TP_ABOVE ) y += dy2 + dy;
4661 if( t->eCode & TP_BELOW ) y -= dy2 + dy;
4662 if( t->eCode & TP_BELOW2 ) y -= dy2 + 3*dy;
4663 if( t->eCode & TP_BIG ) xtraFontScale *= 1.25;
@@ -4667,24 +4668,53 @@
4667 if( t->eCode & TP_RJUST ) nx += jw;
4668
4669 if( pBox!=0 ){
4670 /* If pBox is not NULL, do not draw any <text>. Instead, just expand
4671 ** pBox to include the text */
4672 PNum cw = pik_text_length(t)*p->charWidth*xtraFontScale;
4673 PNum ch = p->charHeight*0.5*xtraFontScale;
 
 
4674 if( t->eCode & TP_RJUST ){
4675 pik_bbox_add_xy(pBox, nx, y-ch);
4676 pik_bbox_add_xy(pBox, nx-cw, y+ch);
 
 
4677 }else if( t->eCode & TP_LJUST ){
4678 pik_bbox_add_xy(pBox, nx, y-ch);
4679 pik_bbox_add_xy(pBox, nx+cw, y+ch);
 
 
4680 }else{
4681 pik_bbox_add_xy(pBox, nx+cw/2, y+ch);
4682 pik_bbox_add_xy(pBox, nx-cw/2, y-ch);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
4683 }
 
 
4684 continue;
4685 }
 
 
4686
4687 pik_append_x(p, "<text x=\"", nx, "\"");
4688 pik_append_y(p, " y=\"", y, "\"");
4689 if( t->eCode & TP_RJUST ){
4690 pik_append(p, " text-anchor=\"end\"", -1);
@@ -4709,14 +4739,16 @@
4709 }
4710 if( (t->eCode & TP_ALIGN)!=0 && pElem->nPath>=2 ){
4711 int n = pElem->nPath;
4712 PNum dx = pElem->aPath[n-1].x - pElem->aPath[0].x;
4713 PNum dy = pElem->aPath[n-1].y - pElem->aPath[0].y;
4714 PNum ang = atan2(dy,dx)*-180/M_PI;
4715 pik_append_num(p, " transform=\"rotate(", ang);
4716 pik_append_xy(p, " ", x, orig_y);
4717 pik_append(p,")\"",2);
 
 
4718 }
4719 pik_append(p," dominant-baseline=\"central\">",-1);
4720 z = t->z+1;
4721 nz = t->n-2;
4722 while( nz>0 ){
@@ -5551,30 +5583,163 @@
5551 else iRes = (iRes &~TP_SZMASK)|TP_SMALL; break;
5552 }
5553 return iRes;
5554 }
5555
5556 /* Return an estimate of the actual number of displayed characters
5557 ** in a character string.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5558 **
5559 ** Omit "\" used to escape characters. And count entities like
5560 ** "&lt;" as a single character. Multi-byte UTF8 characters count
5561 ** as a single character.
 
 
 
 
5562 */
5563 static int pik_text_length(const PToken *pToken){
5564 int n = pToken->n;
5565 const char *z = pToken->z;
5566 int cnt, j;
5567 for(j=1, cnt=0; j<n-1; j++){
5568 if( (z[j] & 0xc0)==0xc0 ) continue;
5569 cnt++;
5570 if( z[j]=='\\' && z[j+1]!='&' ){
5571 j++;
5572 }else if( z[j]=='&' ){
5573 int k;
5574 for(k=j+1; k<j+7 && z[k]!=0 && z[k]!=';'; k++){}
5575 if( z[k]==';' ) j = k;
 
 
 
 
 
 
 
 
 
 
 
 
5576 }
5577 }
5578 return cnt;
5579 }
5580
@@ -5592,51 +5757,26 @@
5592 ** width is now set.
5593 ** (5) This only works for attributes that have an xFit method.
5594 */
5595 static void pik_size_to_fit(Pik *p, PToken *pFit){
5596 PElem *pElem;
5597 int w = 0, h = 0;
5598 int i;
5599 if( p->nErr ) return;
5600 pElem = p->cur;
5601
5602 if( pElem->nTxt==0 ){
5603 pik_error(0, pFit, "no text to fit to");
5604 return;
5605 }
5606 if( pElem->type->xFit==0 ) return;
5607 if( (pElem->mProp & A_HEIGHT)==0 ){
5608 int hasCenter = 0;
5609 int hasSingleStack = 0;
5610 int hasDoubleStack = 0;
5611 pik_txt_vertical_layout(pElem);
5612 for(i=0; i<pElem->nTxt; i++){
5613 if( pElem->aTxt[i].eCode & TP_CENTER ){
5614 hasCenter = 1;
5615 }else if( pElem->aTxt[i].eCode & (TP_ABOVE2|TP_BELOW2) ){
5616 hasDoubleStack = 1;
5617 }else if( pElem->aTxt[i].eCode & (TP_ABOVE|TP_BELOW) ){
5618 hasSingleStack = 1;
5619 }
5620 }
5621 h = hasCenter + hasSingleStack*2 + hasDoubleStack*2;
5622 }
5623 if( (pElem->mProp & A_WIDTH)==0 ){
5624 for(i=0; i<pElem->nTxt; i++){
5625 int cnt = pik_text_length(&pElem->aTxt[i]);
5626 if( pElem->type->eJust==0
5627 && (pElem->aTxt[i].eCode & TP_JMASK)!=0
5628 ){
5629 cnt *= 2;
5630 }
5631 if( cnt>w ) w = cnt;
5632 }
5633 }
5634 if( h>0 || w>0 ){
5635 pik_compute_layout_settings(p);
5636 pElem->type->xFit(p, pElem, w*p->charWidth, h*p->charHeight);
5637 }
5638 }
5639
5640 /* Set a local variable name to "val".
5641 **
5642 ** The name might be a built-in variable or a color name. In either case,
@@ -6343,15 +6483,13 @@
6343 thickness = pik_value(p,"thickness",9,0);
6344 if( thickness<=0.01 ) thickness = 0.01;
6345 wArrow = 0.5*pik_value(p,"arrowwid",8,0);
6346 p->wArrow = wArrow/thickness;
6347 p->hArrow = pik_value(p,"arrowht",7,0)/thickness;
6348 p->rScale = 144.0*pik_value(p,"scale",5,0);
6349 if( p->rScale<5.0 ) p->rScale = 5.0;
6350 p->fontScale = pik_value(p,"fontscale",9,0);
6351 if( p->fontScale<=0.0 ) p->fontScale = 1.0;
6352 p->fontScale *= p->rScale/144.0;
6353 p->charWidth = pik_value(p,"charwid",7,0)*p->fontScale;
6354 p->charHeight = pik_value(p,"charht",6,0)*p->fontScale;
6355 p->bLayoutVars = 1;
6356 }
6357
@@ -6364,10 +6502,11 @@
6364 PNum thickness; /* Stroke width */
6365 PNum margin; /* Extra bounding box margin */
6366 PNum leftmargin; /* Extra bounding box area on the left */
6367 PNum w, h; /* Drawing width and height */
6368 PNum wArrow;
 
6369
6370 /* Set up rendering parameters */
6371 pik_compute_layout_settings(p);
6372 thickness = pik_value(p,"thickness",9,0);
6373 if( thickness<=0.01 ) thickness = 0.01;
@@ -6397,10 +6536,18 @@
6397 }
6398 w = p->bbox.ne.x - p->bbox.sw.x;
6399 h = p->bbox.ne.y - p->bbox.sw.y;
6400 p->wSVG = (int)(p->rScale*w);
6401 p->hSVG = (int)(p->rScale*h);
 
 
 
 
 
 
 
 
6402 pik_append_dis(p, " viewBox=\"0 0 ",w,"");
6403 pik_append_dis(p, " ",h,"\">\n");
6404 pik_elist_render(p, pEList);
6405 pik_append(p,"</svg>\n", -1);
6406 }else{
@@ -7081,6 +7228,6 @@
7081 }
7082 return 0;
7083 }
7084 #endif /* PIKCHR_SHELL */
7085
7086 #line 7111 "pikchr.c"
7087
--- src/pikchr.c
+++ src/pikchr.c
@@ -4652,12 +4652,13 @@
4652 jw = 0.0;
4653 }
4654 for(i=0; i<n; i++){
4655 PToken *t = &aTxt[i];
4656 PNum xtraFontScale = 1.0;
4657 orig_y = pElem->ptAt.y;
4658 PNum nx = 0;
4659 y = 0;
4660 if( t->eCode & TP_ABOVE2 ) y += dy2 + 3*dy;
4661 if( t->eCode & TP_ABOVE ) y += dy2 + dy;
4662 if( t->eCode & TP_BELOW ) y -= dy2 + dy;
4663 if( t->eCode & TP_BELOW2 ) y -= dy2 + 3*dy;
4664 if( t->eCode & TP_BIG ) xtraFontScale *= 1.25;
@@ -4667,24 +4668,53 @@
4668 if( t->eCode & TP_RJUST ) nx += jw;
4669
4670 if( pBox!=0 ){
4671 /* If pBox is not NULL, do not draw any <text>. Instead, just expand
4672 ** pBox to include the text */
4673 PNum cw = pik_text_length(t)*p->charWidth*xtraFontScale*0.01;
4674 PNum ch = p->charHeight*0.5*xtraFontScale;
4675 PNum x0, y0, x1, y1; /* Boundary of text relative to pElem->ptAt */
4676 if( t->eCode & TP_BOLD ) cw *= 1.1;
4677 if( t->eCode & TP_RJUST ){
4678 x0 = nx;
4679 y0 = y-ch;
4680 x1 = nx-cw;
4681 y1 = y+ch;
4682 }else if( t->eCode & TP_LJUST ){
4683 x0 = nx;
4684 y0 = y-ch;
4685 x1 = nx+cw;
4686 y1 = y+ch;
4687 }else{
4688 x0 = nx+cw/2;
4689 y0 = y+ch;
4690 x1 = nx-cw/2;
4691 y1 = y-ch;
4692 }
4693 if( (t->eCode & TP_ALIGN)!=0 && pElem->nPath>=2 ){
4694 int n = pElem->nPath;
4695 PNum dx = pElem->aPath[n-1].x - pElem->aPath[0].x;
4696 PNum dy = pElem->aPath[n-1].y - pElem->aPath[0].y;
4697 if( dx!=0 || dy!=0 ){
4698 PNum dist = hypot(dx,dy);
4699 PNum t;
4700 dx /= dist;
4701 dy /= dist;
4702 t = dx*x0 - dy*y0;
4703 y0 = dy*x0 - dx*y0;
4704 x0 = t;
4705 t = dx*x1 - dy*y1;
4706 y1 = dy*x1 - dx*y1;
4707 x1 = t;
4708 }
4709 }
4710 pik_bbox_add_xy(pBox, x+x0, orig_y+y0);
4711 pik_bbox_add_xy(pBox, x+x1, orig_y+y1);
4712 continue;
4713 }
4714 nx += x;
4715 y += orig_y;
4716
4717 pik_append_x(p, "<text x=\"", nx, "\"");
4718 pik_append_y(p, " y=\"", y, "\"");
4719 if( t->eCode & TP_RJUST ){
4720 pik_append(p, " text-anchor=\"end\"", -1);
@@ -4709,14 +4739,16 @@
4739 }
4740 if( (t->eCode & TP_ALIGN)!=0 && pElem->nPath>=2 ){
4741 int n = pElem->nPath;
4742 PNum dx = pElem->aPath[n-1].x - pElem->aPath[0].x;
4743 PNum dy = pElem->aPath[n-1].y - pElem->aPath[0].y;
4744 if( dx!=0 || dy!=0 ){
4745 PNum ang = atan2(dy,dx)*-180/M_PI;
4746 pik_append_num(p, " transform=\"rotate(", ang);
4747 pik_append_xy(p, " ", x, orig_y);
4748 pik_append(p,")\"",2);
4749 }
4750 }
4751 pik_append(p," dominant-baseline=\"central\">",-1);
4752 z = t->z+1;
4753 nz = t->n-2;
4754 while( nz>0 ){
@@ -5551,30 +5583,163 @@
5583 else iRes = (iRes &~TP_SZMASK)|TP_SMALL; break;
5584 }
5585 return iRes;
5586 }
5587
5588 /*
5589 ** Table of scale-factor estimates for variable-width characters.
5590 ** Actual character widths vary by font. These numbers are only
5591 ** guesses. And this table only provides data for ASCII.
5592 **
5593 ** 100 means normal width.
5594 */
5595 static const unsigned char awChar[] = {
5596 /* Skip initial 32 control characters */
5597 /* ' ' */ 45,
5598 /* '!' */ 55,
5599 /* '"' */ 62,
5600 /* '#' */ 115,
5601 /* '$' */ 90,
5602 /* '%' */ 132,
5603 /* '&' */ 125,
5604 /* '\''*/ 40,
5605
5606 /* '(' */ 55,
5607 /* ')' */ 55,
5608 /* '*' */ 71,
5609 /* '+' */ 115,
5610 /* ',' */ 45,
5611 /* '-' */ 48,
5612 /* '.' */ 45,
5613 /* '/' */ 50,
5614
5615 /* '0' */ 91,
5616 /* '1' */ 91,
5617 /* '2' */ 91,
5618 /* '3' */ 91,
5619 /* '4' */ 91,
5620 /* '5' */ 91,
5621 /* '6' */ 91,
5622 /* '7' */ 91,
5623
5624 /* '8' */ 91,
5625 /* '9' */ 91,
5626 /* ':' */ 50,
5627 /* ';' */ 50,
5628 /* '<' */ 120,
5629 /* '=' */ 120,
5630 /* '>' */ 120,
5631 /* '?' */ 78,
5632
5633 /* '@' */ 142,
5634 /* 'A' */ 102,
5635 /* 'B' */ 105,
5636 /* 'C' */ 110,
5637 /* 'D' */ 115,
5638 /* 'E' */ 105,
5639 /* 'F' */ 98,
5640 /* 'G' */ 105,
5641
5642 /* 'H' */ 125,
5643 /* 'I' */ 58,
5644 /* 'J' */ 58,
5645 /* 'K' */ 107,
5646 /* 'L' */ 95,
5647 /* 'M' */ 145,
5648 /* 'N' */ 125,
5649 /* 'O' */ 115,
5650
5651 /* 'P' */ 95,
5652 /* 'Q' */ 115,
5653 /* 'R' */ 107,
5654 /* 'S' */ 95,
5655 /* 'T' */ 97,
5656 /* 'U' */ 118,
5657 /* 'V' */ 102,
5658 /* 'W' */ 150,
5659
5660 /* 'X' */ 100,
5661 /* 'Y' */ 93,
5662 /* 'Z' */ 100,
5663 /* '[' */ 58,
5664 /* '\\'*/ 50,
5665 /* ']' */ 58,
5666 /* '^' */ 119,
5667 /* '_' */ 72,
5668
5669 /* '`' */ 72,
5670 /* 'a' */ 86,
5671 /* 'b' */ 92,
5672 /* 'c' */ 80,
5673 /* 'd' */ 92,
5674 /* 'e' */ 85,
5675 /* 'f' */ 52,
5676 /* 'g' */ 92,
5677
5678 /* 'h' */ 92,
5679 /* 'i' */ 47,
5680 /* 'j' */ 47,
5681 /* 'k' */ 88,
5682 /* 'l' */ 48,
5683 /* 'm' */ 135,
5684 /* 'n' */ 92,
5685 /* 'o' */ 86,
5686
5687 /* 'p' */ 92,
5688 /* 'q' */ 92,
5689 /* 'r' */ 69,
5690 /* 's' */ 75,
5691 /* 't' */ 58,
5692 /* 'u' */ 92,
5693 /* 'v' */ 80,
5694 /* 'w' */ 121,
5695
5696 /* 'x' */ 81,
5697 /* 'y' */ 80,
5698 /* 'z' */ 76,
5699 /* '{' */ 91,
5700 /* '|'*/ 49,
5701 /* '}' */ 91,
5702 /* '~' */ 118,
5703 };
5704
5705 /* Return an estimate of the width of the displayed characters
5706 ** in a character string. The returned value is 100 times the
5707 ** average character width.
5708 **
5709 ** Omit "\" used to escape characters. And count entities like
5710 ** "&lt;" as a single character. Multi-byte UTF8 characters count
5711 ** as a single character.
5712 **
5713 ** Attempt to scale the answer by the actual characters seen. Wide
5714 ** characters count more than narrow characters. But the widths are
5715 ** only guesses.
5716 */
5717 static int pik_text_length(const PToken *pToken){
5718 int n = pToken->n;
5719 const char *z = pToken->z;
5720 int cnt, j;
5721 for(j=1, cnt=0; j<n-1; j++){
5722 char c = z[j];
5723 if( c=='\\' && z[j+1]!='&' ){
5724 c = z[++j];
5725 }else if( c=='&' ){
 
5726 int k;
5727 for(k=j+1; k<j+7 && z[k]!=0 && z[k]!=';'; k++){}
5728 if( z[k]==';' ) j = k;
5729 cnt += 150;
5730 continue;
5731 }
5732 if( (c & 0xc0)==0xc0 ){
5733 while( j+1<n-1 && (z[j+1]&0xc0)==0x80 ){ j++; }
5734 cnt += 100;
5735 continue;
5736 }
5737 if( c>=0x20 && c<=0x7e ){
5738 cnt += awChar[c-0x20];
5739 }else{
5740 cnt += 100;
5741 }
5742 }
5743 return cnt;
5744 }
5745
@@ -5592,51 +5757,26 @@
5757 ** width is now set.
5758 ** (5) This only works for attributes that have an xFit method.
5759 */
5760 static void pik_size_to_fit(Pik *p, PToken *pFit){
5761 PElem *pElem;
5762 PNum w, h;
5763 PBox bbox;
5764 if( p->nErr ) return;
5765 pElem = p->cur;
5766
5767 if( pElem->nTxt==0 ){
5768 pik_error(0, pFit, "no text to fit to");
5769 return;
5770 }
5771 if( pElem->type->xFit==0 ) return;
5772 pik_bbox_init(&bbox);
5773 pik_compute_layout_settings(p);
5774 pik_append_txt(p, pElem, &bbox);
5775 w = (bbox.ne.x - bbox.sw.x) + p->charWidth;
5776 h = (bbox.ne.y - bbox.sw.y) + 0.5*p->charHeight;
5777 pElem->type->xFit(p, pElem, w, h);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5778 }
5779
5780 /* Set a local variable name to "val".
5781 **
5782 ** The name might be a built-in variable or a color name. In either case,
@@ -6343,15 +6483,13 @@
6483 thickness = pik_value(p,"thickness",9,0);
6484 if( thickness<=0.01 ) thickness = 0.01;
6485 wArrow = 0.5*pik_value(p,"arrowwid",8,0);
6486 p->wArrow = wArrow/thickness;
6487 p->hArrow = pik_value(p,"arrowht",7,0)/thickness;
 
 
6488 p->fontScale = pik_value(p,"fontscale",9,0);
6489 if( p->fontScale<=0.0 ) p->fontScale = 1.0;
6490 p->rScale = 144.0;
6491 p->charWidth = pik_value(p,"charwid",7,0)*p->fontScale;
6492 p->charHeight = pik_value(p,"charht",6,0)*p->fontScale;
6493 p->bLayoutVars = 1;
6494 }
6495
@@ -6364,10 +6502,11 @@
6502 PNum thickness; /* Stroke width */
6503 PNum margin; /* Extra bounding box margin */
6504 PNum leftmargin; /* Extra bounding box area on the left */
6505 PNum w, h; /* Drawing width and height */
6506 PNum wArrow;
6507 PNum pikScale; /* Value of the "scale" variable */
6508
6509 /* Set up rendering parameters */
6510 pik_compute_layout_settings(p);
6511 thickness = pik_value(p,"thickness",9,0);
6512 if( thickness<=0.01 ) thickness = 0.01;
@@ -6397,10 +6536,18 @@
6536 }
6537 w = p->bbox.ne.x - p->bbox.sw.x;
6538 h = p->bbox.ne.y - p->bbox.sw.y;
6539 p->wSVG = (int)(p->rScale*w);
6540 p->hSVG = (int)(p->rScale*h);
6541 pikScale = pik_value(p,"scale",5,0);
6542 if( pikScale<0.99 || pikScale>1.01 ){
6543 p->wSVG *= pikScale;
6544 p->hSVG *= pikScale;
6545 pik_append_num(p, " width=\"", p->wSVG);
6546 pik_append_num(p, "\" height=\"", p->hSVG);
6547 pik_append(p, "\"", 1);
6548 }
6549 pik_append_dis(p, " viewBox=\"0 0 ",w,"");
6550 pik_append_dis(p, " ",h,"\">\n");
6551 pik_elist_render(p, pEList);
6552 pik_append(p,"</svg>\n", -1);
6553 }else{
@@ -7081,6 +7228,6 @@
7228 }
7229 return 0;
7230 }
7231 #endif /* PIKCHR_SHELL */
7232
7233 #line 7258 "pikchr.c"
7234

Keyboard Shortcuts

Open search /
Next entry (timeline) j
Previous entry (timeline) k
Open focused entry Enter
Show this help ?
Toggle theme Top nav button