 |
|
 |
It is enough to call SetGraphicsMode with GM_ADVANCED and specify lfEscapement = 2700, lfOrientation = 0 in a call to CreateFontIndirect and use regular TextOut function to achieve such effect
|
|
|
|
 |
|
 |
1. In GetCharSize(), should add "delete[] lpBits;" before "return results.bottom - results.top;"
2. In DrawVertText(), the brush created by "CreateSolidBrush(bkCol)" should be delete after using
3. DeleteObject() was abused, such as "DeleteObject(oBuf);" and "DeleteObject(hOldCharBmp);"
Look ahead, all is dark.
|
|
|
|
 |
|
 |
Pretty good solution for a common problem; nice work. Here's a couple things that you may have fixed already -it's been a year since you posted:
- There's a memory leak in GetCharSize
if (lpChar[0] == _T(' ')) {
delete[] lpBits; // this fixes the leak
...
return results.bottom - results.top;
}
- In DrawVertText the flollwing code works as intended only if lpRect->top is 0. In cases where lpRect->top is near the bottom of the screen and iTotalHeight is longer than the rectangle's height, text appears near the top of the screen and outside the specified rect:
if (bVCenter)
ScrY = ((lpRect->bottom-lpRect->top)/2) - (iTotalHeight/2);
Something like the following while, not a universal solution, prevents the above behavior:
ScrY = lpRect->top + max(0, ((lpRect->bottom-lpRect->top) - iTotalHeight)/2);
Finally, it's a good idea to clip to lpRect by default. Something like the following accomplishes that:
if (tc[0] != _T('\n')) {
RECT clipRect;
IntersectRect(&clipRect, lpRect, &CharacterPositions[i]);
DrawText(hdc,tc,_tclen(tc),&clipRect,DT_CENTER);
}
Again, good job. Just some thoughts.
|
|
|
|
 |
|
 |
For Chinese and its sibling languages, writing in vertical is fully meaningful and correct. Actually the languages were written only vertically a few decades ago, a tradition inherited from the ages people didn't have paper and had to write on strips of woods and bones etc. Although horizontal writing is in mode for most case at present, some times we still have the needs to put out text messages in a vertical aligned space, and it is then only natural to write the characters vertically. It won't look unnatural like Latin letter based writing does at all.
Before I desired that Windows would offer such a function in its API, and now we have we it here, which works very well. Barvo, and thanks a lot!!
|
|
|
|
 |
|
 |
Thankyou for the compilment. It's funny you you talk about the Chinese writing on strips of Bamboo and bone, since it was the Chinese who first invented paper. Infact I wonder where the concept of writing horizontaly ever came from?
|
|
|
|
 |
|
 |
Right. Writing was invented a few thousands years before the invention of paper in China. And amazingly enough, the ancient folk managed to write quite a few great books on philosophy, history and poetry before the era of paper-making – what an formidable undertaking it was for those who made them. It could easily take two weeks to carve only two pages on the wood strips, and image what a poor guy would do if he only carved one character wrong!
Here I would like to express one desire and then ask you a question.
Could you add yet another DV_XX value for the uFormat parameter of the DrawVertText() function as an option, which would enable multiple lines of text be aligned from right to left as an option (the default should be from left to write)? That is the way Chinese and its siblings should be written when put vertically.
In your article, you said that you draw the characters on a bitmap in order to measure its dimension. What do you use when actually drawing the text on the Device Context(DC), the HFONT font or the bitmap? Drawing the bitmap is totally OK on screen DC, but when dumped to a printer, it would be much coarser than font, and is thus less desirable.
Thank you again. I also like it that you used raw WIN32 in your coding, which makes it more versatile than a MFC implementation would be.
|
|
|
|
 |
|
 |
Adding a right to left reading order shouldn't be too difficult to implement. Some rearranging of the internal sequences and a reverse scan of the string should do it. I will add it when I get time.
The characters are drawn onto a bitmap internally, but the bitmap is constantly being reused. The first version did infact draw all the characters to bitmap, then this bitmap was then bitblt'ed to the DC, but it proved to be too slow. I have changed this in the new version. Now the size of each character is scanned and stored in an array of RECT structures. After all the sizes have been determined each RECT is offset and used in a call to the normal DrawText(). So using this function with a printers DC should be no problem.
Infact in a newer version I may add the possibilty of retrieving the RECT array so that it can be used in further calls with the same string. It may improve the speed a little.
You will find all of my code is raw WIN32, I don't use MFC nor do I have any intention to start. Yes MFC is a lot quicker, but if you make a habit of storing and reusing libraries, then WIN32 can be just as quick.
|
|
|
|
 |
|
 |
Here is the addition you requested. I will post it here with instruction on how to add, and later update the article.
1. Add this function to VertDraw.cpp
static LPCTSTR RightToLeft(LPCTSTR lpString,LPTSTR lpOut)
{
LPTSTR szPos,szBuf;
int iTextLength = _tcslen(lpString);
szBuf = new TCHAR [iTextLength+1];
_tcscpy(szBuf,lpString);
*lpOut = 0;
szPos = _tcsrchr(szBuf,_T('\n'));
if ( szPos ) {
_tcscat(lpOut,szPos+1);
*szPos = 0;
}
while (1) {
szPos = _tcsrchr(szBuf,_T('\n'));
if ( ! szPos )
break;
_tcscat(lpOut,szPos);
*szPos = 0;
}
szPos = szBuf;
_tcscat(lpOut,_T("\n"));
_tcscat(lpOut,szPos);
delete [] szBuf;
return lpOut;
}
2. Change the lpString in the function paramater list..
int DrawVertText(HDC hdc,LPCTSTR lpString,int nCount,LPRECT lpRect,UINT...
to lpInput.
3. Also change the lpString a few lines down...
int iTextLength = _tcsclen(lpString);
to lpInput.
4. Just below the if statements for the uFormat paramater, and above the "Calc longest line" loop, add the following
LPTSTR lpString = new TCHAR [iTextLength+1];
if ((uFormat & DV_RIGHTTOLEFT) == DV_RIGHTTOLEFT)
{
RightToLeft(lpInput,lpString);
}
else
_tcscpy(lpString,lpInput);
5. add a #define DV_RIGHTTOLEFT 0x0x0040 to the header file.
6. add a delete [] lpString to the end of the function.
And now you have right to left verticaly aligned text.
|
|
|
|
 |
|
 |
Hello waldermort
I copied the code to the donwloaded demo project in the way indicated by you, and it worked to draw the vertical text from right to left. When I entered some new texts with "Edit/Input Text" menu, however, a crash took place which did not happen in the original project, at the line
delete [] lpString;
In the end of function DrawVertText().
Looking inside the function, I saw that the string is allocated mid in the function. Do you have any idea why this happen?
Your demo project runs well with VC6.0 But with VC2003, a warning dialog showed up in function EnterString() at the two instances of
while (idx != NULL) {
idx = _tcsstr(tmp,_T("\n"));
....
....
}
which said that "pointer idx is used without initialization". I Change it to the follo
following, and the warning disappeared:
do {
idx = _tcsstr(tmp,_T("\n"));
....
....
} while (idx != NULL);
Thank you a great deal for the nice work. We appreciate it!
|
|
|
|
 |
|
 |
Thanks for the bug report. I believe you were getting a crash because after entering a string it was not correctly null terminated. Here is a possible fix, please let me know of the outcome
if (LOWORD(wParam) == IDOK) {
memset(szBuf,0,MAX_LOADSTRING);
szBuf[0] = MAX_LOADSTRING;
int len = SendMessage(GetDlgItem(hDlg,IDC_EDIT),EM_GETLINE,0,(LONG)szBuf);
if (len <= 0) {
EndDialog(hDlg,0);
return FALSE;
}
szBuf[len] = 0;
memset(szString,0,MAX_LOADSTRING);
tmp = szBuf;
do {
idx = _tcsstr(tmp,_T("\\n"));
_tcsncat(szString,tmp,idx-tmp);
if (idx != 0) {
_tcscat(szString,_T("\n"));
tmp = idx;
tmp = _tcsinc(tmp);
tmp = _tcsinc(tmp);
}
} while (idx != NULL);
To be added to the EnterString string callback. Please notice the addition of a null terminator before looping through the string, also you suggestion of a do while loop.
I will update the article and code as soon as no more bugs are reported.
|
|
|
|
 |
|
 |
Hey, waldermort
Thank you for your prompt reply. Unfortunately, it still crashed at
the same place, when I typed a new string in the dialog
brought up by "Edit/Input Text" nemu, and replacing your new code
at the spot you indicated.
The following texts were shown on the crash dialog
==================================
Debug Error!
Program: ....\Demo.exe
DAMAGE: ater normal block(#9296) at 0x011c6710
==================================
Looking at your code, I saw that in immediatlely after you allocated lpString in function DrawVertText()
LPTSTR lpString = new TCHAR [iTextLength+1];
the varaible lpString is passed into function RightToLeft() as the 2nd parameter. And in that function, the value of it is manipulated multiple times. Could you have changed the memory block it points to that made
delete [] lpString;
to crash in the end of fucntion DrawVertText()?
Best wishes
|
|
|
|
 |
|
 |
Could you possibly give me a sample string that is causing the crash? I will find the problem, I'm just having a hard time trying to reproduce it.
|
|
|
|
 |
|
 |
Hello
Thank you for the code. I have copied them into
the downloaded software of yours, and it worked
well!
I used Visual Studio 6.0.
Looking forwads to your new version of the article and your code
|
|
|
|
 |
|
 |
I mean, why not use set lfEscapement and lfOrientation and call it a day?
This is slow (moving pixels arround) and problematic for any text but Latin (I would like to see it handling Vietnamese, Thai, Arabic or Hindi
|
|
|
|
 |
|
 |
Mihai Nita wrote: why not use set lfEscapement and lfOrientation and call it a day?
These were not designed for drawing text verticaly. Give it a try, playing around with those settings will only result in an unproportional alignment. The purpose of this code is to correct that problem.
Mihai Nita wrote: and problematic for any text but Latin
Not true at all, any character from any language will work perfectly. I actualy designed the code to work with Chinese characters. Also if you look at the source, the size of each character is determined by mapping the pixels.
Mihai Nita wrote: This is slow (moving pixels arround)
I have to agree here, thats why I have just completely recoded it. In the new version no pixels are moved around. Each character is draw (off screen) mapped and stored. I have done away with the multi bitmaps and fixed a few other problems.
|
|
|
|
 |
|
 |
waldermort wrote:
These were not designed for drawing text verticaly. Give it a try, playing around with those settings will only result in an unproportional alignment. The purpose of this code is to correct that problem.
I have played with them, and the only problem that I see is multiple lines.
I don't understant what you mean by "unproportional alignment." I have tried stuff like He-lo and the dash is not as tall as the H, if this is what you mean.
waldermort wrote:
Not true at all, any character from any language will work perfectly. I actualy designed the code to work with Chinese characters. Also if you look at the source, the size of each character is determined by mapping the pixels.
My examples are all about complex scripts. Chinese is not. In complex scripts several Unicode code points combine to for one character. Your code is not doing the drawing for each character, but for each code point. Try something like . Also problems with some Chinese Traditional (characters beyond U+FFFF, using surrogates)
|
|
|
|
 |
|