DirectWrite space char wrapping - direct2d

First of all sorry for my bad English. But i can't solve my problem for the last few days. I am working on a simple text editor that use DirectWrite to render text. When i render my buffer, that contains a text, direct2d wraps my words, but this doesn't work when i type space chars ' ' or tabs '\t'. My cursor is going outside the editor's window and doesn't "jumt" to the new line.
int RenderSystem::Init(
Settings* set,
HWND hwnd
)
{
if (system_is_init)
return 0;
settings = set;
D2D1CreateFactory(
D2D1_FACTORY_TYPE_SINGLE_THREADED,
&factory
);
DWriteCreateFactory(
DWRITE_FACTORY_TYPE_SHARED,
__uuidof(IDWriteFactory),
(IUnknown**)(&write_factory)
);
LoadFontCollection(L"Fonts/liberation-mono/liberation-mono.ttf");
write_factory->CreateTextFormat(
L"Liberation Mono",
font_collection,
DWRITE_FONT_WEIGHT_REGULAR,
DWRITE_FONT_STYLE_NORMAL,
DWRITE_FONT_STRETCH_NORMAL,
(float)settings->font_size,
L"en-us",
&text_format
);
text_format->SetWordWrapping(
DWRITE_WORD_WRAPPING_WRAP
);
text_renderer = new BasicTextRenderer();
return 0;
}
void RenderSystem::Render(
ID2D1HwndRenderTarget* render_target,
Buffer* buffer
)
{
render_target->CreateSolidColorBrush(
settings->text_foreground_color,
&text_foreground_brush
);
render_target->CreateSolidColorBrush(
settings->cursor_background_color,
&cursor_background_brush
);
render_target->CreateSolidColorBrush(
settings->cursor_foreground_colot,
&cursor_foreground_brush
);
D2D1_SIZE_U render_target_size = render_target->GetPixelSize();
IDWriteTextLayout* text_layout;
UINT size;
WCHAR* text = buffer->GetData(size); //Here i get text from my buffer
write_factory->CreateTextLayout(
text,
size,
text_format,
(float)render_target_size.width,
(float)render_target_size.height,
&text_layout
);
delete[] text;
DrawingContext* drawing_context = new DrawingContext(
render_target,
text_foreground_brush
);
DrawingEffect* cursor_effect = new DrawingEffect(
cursor_foreground_brush,
cursor_background_brush
);
DWRITE_TEXT_RANGE text_range;
text_range.startPosition = buffer->GetCursorPos();
text_range.length = 1;
text_layout->SetDrawingEffect(
cursor_effect,
text_range
);
render_target->BeginDraw();
render_target->Clear(settings->background_color);
text_layout->Draw(
drawing_context,
text_renderer,
0,
0
);
render_target->EndDraw();
text_layout->Release();
text_foreground_brush->Release();
cursor_background_brush->Release();
cursor_foreground_brush->Release();
delete drawing_context;
}
I hope someone will understand what my problem is... Thank you for any help.

I don't think it's necessarily wrong. It's just the way DirectWrite treats trailing spacing characters - they do not contribute to wrapping line width. You can try different wrapping modes, but what you probably need is to implement layout logic yourself, using cluster metrics and line breaking positions from DirectWrite API.
DirectWrite text layout formatting capabilities are enough for general UI text rendering, anything more complicated requires lower level DirectWrite API (text analyzer) and more complicated logic at application side.

Related

Something strange with positioning text with custom font in Itext7

I'm working on a program that creates several pdf docs and puts different text in the same location in them.
Text should be placed in a particular area and if it doesn't fit it in width it should wrap. It also has a custom font and may be differently aligned in that area. It should be Vertically aligned to Top because when the area is laid out for three lines and I has only one, it should appear on the top. Finally, I need to preserve leading on the level of font-size.
It is important to be precise in text positioning (e.g. I need an upper left corner of "H" from "Hello world" to appear definitely at 0, 100).
Now, I'm using
canvas.showTextAligned(paragraph, 0, 300,
TextAlignment.valueOf(alignment),
VerticalAlignment.TOP);
However, when I try to implement it with different fonts it has a various offset from desired y = 300. Moreover, offset differ from font to font. For Helvetica (everywhere 50 fontSize is used) offset is about 13 px, for Oswald about 17 px and for SedgwickAveDisplay it is massive 90 px.
I added borders to paragraph for debugging purpose and things become more strange.
Helvetica:
SedgwickAveDisplay:
The full snippet of my code to create pdf is below:
public byte[] createBadgeInMemory(int i) throws IOException {
ByteArrayOutputStream out = new ByteArrayOutputStream();
PdfDocument newPdf = new PdfDocument(new PdfWriter(out));
srcPdf.copyPagesTo(1,1,newPdf);
PdfPage page = newPdf.getFirstPage();
PdfCanvas pdfCanvas = new PdfCanvas(page);
Canvas canvas = new Canvas(pdfCanvas, newPdf, pageSize);
File defaultFont = new File("src/main/resources/fonts/Helvetica.otf");
PdfFont font = PdfFontFactory
.createFont(fontPath == null ? defaultFont.getAbsolutePath() : fontPath,
PdfEncodings.IDENTITY_H, true);
String value = "Example word";
Paragraph paragraph = new Paragraph(value);
float textWidth = font.getWidth("Example", 50);
paragraph.setWidth(textWidth);
switch (alignment) {
case("CENTER"):
textWidth /= 2;
break;
case("RIGHT"):
break;
default:
textWidth = 0;
break;
}
paragraph.setFont(font)
.setFontSize(fontSize)
.setFixedLeading(fontSize)
.setFontColor(new DeviceRgb(red, green, blue))
.setMargin(0)
.setPadding(0);
paragraph.setBorderTop(new DashedBorder(Color.BLACK, 0.5f))
.setBorderBottom(new DashedBorder(Color.BLACK, 0.5f))
.setBorderRight(new DashedBorder(Color.BLACK, 0.5f));
paragraph.setHyphenation(new HyphenationConfig(0,
"Example".length()));
canvas.showTextAligned(paragraph,
0 + textWidth,
300,
TextAlignment.valueOf(alignment),
VerticalAlignment.TOP);
newPdf.close();
return out.toByteArray();
}
I also tried variant from here, but for some reason text inside rectangle cuts out at some point (for instance, if I have area width 100px and text snippet I put in that I know occupies exactly 100 px (with the help of font.getWidth(value)), I have my text cut at nearly 80 px).
I also haven't found a way to align text inside a rectangle.
This is the result with Rectangle. A solid border is Rectangle border. As you can see it cuts letter "t" in "Redundant". It also should contain "word" on the second line, but it doesn't.
I copied code from an example.
I need your help. What am I doing wrong or may be there is another way to layout text in particular area with alignment and font?
Thank you in advance.
Update 21.09.17
Also tried variant from this question with SedgwickAveDisplay:
paragraph.setHorizontalAlignment(HorizontalAlignment.LEFT);
paragraph.setVerticalAlignment(VerticalAlignment.TOP);
paragraph.setFixedPosition( 0, 300 - textHeight, "Example".length());
doc.add(paragraph);
The result is the same as on the second screenshot.
This is a font-specific problem. iText guesses information about font glyphs, namely the bboxes incorrectly.
There is a quick and dirty method to adjust this behavior. You can create a custom renderer for text and adjust the calculated positions in it. An example of such a class would be as follows:
class CustomTextRenderer extends TextRenderer {
private CustomTextRenderer(Text text) {
super(text);
}
#Override
public LayoutResult layout(LayoutContext layoutContext) {
LayoutResult result = super.layout(layoutContext);
Rectangle oldBbox = this.occupiedArea.getBBox().clone();
// you can also make the height more than font size or less if needed
this.occupiedArea.setBBox(oldBbox.moveUp(oldBbox.getHeight() - fontSize).setHeight(fontSize));
yLineOffset = fontSize * 0.8f; // here you config the proportion of the ascender
return result;
}
#Override
public IRenderer getNextRenderer() {
return new CustomTextRenderer((Text) modelElement);
}
}
In order that new rendering logic to be applied, you have to use it in the following way:
Text text = new Text(value);
text.setNextRenderer(new CustomTextRenderer(text));
Paragraph paragraph = new Paragraph(text);
Please note that you have to be very careful with this kind of low-level layouting, be aware of you are doing and use this as seldom as possible.
Finally, I created a variant that worked for me.
pdfCanvas.beginText()
.setFontAndSize(font, fontSize)
.setLeading(fontSize)
.moveText(0, 300);
numberOfLines = 0;
sumOfShifts = 0;
float maxWidth = computeStringWidth("Exaxple");
String[] words = value.split("\\s");
StringBuilder line = new StringBuilder();
line.append(words[0]);
float spaceWidth = computeStringWidth(" ") ;
float lineWidth;
for (int index = 1; index < words.length; index++) {
String word = words[index];
float wordWidth = computeStringWidth(word) ;
lineWidth = computeStringWidth(line.toString()) ;
if (lineWidth + spaceWidth + wordWidth <= maxWidth) {
line.append(" ").append(word);
} else {
showTextAligned(alignment, pdfCanvas, line.toString(), lineWidth, maxWidth);
line.delete(0, line.length());
line.append(word);
}
}
if(line.length() != 0) {
lineWidth = computeStringWidth(line.toString()) ;
showTextAligned(alignment, pdfCanvas, line.toString(), lineWidth, maxWidth);
}
pdfCanvas.endText();
As computeStringWidth(String str) I used
Toolkit.getToolkit().getFontLoader().computeStringWidth(String str, Font font);
from import com.sun.javafx.tk.Toolkit with Font from javafx.scene.text.Font. I've chosen it because I use it in other parts of my app.
showTextAligned(...) is my own method that looks this way:
private void showTextAligned(String alignment,
PdfCanvas pdfCanvas,
String line,
float lineWidth,
float maxWidth) {
switch (alignment) {
case "CENTER": {
float shift = (maxWidth - lineWidth) / 2 - sumOfShifts;
pdfCanvas.moveText(shift, 0);
if(numberOfLines == 0) pdfCanvas.showText(line);
else pdfCanvas.newlineShowText(line);
numberOfLines++;
sumOfShifts += shift;
break;
}
case "RIGHT": {
float shift = maxWidth - lineWidth - sumOfShifts;
pdfCanvas.moveText(shift, 0);
if(numberOfLines == 0) pdfCanvas.showText(line);
else pdfCanvas.newlineShowText(line);
numberOfLines++;
sumOfShifts += shift;
break;
}
default:
pdfCanvas.moveText(0, 0);
if(numberOfLines == 0) pdfCanvas.showText(line);
else pdfCanvas.newlineShowText(line);
numberOfLines++;
break;
}
}
In my project, I used my variant, because it gives me an opportunity to work with hyphenation deeper (for instance, I can in future add functionality to avoid putting prepositions as a last word in the line).

Save high quality images from Autocad

my problem is that I don´t have much experience with Autocad. Thus I don´t know how to save a project in a good quality image (png?) to insert in a latex document.
Could you give me a hint?
Thank you
Autocad's Publish to Web printers are pretty bad. What I would do is print using DWG to PDF printer or similar (there are a few in autocad's default printer list) then convert that pdf to raster images using a second software like Photoshop, GIMP, etc. There are even small software that convert pdf's to jpgs like TTRPDFToJPG3. If you have a specific idea of what kind of output you're looking for, please feel free to elaborate further. cheers!
If you're looking for a programmatic way to capture the screen, here it is:
using acApp = Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.Runtime;
using System.Drawing.Imaging;
using System.Drawing;
namespace ScreenshotTest
{
public class Commands
{
[CommandMethod("CSS")]
static public void CaptureScreenShot()
{
ScreenShotToFile(
acApp.Application.MainWindow,
"c:\\main-window.png",
0, 0, 0, 0
);
ScreenShotToFile(
acApp.Application.DocumentManager.MdiActiveDocument.Window,
"c:\\doc-window.png",
30, 26, 10, 10
);
}
private static void ScreenShotToFile(
Autodesk.AutoCAD.Windows.Window wd,
string filename,
int top, int bottom, int left, int right
)
{
Point pt = wd.Location;
Size sz = wd.Size;
pt.X += left;
pt.Y += top;
sz.Height -= top + bottom;
sz.Width -= left + right;
// Set the bitmap object to the size of the screen
Bitmap bmp =
new Bitmap(
sz.Width,
sz.Height,
PixelFormat.Format32bppArgb
);
using (bmp)
{
// Create a graphics object from the bitmap
using (Graphics gfx = Graphics.FromImage(bmp))
{
// Take a screenshot of our window
gfx.CopyFromScreen(
pt.X, pt.Y, 0,0, sz,
CopyPixelOperation.SourceCopy
);
// Save the screenshot to the specified location
bmp.Save(filename, ImageFormat.Png);
}
}
}
}
}
Source: Taking screenshots of AutoCAD’s main and drawing windows using .NET
Thanks to everyone. I am saving the files in pdf and after I´m using GIMP to convert them in PNG.

Getting the exact edit box dimensions for the given text

I need to be able to determine the size of the edit box according to the text I have, and a maximum width.
There are similar questions and answers, which suggest GetTextExtentPoint32 or DrawTextEx.
GetTextExtentPoint32 doesn't support multiline edit controls, so it doesn't fit.
DrawTextEx kind of works, but sometimes the edit box turns out to be larger than necessary, and, what's worse, sometimes it's too small.
Then there's EM_GETRECT and EM_GETMARGINS. I'm not sure whether I should use one of them, or maybe both.
What is the most accurate method for calculating the size? This stuff is more complicated then it should be... and I prefer not to resort to reading the source code of Wine or ReactOS.
Thanks.
Edit
Here's my code and a concrete example:
bool AutoSizeEditControl(CEdit window, LPCTSTR lpszString, int *pnWidth, int *pnHeight, int nMaxWidth = INT_MAX)
{
CFontHandle pEdtFont = window.GetFont();
if(!pEdtFont)
return false;
CClientDC oDC{ window };
CFontHandle pOldFont = oDC.SelectFont(pEdtFont);
CRect rc{ 0, 0, nMaxWidth, 0 };
oDC.DrawTextEx((LPTSTR)lpszString, -1, &rc, DT_CALCRECT | DT_EDITCONTROL | DT_WORDBREAK);
oDC.SelectFont(pOldFont);
::AdjustWindowRectEx(&rc, window.GetStyle(), (!(window.GetStyle() & WS_CHILD) && (window.GetMenu() != NULL)), window.GetExStyle());
UINT nLeftMargin, nRightMargin;
window.GetMargins(nLeftMargin, nRightMargin);
if(pnWidth)
*pnWidth = rc.Width() + nLeftMargin + nRightMargin;
if(pnHeight)
*pnHeight = rc.Height();
return true;
}
I call it with nMaxWidth = 143 and the following text (below), and get nHeight = 153, nWidth = 95. But the numbers are too small for the text to fit, on both axes.
The text (two lines):
Shopping
https://encrypted.google.com/search?q=winapi+resize+edit+control+to+text+size&source=lnms&tbm=shop&sa=X&ved=0ahUKEwiMyNaWxZjLAhUiLZoKHQcoDqUQ_AUICigE
Edit 2
I found out that the word wrapping algorithm of DrawTextEx and of the exit control are different. For example, the edit control wraps on ?, DrawTextEx doesn't. What can be done about it?

Pango + Cairo; is there an existing approach for handling <img> style tags in text?

Pango syntax supports some text only markup. As far as i can see this does not extend to embedding images as well.
Looking around I cannot find much in the way of an existing implementation, but i havent done pango+cairo work before so i might be missing the obvious community for it.
As far as i can tell a reasonable approach would be to just analyse a string, pull out any tags, create cairo images, and then modify the pango layout around them accordingly.
It also seems like something someone might have done before.
Im specifically looking for an answer on these questions:
Does pango+cairo already solve this and I have just misread the docs?
Has something like this been done before, and where is a reference?
Is this a reasonable approach, or should i try something else, and what?
(also note i am using ruby, so that may affect my options)
I've been through the source of the markup parser and it does not allow for "shape" attributes (the way Pango almost incorporates graphics) but it is possible to do it "by hand".
Since there is absolutely no example code on the Web, here's Pango/Cairo/Images 101.
For a simple demo, I created an 800x400 window, added a GtkDrawingArea and connected up the "draw" signal. Before entering the main program loop, I initialized it with the following code:
PangoLayout *Pango;
void init_drawingArea (GtkWidget *pWidget)
{
cairo_surface_t *pImg = cairo_image_surface_create_from_png ("linux.png");
PangoRectangle r = {0, 0, PANGO_SCALE * cairo_image_surface_get_width (pImg),
PANGO_SCALE * cairo_image_surface_get_height(pImg)};
PangoContext *ctxt = gtk_widget_get_pango_context (pWidget);
PangoAttrList *attList = pango_attr_list_new();
PangoAttribute *attr;
Pango = pango_layout_new (ctxt);
pango_cairo_context_set_shape_renderer (ctxt, render, NULL, NULL);
pango_layout_set_text (Pango, pszLorem, -1);
pango_layout_set_width(Pango, PANGO_SCALE * 800);
attr = pango_attr_shape_new_with_data(&r, &r, pImg, NULL, NULL);
attr->start_index = 0; attr->end_index = 1;
pango_attr_list_insert (attList, attr);
attr = pango_attr_shape_new_with_data(&r, &r, pImg, NULL, NULL);
attr->start_index = 152; attr->end_index = 153;
pango_attr_list_insert (attList, attr);
pango_layout_set_attributes (Pango, attList);
}
The context's shape renderer is set to render () and a PangoLayout is created and initialized. It then creates 2 shape attributes, sets the user data to a cairo surface which we populate from a png file and applies the attributes to characters 0 and 152 of the text.
The "draw" signal processing is straightforward.
gboolean onDraw (GtkWidget *pWidget, cairo_t *cr, gpointer user_data)
{
pango_cairo_show_layout (cr, Pango);
return 1;
}
and the render () PangoCairoShapeRenderFunc function is called as needed:
void render (cairo_t *cr, PangoAttrShape *pShape, gboolean do_path, gpointer data)
{
cairo_surface_t *img = (cairo_surface_t *)pShape->data;
double dx, dy;
cairo_get_current_point(cr, &dx, &dy);
cairo_set_source_surface(cr, img, dx, dy);
cairo_rectangle (cr, dx, dy, pShape->ink_rect.width/PANGO_SCALE,
pShape->ink_rect.height/PANGO_SCALE);
cairo_fill(cr);
}
Taking the current point from cairo, it draws a rectangle and fills it with the image.
And that's pretty much all it does. Images were added as an afterthought and it shows. They are subject to the same rules as any other glyph so they are limited to the equivalent of CSS's display: inline.
I've put the code up at http://immortalsofar.com/PangoDemo/ if anyone wants to play with it. Me, I arrived here trying to get around GtkTextBuffer's limitations. Guess I'll just have to go deeper.

How to manipulate DLGTEMPLATE programmatically?

What?
I have a DLGTEMPLATE loaded from a resource DLL, how can I change the strings assigned to the controls at runtime programmatically?
I want to be able to do this before the dialog is created, such that I can tell that the strings on display came from the resource DLL, and not from calls to SetWindowText when the dialog is initialized.
Google has found examples of creating DLGTEMPLATE in code, or twiddling simple style bits but nothing on editing the strings in memory.
How?
I am doing this by hooking the Dialog/Property Sheet creation API's. Which gives me access to the DLGTEMPLATE before the actual dialog is created and before it has a HWND.
Why?
I want to be able to do runtime localization, and localization testing. I already have this implemented for loading string (including the MFC 7.0 wrapper), menus and accelerator tables, but I am struggling to handle dialog/property sheet creation.
Code examples would be the perfect answer, ideally a class to wrap around the DLGTEMPLATE, if I work out my own solution I will post it.
You can't edit the strings in memory. The DLGTEMPLATE structure is a direct file mapping of the relevent bytes of the resource dll. Thats read only.
You are going to need to process the entire DLGTEMPLATE structure and write out a new one with the altered length strings.
It will frankly be easier to just hook the WM_INITDIALOG and alter the strings by interacting with the controls than building a DLGTEMPLATE writer. Because there arn't a lot of those around. Unless you have an additional requirement to actually save altered dialog resources to disk as raw .res files (or attempt to modify the .dll inplace) Id really recommend you avoid this approach.
You say you are already doing this for accellerator tables and menu strings - if you can guarantee that the patched in strings are going to be shorter, then just make a binary copy of the DLGTEMPLATE struct, and write the non trivial scanning code necessary to find each string so you can patch the copy in place.
There is a file out there somewhere (which I think originated at Microsoft but I am not completely sure) called RESFMT.ZIP which explains this with some code examples. Raymond Chen also does some excellent explanations of this on his blog. Note that the format of DIALOGEX and DIALOG controls are different.
As noted in some other answers you would need to create the structure again from the start. This isn't all bad as you already have the basic information. Adding the controls is where is gets hard.
Basically, allocate a largish block of memory into a WORD *lpIn. Then add the structure up on top of that. adding the basic information for the DIALOG (see DLGTEMPLATE) and the controls is pretty obvious as the information is there in MSDN.
The two biggest problems you will encounter are: Making sure that the various part start on an alignment boundary, and interpreting the values of DIALOG controls, especially when to add a just a string or, a string or ordinal. Each control needs to start on an even boundary.
For the first (borrowed from somewhere I think RESFMT.ZIP):
WORD *AlignDwordPtr (WORD *lpIn)
{
ULONG ul;
ul = (ULONG) lpIn;
ul +=3;
ul >>=2;
ul
What I did was build a series of functions like this one following that allowed me to assemble DIALOGS in memory. (My need was so I could have some common code that didn't need an associated RC file for some very basic messages).
Here is an example...
WORD *AddStringOrOrdinalToWordMem( WORD *lpw, char *sz_Or_Ord )
{
LPWSTR lpwsz;
int BufferSize;
if (sz_Or_Ord == NULL)
{
*lpw++ = 0;
}
else
{
if (HIWORD(sz_Or_Ord) == 0) //MAKEINTRESOURCE macro
{
*lpw++ = 0xFFFF;
*lpw++ = LOWORD(sz_Or_Ord);
}
else
{
if (strlen(sz_Or_Ord))
{
lpwsz = ( LPWSTR ) lpw;
BufferSize = MultiByteToWideChar( CP_ACP, 0, sz_Or_Ord, -1, lpwsz, 0 );
MultiByteToWideChar( CP_ACP, 0, sz_Or_Ord, -1, lpwsz, BufferSize );
lpw = lpw + BufferSize;
}
else
{
*lpw++ = 0;
}
}
}
return( lpw );
}
The header file to the complete module included these functions:
WORD *AddControlToDialogTemplateEx(MTDialogTemplateType *dlgtmp,
char *Title,
WORD Id,
char *WinClass,
DWORD Style,
short x,
short y,
short cx,
short cy,
DWORD ExStyle,
int HelpID);
int DestroyDlgTemplateEx(MTDialogTemplateType *dlgtmp);
MTDialogTemplateType *CreateDlgTemplateEx( char *Name, // We use name just for reference, so it can be NULL
short x,
short y,
short cx,
short cy,
DWORD ExtendedStyle,
DWORD Style,
char *Menu,
char *WinClass,
char *Caption,
char *FontTypeFace,
int FontSize,
int FontWeigth,
int FontItalic,
int Charset,
int HelpID,
int NumberOfControls);
Which allowed me to assemble whole dialogs easily from code.
See the API function ::EnumChildWindows( HWND, WNDENUMPROC, LPARAM )
You can call this in a CFormView::Create or CDialog::OnInitDialog to give yourself a chance to replace the control captions. Don't worry, the old strings don't flicker up before you replace them.
In your dialog resource, set the control captions to a key in some kind of dictionary. If you're compiling /clr you can use a managed string table resource. In your callback, look up the translated string in your dictionary and set the control's caption to the translation. Another benefit of /clr and managed string table is that you can automatically look up the right language by Windows (or you) having already set System::Threading::Thread::CurrentThread->CurrentUICulture.
Something like this
CMyDialog::OnInitDialog()
{
::EnumChildWindows(
this->GetSafeHwnd(),
CMyDialog::UpdateControlText,
(LPARAM)this )
}
BOOL CALLBACK CMyDialog::UpdateControlText( HWND hWnd, LPARAM lParam )
{
CMyDialog* pDialog = (CMyDialog*)lParam;
CWnd* pChildWnd = CWnd::FromHandle( hWnd );
int ctrlId = pChildWnd->GetDlgCtrlID();
if (ctrlId)
{
CString curWindowText;
pChildWnd->GetWindowText( curWindowText );
if (!curWindowText.IsEmpty())
{
CString newWindowText = // some look up
pChildWnd->SetWindowText( newWindowText );
}
}
}
You'll have to locate the string you want to modify in the mem buffer that represents the template. The only way to do that is to traverse the whole template. Which is not easy.
Once you've done that, either insert bytes in the buffer if your new string is longer than the original one. Or shrink the buffer if the new string is shorter.
As Chris wrote, it would be much easier to modify the text in WM_INITDIALOG and try to re-phrase you requirement that says you may not call SetWindowText().
Thanks all, I actually had 24 hours rest on the problem, then went with a global windows hook filtering WM_INITDIALOG which was a much simpler method, worked out just fine, no API hooking required, 2 pages of code down to just a few lines.
Thanks for all the answers.

Resources