Determining text width & truncating

G

Gary Hillerson

I need to truncate a text string if it does not fit within a certain
width, and i can't use tables in this situation, so i'm using tabs.

I know the width of the tab stop. I can't find how to determine the
width of a text string in Word VBA (2010/2007). There's a TextWidth
function in Access, but apparently not in Word.

Can anyone point me at a way to compute the text width?

And given that, I assume that the way to truncate the text is to
iteratively lop characters off the end until its width fits into the
desired amount of space? Is there a better way?


Thanks in advance for any help,
gary
 
G

Graham Mayor

You may not be able to use a table. but can you use a borderless frame? This
can be positioned where you want it and if you set the sizes to fixed,
anything you type into it will be truncated to fill the available space.

If not tell us *exactly* what you are doing and it may be possible to come
up with something less complicated than you have in mind.

--
<>>< ><<> ><<> <>>< ><<> <>>< <>><<>
Graham Mayor - Word MVP


<>>< ><<> ><<> <>>< ><<> <>>< <>><<>
 
T

That Guy

I agree that if we knew exactly what you were doing that we could
possibly help you.

There is no easy way to hook into MS words 'key press' or 'story
change' events (if that is even what they are called) because they are
not exposed.

Form fields could be an option. I have used these extensively when
creating MS word fill-able forms. If you were to use a form field you
could set a character limit. If you are talking a larger body of text
then I would suggest creating a frame as Graham has suggested and sets
its bounds where you want the text to fall.

If you are looking for a way to syntactically track changes in an area
of a document or you are trying to eliminate any word typed that is
less than 4 letters or something like that, then you can only
accomplish this through API calls (Subclassing), or by having a button
that is pressed to check the document after each section of text is
entered; at which time you can use the 'Click' event to check the
contents of the document. The last option is to create a massive
amount of overhead and implement a timer that would check the content
of the document at a specific interval.

Any way you do this it is going to be difficult if you are trying to
do something with code.
 
G

Gary Hillerson

Here's what I'm doing: I'm working on a project for a friend whose
company produces a piece of equipment used in television stations that
includes its own linear-format internal database. He wanted me to
create a Word document that shows all of the database. The database is
organized into a collection of satellites, each of which has a
collection of transponders, each of which has a collection of
settings.

So I iterate through the database and generate a Word document. It is
a large database, and the resulting document is between 125 and 200
pages long.

I originally created a table for each satellite, and a row for each
transponder, with every setting a data cell in the row. That worked
great, except that it essentially brought Word and my very high-speed
desktop to its knees, and would not run on my friend's laptop without
him shutting everything else dow: Word does not do well handling long
documents with hundreds of tables (each 1/2 to 2 pages long). Even
opening the completed document put a serious hurt on my machine.

Putting everything into table cells meant that a setting too long to
display in a cell would automatically wrap, so there were no issues
with appearance.

To make the program usable, i got rid of tables and instead set up tab
settings and background colors, so the end result looks like a table,
but doesn't actually use tables. The program now runs on my computer
in 10 seconds instead of 15 minutes, and the output it produces looks
great. The only problem is that when a data value string is exceeds
the tab "boundary", it flows over the boundary and skews the remainder
of that paragraph, ruining the alignment, which is not acceptable.

This doesn't happen in many places, because we've tweaked the tab and
font settings to make almost everything fit, but it does happen in
places. We would be fine with the text getting truncated instead of
overflowing the tab boundary.

So my plan was to "measure" the width of each data value against the
width of the current tab space it needs to fit into, and truncate the
text if it won't fit. Unfortunately, i can't figure out how to make
that determination, and I can't afford for it to be too expensive of a
computation.

Sure seems like if I know the font information, know the text string,
and know the amount of space I have available, that I should be able
to figure out how to truncate the text to fit in that space. But this
is apparently a non-trivial task in Word.

We're using Word 2010/2007. Any suggestions appreciated.

Thanks in advance,
Gary
 
G

Gary Hillerson

I figured out one way to do it, though it definitely slows the program
down considerably.

I create a global text box at init time, and while running, assign the
text i want to display in it, have it auto-resize, and then get its
width. If that width is too long, i can truncate (not yet doing that
-- just detecting at this point). I am also encountering a weird text
box issue.

The weird issue: in the GetTextWidth function, it seems that I have to
force the textbox to update its width computation somehow after i
assign the text to it. I tried resetting the height, and that seems to
work, but i don't think i'm doing the right thing here. Without that,
i don't get back the correct width value.

Any advice on that point?

Here's the code:

------------------------------------------------------------------

Public gTextBox As Shape 'global

Public Sub CreateTextBox(ByVal fontName As String, _
ByVal fontSize As Single)
Set gTextBox =
ActiveDocument.Shapes.AddTextbox(msoTextOrientationHorizontal, 0, 0,
1, 1)
With gTextBox
.WrapFormat.Type = wdWrapNone
With .TextFrame
.AutoSize = msoAutoSizeShapeToFitText
.WordWrap = False
.MarginBottom = 0
.MarginLeft = 0
.MarginRight = 0
.MarginTop = 0
.TextRange.Font.Name = fontName
.TextRange.Font.Size = fontSize
End With
End With
End Sub


Private Function GetTextWidth(ByVal s As String) As Long
With gTextBox
.TextFrame.TextRange.Text = s
.Height = 20 'force it to recalculate width
TestTextWidth = .Width
End With
End Function


Public Sub DoTest()
Dim s As String

Call CreateTextBox("Arial", 9)
Do While 1 = 1
s = InputBox("Enter text string ")
If (s = "") Then Exit Do
MsgBox "Width of '" & s & "' IS: " & CStr(GetTextWidth(s))
Loop
gTextBox.Delete
End Sub
 
G

Graham Mayor

What you appear to be doing is re-inventing mail merge. You might find it
faster and easier to set up simply to merge into a table This should impose
less of a load on the hardware than building tables on the fly.

See if http://www.gmayor.com/ManyToOne.htm points a way.

Fellow MVP 'macropod' has prepared a tutorial employing a different method
based on the Microsoft support link (below), with working field codes and a
sample Excel data source which you can also download from my website
http://www.gmayor.com/Zips/Catalogue Mailmerge.zip

--
<>>< ><<> ><<> <>>< ><<> <>>< <>><<>
Graham Mayor - Word MVP


<>>< ><<> ><<> <>>< ><<> <>>< <>><<>
 
G

Gary Hillerson

I clearly did not explain very well, as what i'm trying to do has no
resemblance to what Mail Merge does.

What i need is a way to compute the width in points of a piece of text
in the current paragraph style's font.

G
 
T

That Guy

I clearly did not explain very well, as what i'm trying to do has no
resemblance to what Mail Merge does.

What i need is a way to compute the width in points of a piece of text
in the current paragraph style's font.

G

I am not sure the width in points is going to be your best option.

If you know how many characters fit in a specified area that you are
trying not to exceed then you can test each 'Range' as it comes in.

Word Range objects have the 'Range.Characters.Count' property which
can tell you how many characters are in a specified range. If you know
your breaking or wrapping point in characters then you can test the
character count when you are bringing the range in to be placed in the
document. This should be done in the import routine, not in word.

That being said I would suggest, for human readability and for ease of
use, that you abandon trying to make this document in word and instead
create an xml/xhtml or simply Html solution. This would make it far
easier to load and view and if you went the route of xml/xhtml then
you can easily search and show. Also I am not sure why anyone would
need to view 150-200 pages of settings unless this is simply meant to
be an intermediary place for the data to live before it is used in a
different process.

If you are in love with the idea of using Word I suggest exploring the
character counting method for finding your break or wrap point. If you
are open to a different way to store the data email me and I will be
happy to explore other options with you. I have done something similar
in the past for a set of device profiles for industrial electrical
motor starters.

best of luck.
 
G

Gary Hillerson

Well, thanks for the suggestion, but it's not my option to pick the
desired solution -- i've just been hired to implement it. It works
beautifully except for this problem with the occasional text string
being too wide to fit in the tab width.

Though Range.Characters.Count would work if i knew the
wrapping/truncating point, that's the very problem i'm trying solve --
where to truncate the text to fit inside a specified width. So i don't
think that'll work.

Using a textbox with no-wrap and autosize properties set up does the
trick -- it tells me the width of the text in points; however, it
takes so darn long that it makes the program too slow. Next attempt
is to use the windows API DrawText and see if i can make that work
without the slowdown.

Thanks for your efforts and tips.

g
 
G

Gary Hillerson

I've attached a module that accomplishes what I want and does it very
quickly. I found some code snippets in various places on line, cobbled
them together with some of my own, and now have a GetTextWidth
function and FitTextToWidth function that work just fine.

The trick was to use Windows API calls, particularly the
GetTextExtentPoint function, which required setting up the default
font characteristics using the CreateFont API.

Take a look
 
T

Tony Jollans

You shouldn't need an API for this - the Range.Information(wdHorizontalPositionRelativeToPage) will give you the position of text in points from the left of the page. You can calculate the width from that if the text is all on one line, but as, I think, you really want the width to calculate the end point that seems the wrong way round.
 
D

Diana Saftler

Gary;
I have exactly the same requirement:
I need to know whether the string in the first 'column' is going to extend past the next tab.

I didn't see your solution in the conversation.
Could you please let me know how you resolved this?

Thanks!

Diana
 
S

Stefan Blom

It's unclear if the original issue was ever resolved. I recommend that you
post your question in a new thread. Try to explain as clearly as you can
what you are trying to accomplish and what you have tried so far.

-- 
Stefan Blom
Microsoft Word MVP




---------------------------------------------
"Diana Saftler" wrote in message
Gary;
I have exactly the same requirement:
I need to know whether the string in the first 'column' is going to extend
past the next tab.

I didn't see your solution in the conversation.
Could you please let me know how you resolved this?

Thanks!

Diana
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top