Fossil SCM
New pikchr.c with improved estimates for bounding boxes on text.
Commit
bac677f7a968d33b486f1caf794a3bebd2bc63605fa4c91b813cc87753bb73b0
Parent
3ec33f98f7cb23e…
1 file changed
+204
-57
+204
-57
| --- src/pikchr.c | ||
| +++ src/pikchr.c | ||
| @@ -4652,12 +4652,13 @@ | ||
| 4652 | 4652 | jw = 0.0; |
| 4653 | 4653 | } |
| 4654 | 4654 | for(i=0; i<n; i++){ |
| 4655 | 4655 | PToken *t = &aTxt[i]; |
| 4656 | 4656 | 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; | |
| 4659 | 4660 | if( t->eCode & TP_ABOVE2 ) y += dy2 + 3*dy; |
| 4660 | 4661 | if( t->eCode & TP_ABOVE ) y += dy2 + dy; |
| 4661 | 4662 | if( t->eCode & TP_BELOW ) y -= dy2 + dy; |
| 4662 | 4663 | if( t->eCode & TP_BELOW2 ) y -= dy2 + 3*dy; |
| 4663 | 4664 | if( t->eCode & TP_BIG ) xtraFontScale *= 1.25; |
| @@ -4667,24 +4668,53 @@ | ||
| 4667 | 4668 | if( t->eCode & TP_RJUST ) nx += jw; |
| 4668 | 4669 | |
| 4669 | 4670 | if( pBox!=0 ){ |
| 4670 | 4671 | /* If pBox is not NULL, do not draw any <text>. Instead, just expand |
| 4671 | 4672 | ** 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; | |
| 4673 | 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; | |
| 4674 | 4677 | 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; | |
| 4677 | 4682 | }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; | |
| 4680 | 4687 | }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 | + } | |
| 4683 | 4709 | } |
| 4710 | + pik_bbox_add_xy(pBox, x+x0, orig_y+y0); | |
| 4711 | + pik_bbox_add_xy(pBox, x+x1, orig_y+y1); | |
| 4684 | 4712 | continue; |
| 4685 | 4713 | } |
| 4714 | + nx += x; | |
| 4715 | + y += orig_y; | |
| 4686 | 4716 | |
| 4687 | 4717 | pik_append_x(p, "<text x=\"", nx, "\""); |
| 4688 | 4718 | pik_append_y(p, " y=\"", y, "\""); |
| 4689 | 4719 | if( t->eCode & TP_RJUST ){ |
| 4690 | 4720 | pik_append(p, " text-anchor=\"end\"", -1); |
| @@ -4709,14 +4739,16 @@ | ||
| 4709 | 4739 | } |
| 4710 | 4740 | if( (t->eCode & TP_ALIGN)!=0 && pElem->nPath>=2 ){ |
| 4711 | 4741 | int n = pElem->nPath; |
| 4712 | 4742 | PNum dx = pElem->aPath[n-1].x - pElem->aPath[0].x; |
| 4713 | 4743 | 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 | + } | |
| 4718 | 4750 | } |
| 4719 | 4751 | pik_append(p," dominant-baseline=\"central\">",-1); |
| 4720 | 4752 | z = t->z+1; |
| 4721 | 4753 | nz = t->n-2; |
| 4722 | 4754 | while( nz>0 ){ |
| @@ -5551,30 +5583,163 @@ | ||
| 5551 | 5583 | else iRes = (iRes &~TP_SZMASK)|TP_SMALL; break; |
| 5552 | 5584 | } |
| 5553 | 5585 | return iRes; |
| 5554 | 5586 | } |
| 5555 | 5587 | |
| 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. | |
| 5558 | 5708 | ** |
| 5559 | 5709 | ** Omit "\" used to escape characters. And count entities like |
| 5560 | 5710 | ** "<" as a single character. Multi-byte UTF8 characters count |
| 5561 | 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. | |
| 5562 | 5716 | */ |
| 5563 | 5717 | static int pik_text_length(const PToken *pToken){ |
| 5564 | 5718 | int n = pToken->n; |
| 5565 | 5719 | const char *z = pToken->z; |
| 5566 | 5720 | int cnt, j; |
| 5567 | 5721 | 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=='&' ){ | |
| 5573 | 5726 | int k; |
| 5574 | 5727 | for(k=j+1; k<j+7 && z[k]!=0 && z[k]!=';'; k++){} |
| 5575 | 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; | |
| 5576 | 5741 | } |
| 5577 | 5742 | } |
| 5578 | 5743 | return cnt; |
| 5579 | 5744 | } |
| 5580 | 5745 | |
| @@ -5592,51 +5757,26 @@ | ||
| 5592 | 5757 | ** width is now set. |
| 5593 | 5758 | ** (5) This only works for attributes that have an xFit method. |
| 5594 | 5759 | */ |
| 5595 | 5760 | static void pik_size_to_fit(Pik *p, PToken *pFit){ |
| 5596 | 5761 | PElem *pElem; |
| 5597 | - int w = 0, h = 0; | |
| 5598 | - int i; | |
| 5762 | + PNum w, h; | |
| 5763 | + PBox bbox; | |
| 5599 | 5764 | if( p->nErr ) return; |
| 5600 | 5765 | pElem = p->cur; |
| 5601 | 5766 | |
| 5602 | 5767 | if( pElem->nTxt==0 ){ |
| 5603 | 5768 | pik_error(0, pFit, "no text to fit to"); |
| 5604 | 5769 | return; |
| 5605 | 5770 | } |
| 5606 | 5771 | 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); | |
| 5638 | 5778 | } |
| 5639 | 5779 | |
| 5640 | 5780 | /* Set a local variable name to "val". |
| 5641 | 5781 | ** |
| 5642 | 5782 | ** The name might be a built-in variable or a color name. In either case, |
| @@ -6343,15 +6483,13 @@ | ||
| 6343 | 6483 | thickness = pik_value(p,"thickness",9,0); |
| 6344 | 6484 | if( thickness<=0.01 ) thickness = 0.01; |
| 6345 | 6485 | wArrow = 0.5*pik_value(p,"arrowwid",8,0); |
| 6346 | 6486 | p->wArrow = wArrow/thickness; |
| 6347 | 6487 | 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 | 6488 | p->fontScale = pik_value(p,"fontscale",9,0); |
| 6351 | 6489 | if( p->fontScale<=0.0 ) p->fontScale = 1.0; |
| 6352 | - p->fontScale *= p->rScale/144.0; | |
| 6490 | + p->rScale = 144.0; | |
| 6353 | 6491 | p->charWidth = pik_value(p,"charwid",7,0)*p->fontScale; |
| 6354 | 6492 | p->charHeight = pik_value(p,"charht",6,0)*p->fontScale; |
| 6355 | 6493 | p->bLayoutVars = 1; |
| 6356 | 6494 | } |
| 6357 | 6495 | |
| @@ -6364,10 +6502,11 @@ | ||
| 6364 | 6502 | PNum thickness; /* Stroke width */ |
| 6365 | 6503 | PNum margin; /* Extra bounding box margin */ |
| 6366 | 6504 | PNum leftmargin; /* Extra bounding box area on the left */ |
| 6367 | 6505 | PNum w, h; /* Drawing width and height */ |
| 6368 | 6506 | PNum wArrow; |
| 6507 | + PNum pikScale; /* Value of the "scale" variable */ | |
| 6369 | 6508 | |
| 6370 | 6509 | /* Set up rendering parameters */ |
| 6371 | 6510 | pik_compute_layout_settings(p); |
| 6372 | 6511 | thickness = pik_value(p,"thickness",9,0); |
| 6373 | 6512 | if( thickness<=0.01 ) thickness = 0.01; |
| @@ -6397,10 +6536,18 @@ | ||
| 6397 | 6536 | } |
| 6398 | 6537 | w = p->bbox.ne.x - p->bbox.sw.x; |
| 6399 | 6538 | h = p->bbox.ne.y - p->bbox.sw.y; |
| 6400 | 6539 | p->wSVG = (int)(p->rScale*w); |
| 6401 | 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 | + } | |
| 6402 | 6549 | pik_append_dis(p, " viewBox=\"0 0 ",w,""); |
| 6403 | 6550 | pik_append_dis(p, " ",h,"\">\n"); |
| 6404 | 6551 | pik_elist_render(p, pEList); |
| 6405 | 6552 | pik_append(p,"</svg>\n", -1); |
| 6406 | 6553 | }else{ |
| @@ -7081,6 +7228,6 @@ | ||
| 7081 | 7228 | } |
| 7082 | 7229 | return 0; |
| 7083 | 7230 | } |
| 7084 | 7231 | #endif /* PIKCHR_SHELL */ |
| 7085 | 7232 | |
| 7086 | -#line 7111 "pikchr.c" | |
| 7233 | +#line 7258 "pikchr.c" | |
| 7087 | 7234 |
| --- 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 | ** "<" 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 | ** "<" 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 |