Discussion:
Hot to rorate text using QuickTime API
(too old to reply)
Neel
2006-05-18 13:09:09 UTC
Permalink
Hi,
One of the module of my project of Landscap Report needs, where text is
shown rotated by 90 degrees/ exactly vertically. I don´t know, how to
rotate this text. I have seen past threads related to the same, also
found CCapionPlus class has been written to resolve the same issue,but
could not locate the same. Can anyone tell me which API should i use to
put /draw text vertically. Can i use MacDrawText(...) ?

Can anyone help me to solve.

This is the code which draw text horizontally.

// Prints the Text at the specified position
virtual void DrawString(int f_nX, int f_nY, char *f_cpStr)
{
::RGBForeColor(&m_sRGB_Text);
MoveTo(f_nX, f_nY + 12);
::DrawText((void *) f_cpStr, 0, strlen(f_cpStr));

if ( m_bLandScapeMode )
{
// Here want to draw text vertically / rotate to 90 degree.
}
}
//////////////////////////////////////////////////////////////////
David Phillip Oster
2006-05-18 14:35:01 UTC
Permalink
Post by Neel
Hi,
One of the module of my project of Landscap Report needs, where text is
shown rotated by 90 degrees/ exactly vertically. I donŽt know, how to
rotate this text. I have seen past threads related to the same, also
found CCapionPlus class has been written to resolve the same issue,but
could not locate the same. Can anyone tell me which API should i use to
put /draw text vertically. Can i use MacDrawText(...) ?
Can anyone help me to solve.
This is the code which draw text horizontally.
// Prints the Text at the specified position
virtual void DrawString(int f nX, int f nY, char *f cpStr)
{
::RGBForeColor(&m sRGB Text);
MoveTo(f nX, f nY + 12);
::DrawText((void *) f cpStr, 0, strlen(f cpStr));
if ( m bLandScapeMode )
{
// Here want to draw text vertically / rotate to 90 degree.
}
}
I'd privately sent you CCaptionPlus, but it doesn't include support for
high quality printing. _That_ was previously discussed in this newsgroup:

From: "Ilja A. Iwas" <***@informatik.hu-berlin.de.removethis>
Subject: Re: printing problem
Date: Mon, 14 Feb 2000 13:51:56 +0100

...
In article <davep-***@10.0.0.2>, Dave Polaschek
<***@polaschek-computing.com> wrote:
....
Post by Neel
Thanks to Daves help I was able to put together a pane that prints
rotated text (90 or -90 degrees rotated) on screen and on postscript
printers. I wasn't able to see what happens on QuickDraw printers,
since I don't have access to one.
you can do a search using http://groups.google.com to find it.

A more modern way to do this would be to use code similiar to Apple's
CircleView.m from their CircleView sample code, condensed here:

NSGraphicsContext *context = [NSGraphicsContext currentContext];
NSRect lineFragmentRect = [layoutManager
lineFragmentRectForGlyphAtIndex:glyphIndex effectiveRange:NULL];

layoutLocation = [layoutManager locationForGlyphAtIndex:glyphIndex];

NSAffineTransform *transform = [NSAffineTransform transform];

[transform translateXBy:viewLocation.x yBy:viewLocation.y];
[transform rotateByRadians:-angle];

[context saveGraphicsState];
[transform concat];

// drawGlyphsForGlyphRange: draws the glyph at its laid-out location in
container coordinates.
// Since we are using the transform to place the glyph, we subtract the
laid-out location here.
[layoutManager drawGlyphsForGlyphRange:NSMakeRange(glyphIndex, 1)
atPoint:NSMakePoint(-layoutLocation.x, -layoutLocation.y)];
[context restoreGraphicsState];


Naturally, this omits critical details. See the original sample code, on
Apple's web site.

And, the above would involve re-writing your program to use Objective-C,
at least in part.

The idea of using a transformation matrix and letting the text system
rotate the text should also work with QuicktimeGraphicImporters.
david ralley
2006-05-18 14:39:28 UTC
Permalink
I wouldn't do this using QuickTime, but with MLTE using a call
to TXNDrawCFStringTextBox().

myOptions.optionTags = kTXNRotateTextMask;
myOptions.rotation = FixRatio(90, 1); // Positive = counterclockwise
myOptions.options = nil;
s = TXNDrawCFStringTextBox(CFSTR("Hello world!"), &bounds,
nil, &myOptions);

This won't help if you need the text to be editable, or if you
need multiple styles within the text. For that you'll need to dig
into ATSUI.

david
Neel
2006-05-19 09:26:50 UTC
Permalink
Hi,

I am using QuickDrawText.h. Is there any api available or simple
parameter where we can set veritical rotation say 90 and rotate the
text.

Also if possible can someone provide snippet of code using
"QuicktimeGraphicImporters"
which takes a simple text string and rotate to 90.

Thanks,
Neel
2006-05-19 10:34:01 UTC
Permalink
Can we have simple API which does the same thing ?
David Phillip Oster
2006-05-20 03:43:13 UTC
Permalink
Post by Neel
Can we have simple API which does the same thing ?
A quick check of http://groups.google.com/ shows I answered this same
question in June of 2003. I've revised the code, because, in modern OS
X, PicHandles are no longer handles (You can't call SetHandleSize() on
them).

Here is the revised code:

// CText90View.h

#ifndef _H_CText90View
#define _H_CText90View
#pragma once

#include <LTextEditView.h>

class StScaledUpGWorld{
GWorldPtr mSaveGWorld;
GDHandle mSaveGD;
GWorldPtr mGW;
SInt32 mScaleFactor;
public:
StScaledUpGWorld(){ mGW = NULL; mScaleFactor = 4; }
StScaledUpGWorld(GraphicsImportComponent inGIC, long
scaleFactor = 1);
StScaledUpGWorld(PicHandle ph, long scaleFactor = 1);
~StScaledUpGWorld();
operator GWorldPtr () { return mGW; }
GWorldPtr Release();
void Rotate90();
void Adopt(GWorldPtr inGW, GWorldPtr saveGW, GDHandle saveGD);
void Draw(GWorldPtr gw = NULL, GDHandle gd = NULL);
private:
void Init(GraphicsImportComponent inGIC, long scaleFactor);
void Init(PicHandle ph, long scaleFactor);
void Die();
};


class StPicHandle{
PicHandle mPH;
public:
StPicHandle(PicHandle inPH);
~StPicHandle();
operator PicHandle(){ return mPH; }
PicHandle Release();
void Adopt(PicHandle inPH);
};


class CText90View : public LTextEditView {
public:
enum { class_ID = FOUR_CHAR_CODE('TxtV') };

CText90View( LStream* inStream );

virtual ~CText90View();

virtual void UserChangedText();
virtual void DrawSelf();
virtual void PrintPanelSelf(const PanelSpec& inPanel);

Boolean IsDirty() const
{
return mIsDirty;
}

void SetDirty(
Boolean inDirty)
{
mIsDirty = inDirty;
}

protected:
bool mIsDirty;

private:
CText90View();
CText90View(const CText90View& inOriginal);
CText90View& operator=(const CText90View& inRhs);
PicHandle GetAsPict(const Rect& r); // caller must KillPicture
StPicHandle mPh;
StScaledUpGWorld mGW;
};

void RotateAboutCenter(GraphicsImportComponent gic, SInt32 degrees);
void ScaleAboutTopLeft(GraphicsImportComponent gic, SInt32 scaleFactor);
PicHandle ScaleAboutTopLeft(PicHandle inPH, SInt32 scaleFactor);
PicHandle CopyPicture(PicHandle inPH);

#endif // _H_CText90View

// CText90View.cp -draws text rotated by 90 degrees.
// for printing, it renders to an offscreen GWorld
#include "CText90View.h"
#include <LStream.h>
#include <cstring>
#include <ImageCompression.h>
#include <QuicktimeComponents.h>


// make a GWorld initialized from a GraphicsImportComponent. Dispose
when goes out of scope.
StScaledUpGWorld::StScaledUpGWorld(GraphicsImportComponent inGIC, long
scaleFactor){
try{
Init(inGIC, scaleFactor);
}catch(...){
Die();
throw;
}
}

// make a GWorld initialized from a PicHandle. Dispose when goes out of
scope.
StScaledUpGWorld::StScaledUpGWorld(PicHandle ph, long scaleFactor){
try{
Init(ph, scaleFactor);
}catch(...){
Die();
throw;
}
}


void StScaledUpGWorld::Init(GraphicsImportComponent inGIC, long
scaleFactor){
GetGWorld(&mSaveGWorld, &mSaveGD);
mGW = NULL;
mScaleFactor = scaleFactor;
Rect r;
ThrowIfOSStatus_(GraphicsImportGetBoundsRect(inGIC, &r));
try{
StDisableDebugThrow_();
ThrowIfOSStatus_(NewGWorld(&mGW, 32, &r, NULL, NULL,
pixelsLocked));
}catch(...){
ThrowIfOSStatus_(NewGWorld(&mGW, 32, &r, NULL, NULL,
useTempMem|pixelsLocked));
}
LockPixels(GetGWorldPixMap(mGW));
GWorldPtr savePort;
GDHandle saveDev;
GetGWorld(&savePort, &saveDev);
SetGWorld(mGW, NULL);
EraseRect(&r);
SetGWorld(savePort, saveDev);
ThrowIfOSStatus_(GraphicsImportSetGWorld(inGIC, mGW, NULL));
ThrowIfOSStatus_(GraphicsImportDraw(inGIC));
SetGWorld(savePort, saveDev);
}

void StScaledUpGWorld::Init(PicHandle ph, long scaleFactor){
GetGWorld(&mSaveGWorld, &mSaveGD);
mGW = NULL;
mScaleFactor = scaleFactor;
Rect r;
r = (**ph).picFrame;
try{
StDisableDebugThrow_();
ThrowIfOSStatus_(NewGWorld(&mGW, 32, &r, NULL, NULL,
pixelsLocked));
}catch(...){
ThrowIfOSStatus_(NewGWorld(&mGW, 32, &r, NULL, NULL,
useTempMem|pixelsLocked));
}
LockPixels(GetGWorldPixMap(mGW));
GWorldPtr savePort;
GDHandle saveDev;
GetGWorld(&savePort, &saveDev);
SetGWorld(mGW, NULL);
EraseRect(&r);
StHandleLocker lock((Handle) ph);
DrawPicture(ph, &r);
SetGWorld(savePort, saveDev);
}


StScaledUpGWorld::~StScaledUpGWorld(){
Die();
}

void StScaledUpGWorld::Die(){
if(NULL != mGW){
SetGWorld(mSaveGWorld, mSaveGD);
DisposeGWorld(mGW);
}
}

void StScaledUpGWorld::Draw(GWorldPtr inDestGW, GDHandle gd){
if(NULL != mGW){
if(NULL == inDestGW){ inDestGW = mSaveGWorld; }
if(NULL == gd){ gd = mSaveGD; }
SetGWorld(inDestGW, gd);
Rect bounds;
Rect r;
GetPortBounds(mGW, &bounds);
r = bounds;
r.right = r.left + (r.right - r.left)/mScaleFactor;
r.bottom = r.top + (r.bottom - r.top)/mScaleFactor;

StColorState saveColors;
StColorState::Normalize();
CopyBits(GetPortBitMapForCopyBits(mGW),
GetPortBitMapForCopyBits(inDestGW),
&bounds, &r, ditherCopy, NULL);
}
}

void StScaledUpGWorld::Adopt(GWorldPtr inGW, GWorldPtr saveGW, GDHandle
saveGD){
if(inGW != mGW){
if(NULL != mGW){
DisposeGWorld(mGW);
}
mGW = inGW;
mSaveGWorld = saveGW;
mSaveGD = saveGD;
}
}

GWorldPtr StScaledUpGWorld::Release(){
GWorldPtr val = mGW;
mGW = NULL;
return val;
}


namespace {
/* ScanLineTranspose - p is a horizontal row of pixels. copy them, down,
into destPH at x coordinate x.
n is the number of pixels.
*/
void ScanLineTranspose(Ptr px, SInt32 n, PixMapHandle destPH, SInt32 x){
unsigned char *p, *pEnd, *dest;
SInt32 rowBytes;

p = reinterpret_cast<unsigned char*>(px);
dest = reinterpret_cast<unsigned char *>( GetPixBaseAddr(destPH));
rowBytes = GetPixRowBytes(destPH);
switch((**destPH).pixelSize){
case 32:
pEnd = p + n*4;
dest += x*4;
for(; p < pEnd;p += 4, dest += rowBytes){
* (SInt32*) dest = * (SInt32*) p;
}
break;
default:
Throw_(paramErr);
break;
}
}

}


/* Rotate90 - 90 degrees clockwise.
*/
void StScaledUpGWorld::Rotate90(){
PixMapHandle srcPH;
GWorldPtr destGW;
PixMapHandle destPH;
Rect r;
SInt32 x, y, xMax, yMax, rowBytes;
Ptr yp;

destGW = NULL;
srcPH = GetGWorldPixMap(mGW); // experiments show: when mem is tight,
can return NULL!
if(NULL == srcPH){
return;
}
if( ! LockPixels(srcPH)){
return;
}
r.left = (**srcPH).bounds.top;
r.top = (**srcPH).bounds.left;
r.right = (**srcPH).bounds.bottom;
r.bottom = (**srcPH).bounds.right;
if(noErr == NewGWorld(&destGW, (**srcPH).pixelSize, &r,
(**srcPH).pmTable, NULL, 0) ||
noErr == NewGWorld(&destGW,(**srcPH).pixelSize, &r,
(**srcPH).pmTable, NULL, useTempMem)){

destPH = GetGWorldPixMap(destGW); // experiments show: when mem
is tight, can return NULL!
if(NULL == destPH){
UnlockPixels(srcPH);
DisposeGWorld(destGW);
return;
}
if( ! LockPixels(destPH)){
UnlockPixels(srcPH);
DisposeGWorld(destGW);
return;
}
yp = GetPixBaseAddr(srcPH) ;
rowBytes = GetPixRowBytes(srcPH); // mask off flag bits.
xMax = ((**srcPH).bounds.right - (**srcPH).bounds.left);
yMax = ((**srcPH).bounds.bottom - (**srcPH).bounds.top);
for(x = yMax-1, y = 0; y < yMax; y++, x--, yp += rowBytes){
ScanLineTranspose(yp, xMax, destPH, x);
}
UnlockPixels(destPH);
}
UnlockPixels(srcPH);
Adopt(destGW, mSaveGWorld, mSaveGD);
}

// make a GraphicsImportComponent initialized from a picFileInMem.
Dispose when goes out of scope.
class StGraphicsImportComponent{
GraphicsImportComponent mGIC;
public:
explicit StGraphicsImportComponent(const FSSpec& inFS);
explicit StGraphicsImportComponent(Handle picFileInMem);
explicit StGraphicsImportComponent(GraphicsImportComponent
inGic){ mGIC = inGic;}
~StGraphicsImportComponent();
GraphicsImportComponent Release();
operator GraphicsImportComponent(){ return mGIC; }
private:
void Init(Handle picFileInMem);
void Die();
};

StGraphicsImportComponent::StGraphicsImportComponent(const FSSpec& inFS)
{
ThrowIfOSStatus_(GetGraphicsImporterForFile(&inFS, &mGIC));
}

StGraphicsImportComponent::StGraphicsImportComponent(Handle
picFileInMem){
try{
Init(picFileInMem);
}catch(...){
Die();
}
}


StGraphicsImportComponent::~StGraphicsImportComponent()
{
Die();
}

void StGraphicsImportComponent::Init(Handle picFileInMem){
mGIC = NULL;
ThrowIfOSStatus_(OpenADefaultComponent(GraphicsImporterComponentType,
kQTFileTypePicture, &mGIC));
ThrowIfOSStatus_(GraphicsImportSetDataHandle(mGIC, picFileInMem));
}

void StGraphicsImportComponent::Die(){
if(NULL != mGIC){
CloseComponent(mGIC);
}
}

GraphicsImportComponent StGraphicsImportComponent::Release(){
GraphicsImportComponent val = mGIC;
mGIC = NULL;
return val;
}



StPicHandle::StPicHandle(PicHandle inPH) : mPH(inPH){
}

StPicHandle::~StPicHandle(){
if(NULL != mPH){
KillPicture(mPH);
}
}

PicHandle StPicHandle::Release(){
PicHandle val = mPH;
mPH = NULL;
return val;
}

void StPicHandle::Adopt(PicHandle inPH){
if(inPH != mPH){
if(NULL != mPH){
KillPicture(mPH);
}
mPH = inPH;
}
}


class StPicFileFromHandle {
StHandleBlock mH;
public:
StPicFileFromHandle(PicHandle ph);
operator Handle(){ return mH; }
};

StPicFileFromHandle::StPicFileFromHandle(PicHandle ph) :
mH(512 + GetHandleSize(reinterpret_cast<Handle>(ph))){
std::memset(*mH, 0, 512);
BlockMoveData(*ph, *mH + 512,
GetHandleSize(reinterpret_cast<Handle>(ph)));
}

// € CText90View - LStream constructor
CText90View::CText90View(LStream* inStream) : LTextEditView(inStream),
mPh(NULL){
mIsDirty = false;
}


// € ~CText90View Destructor
CText90View::~CText90View(){
}


// € UserChangedText
void CText90View::UserChangedText(){
if(not IsDirty()){
SetUpdateCommandStatus(true);
SetDirty(true);
}
}

void CText90View::PrintPanelSelf(const PanelSpec& /*inPanel*/){
GWorldPtr saveGW;
GDHandle saveGD;
GetGWorld(&saveGW, &saveGD);
mGW.Draw(GetMacPort(), saveGD);
}


void CText90View::DrawSelf(){
Rect r;
CalcLocalFrameRect(r);
StPicHandle ph(GetAsPict(r));
StPicFileFromHandle picFile(ph);
StGraphicsImportComponent gic(picFile);
RotateAboutCenter(gic, 90);
ThrowIfOSStatus_(GraphicsImportDraw(gic));

if(NULL == mGW){
GWorldPtr saveGW;
GDHandle saveGD;
Rect r;
CalcLocalFrameRect(r);
StPicHandle ph(GetAsPict(r));
StPicHandle ph2(ScaleAboutTopLeft(ph, 4));
StScaledUpGWorld gw(ph2, 4);
gw.Rotate90();
GetGWorld(&saveGW, &saveGD);
mGW.Adopt(gw.Release(), saveGW, saveGD);
}
}

void RotateAboutCenter(GraphicsImportComponent gic, SInt32 degrees){
Rect r;
MatrixRecord mat;
ThrowIfOSStatus_(GraphicsImportGetMatrix(gic, &mat));
ThrowIfOSStatus_(GraphicsImportGetBoundsRect(gic, &r));
RotateMatrix(&mat, degrees << 16,
static_cast<long>(r.right - r.left) << 15,
static_cast<long>(r.bottom - r.top) << 15);
GraphicsImportSetMatrix(gic, &mat);
}

void ScaleAboutTopLeft(GraphicsImportComponent gic, SInt32 scaleFactor){
Rect r;
MatrixRecord mat;
ThrowIfOSStatus_(GraphicsImportGetMatrix(gic, &mat));
ThrowIfOSStatus_(GraphicsImportGetBoundsRect(gic, &r));
ScaleMatrix(&mat, scaleFactor << 16, scaleFactor << 16,
static_cast<long>(r.left) << 16,
static_cast<long>(r.top) << 16);
GraphicsImportSetMatrix(gic, &mat);
}

// caller must dispose.
PicHandle ScaleAboutTopLeft(PicHandle inPH, SInt32 scaleFactor){
Rect r;
OpenCPicParams picParams;
r = (**inPH).picFrame;
r.right = r.left + (r.right - r.left)*scaleFactor;
r.bottom = r.top + (r.bottom - r.top)*scaleFactor;

picParams.srcRect = r;
picParams.hRes = 0x00480000 * 1;
picParams.vRes = picParams.hRes;
picParams.version = 2;
StPicHandle ph(OpenCPicture(&picParams));
ClipRect(&r);
StHandleLocker lock((Handle) inPH);
DrawPicture(inPH, &r);
ClosePicture();
return ph.Release();
}


namespace {

Handle CopyHandle(Handle inH){
ThrowIfOSErr_(HandToHand(&inH));
return inH;
}


}

PicHandle CopyPicture(PicHandle inPH){
return (PicHandle) CopyHandle((Handle) inPH);
}


PicHandle CText90View::GetAsPict(const Rect& r){
if(NULL != (PicHandle) mPh){
return CopyPicture(mPh);
}
OpenCPicParams picParams;
picParams.srcRect = r;
picParams.hRes = 0x00480000;
picParams.vRes = picParams.hRes;
picParams.version = 2;
// put it in a picture
StPicHandle ph(OpenCPicture(&picParams));
LTextEditView::DrawSelf();
ClosePicture();
mPh.Adopt(CopyPicture(ph));
return ph.Release();
}

David Phillip Oster
2006-05-20 02:46:20 UTC
Permalink
In article
Post by david ralley
I wouldn't do this using QuickTime, but with MLTE using a call
to TXNDrawCFStringTextBox().
myOptions.optionTags = kTXNRotateTextMask;
myOptions.rotation = FixRatio(90, 1); // Positive = counterclockwise
myOptions.options = nil;
s = TXNDrawCFStringTextBox(CFSTR("Hello world!"), &bounds,
nil, &myOptions);
This won't help if you need the text to be editable, or if you
need multiple styles within the text. For that you'll need to dig
into ATSUI.
That looks like the right answer but it isn't because of the insanely
limited API to TXNDrawCFStringTextBox(). You _can_ rotate the text, but
the text is rotated about the top left of the bound rect, and
TXNDrawCFStringTextBox clips to its bound,rect so according to my
experiments, for rotation degrees outside the range -30..30 the text is
completely clipped off.

void VerticalCaption::DrawSelf(){
Rect frame;
TXNTextBoxOptionsData tx;
CalcLocalFrameRect(frame);

SInt16 just = UTextTraits::SetPortTextTraits(mTxtrID);

RGBColor textColor;
GetForeColor(&textColor);

ApplyForeAndBackColors();
RGBForeColor(&textColor);

EraseRect(&frame);
tx.optionTags = kTXNRotateTextMask | kTXNUseFontFallBackMask;
tx.rotation = -mDegrees * 65536.; // float to fixed conversion.
tx.options = NULL;
TXNDrawCFStringTextBox(mRef, &frame, NULL, &tx);
}
Loading...