This is DrawingView.m in view mode; [Download] [Up]
/* * (a) (C) 1990 by Adobe Systems Incorporated. All rights reserved. * * (b) If this Sample Code is distributed as part of the Display PostScript * System Software Development Kit from Adobe Systems Incorporated, * then this copy is designated as Development Software and its use is * subject to the terms of the License Agreement attached to such Kit. * * (c) If this Sample Code is distributed independently, then the following * terms apply: * * (d) This file may be freely copied and redistributed as long as: * 1) Parts (a), (d), (e) and (f) continue to be included in the file, * 2) If the file has been modified in any way, a notice of such * modification is conspicuously indicated. * * (e) PostScript, Display PostScript, and Adobe are registered trademarks of * Adobe Systems Incorporated. * * (f) THE INFORMATION BELOW IS FURNISHED AS IS, IS SUBJECT TO * CHANGE WITHOUT NOTICE, AND SHOULD NOT BE CONSTRUED * AS A COMMITMENT BY ADOBE SYSTEMS INCORPORATED. * ADOBE SYSTEMS INCORPORATED ASSUMES NO RESPONSIBILITY * OR LIABILITY FOR ANY ERRORS OR INACCURACIES, MAKES NO * WARRANTY OF ANY KIND (EXPRESS, IMPLIED OR STATUTORY) * WITH RESPECT TO THIS INFORMATION, AND EXPRESSLY * DISCLAIMS ANY AND ALL WARRANTIES OF MERCHANTABILITY, * FITNESS FOR PARTICULAR PURPOSES AND NONINFRINGEMENT * OF THIRD PARTY RIGHTS. */ /* * DrawingView.m * * This view represents the page that the image is drawn onto. It is * a subview of the DocView. The DocView is the document view of * the ClipView. This view's real size grows with the scale but the * bounds always stays the same. * * Version: 2.0 * Author: Ken Fromm * History: * 03-07-91 Added this comment. */ #import "DrawingView.h" #import "DrawingViewWraps.h" #import "DocView.h" #import "TextApp.h" #import "hdshowaux.h" #import <appkit/Button.h> #import <appkit/Cell.h> #import <appkit/Font.h> #import <appkit/Matrix.h> #import <appkit/nextstd.h> #import <dpsclient/dpsclient.h> #import <dpsclient/wraps.h> #import <ctype.h> extern char*const textstrings[]; extern float textstart[][2]; @implementation DrawingView /* * Allocate a gstate, set the clipping to NO because it will be clipped * by the clip view. */ +newFrame:(NXRect *) frm { id font; self = [super newFrame:frm]; [[[self allocateGState] setClipping:NO] setFlipped:YES]; NX_MALLOC(charspace, NXCoord, MAX_XSHOW); AllocShowStruct(&s); showtype = XSHOW; /* use xshow */ cache = YES; /* use font cache */ fontsize = FONTSIZE; font = [Font newFont:"Times-Roman" size:fontsize]; PSWCopyFont("Times-Roman", "Times-RomanOutline"); PSWCurrentcacheparams(&size, &lower, &upper); return self; } - free { PSWSetcacheparams(size, lower, upper); NXPing(); NX_FREE(charspace); FreeShowStruct(&s); return [super free]; } - (int) rotation { return rotation; } - fontsize:sender { fontsize = atof([[sender selectedCell] title]); [self reshow:self]; return self; } - justify:sender { justify = ([sender selectedTag] != 0); [self reshow:self]; return self; } - kern:sender { kern = [[sender selectedCell] state]; [self reshow:self]; return self; } - track:sender { track = [[sender selectedCell] state]; [self reshow:self]; return self; } /* * Sets the font cache to an arbitrarily low size. It's almost never necessary * to change the font cache size. This is only here to provide some * demonstration of the effectiveness of the font cache. Its operation is * best left alone. */ - cache:sender { cache = [[sender selectedCell] state]; if (cache) PSWSetcacheparams(size, lower, upper); else PSWSetcacheparams(0, 0, 0); [self reshow:self]; return self; } - outline:sender { outline = [[sender selectedCell] state]; [self reshow:self]; return self; } - show:sender { showtype = [sender selectedTag]; [self reshow:self]; return self; } - compareKerns:sender { id cell; compareKern = [[sender selectedCell] state]; if (compareKern && compareWidths) { compareWidths = NO; cell = [[NXApp comparisonsMatrix] findCellWithTag:WIDTH_TAG]; [cell setState:NO]; [[cell controlView] drawCell:cell]; } [self reshow:self]; return self; } - compareWidths:sender { id cell; compareWidths = [[sender selectedCell] state]; if (compareWidths && compareKern) { compareKern = NO; cell = [[NXApp comparisonsMatrix] findCellWithTag:KERN_TAG]; [cell setState:NO]; [[cell controlView] drawCell:cell]; } [self reshow:self]; return self; } - rotate:sender { id field; field = [NXApp rotationField]; if (sender == field) rotation = atoi([field stringValue]); else if ([sender selectedTag] == 0) rotation += ROTATION; else rotation -= ROTATION; rotation = rotation % 360; [field setIntValue:rotation]; [self resetFields:self]; [self rotateTo:(float) rotation]; [superview rotateDrawView]; [self displayFields:self]; return self; } /* * Changes the title of the menu cell according * to the value of the trace variable. */ -trace:sender { if (trace == NO) [[sender selectedCell] setTitle:"Trace On"]; else [[sender selectedCell] setTitle:"Trace Off"]; trace = !trace; return self; } - eraseFields:sender { id matrixId; int i; matrixId = [NXApp timingMatrix]; for (i= 0; i < [matrixId cellCount]; i++) [[matrixId cellAt:i :0] setStringValue:""]; matrixId = [NXApp statusMatrix]; for (i= 0; i < [matrixId cellCount]; i++) [[matrixId cellAt:i :0] setStringValue:""]; eraseFields = NO; return self; } - resetFields:sender { /* Prepare for next time around. */ timing_info.chars = timing_info.reshows = timing_info.kerns = timing_info.time = 0; return self; } - displayFields:sender { id matrixId; int i, cvalues[7]; if (timing_info.reshows) { matrixId = [NXApp timingMatrix]; [[matrixId cellAt:0 :0] setIntValue:rint(timing_info.time/timing_info.reshows)]; [[matrixId cellAt:1 :0] setIntValue:rint(timing_info.chars/timing_info.reshows)]; [[matrixId cellAt:2 :0] setIntValue:rint(timing_info.kerns/timing_info.reshows)]; matrixId = [NXApp statusMatrix]; PSWCachestatus(cvalues); for (i= 0; i < [matrixId cellCount]; i++) [[matrixId cellAt:i :0] setIntValue:cvalues[i]]; } [self resetFields:self]; eraseFields = YES; return self; } - reshow:sender { NXRect visRect; eraseFields = NO; [self resetFields:self]; [self getVisibleRect:&visRect]; [self display:&visRect :1]; [self displayFields:self]; return self; } /* * If the docview is zooming, then scale the drawing view. */ - mouseDown:(NXEvent *)event { NXPoint p; p = event->location; if ([superview isZooming]) { eraseFields = NO; [self resetFields:self]; [superview scaleDrawViewToPoint:&p]; [self displayFields:self]; } return self; } static NXCoord filltrackwidths(char *str, float *array, ShowStruct *s, float value) { int i, len; NXCoord trackwidth = 0.0; if (str && value) { len = strlen(str); if (array) { for (i = 0; i < len; i++) array[i] += value; } else if (s) AddTracking(s, 0, value); trackwidth = len * value; } return trackwidth; } static void fillspacewidths(char *str, float *array, ShowStruct *s, float value) { int i, len; if (str && value) { if (array) { len = strlen(str); for (i = 0; i < len; i++) if (str[i] == ' ') array[i] += value; } else if (s) AddSpaceAdj(s, 0, value); } } /* * If an array is passed in then place the character width for each character * in str in it (used when displaying with xshow). Also keep track of the total width * and the number of spaces and return the value of the width divided by the * spaces (used when displaying with full justification). */ static NXCoord fillstringwidths(char *str, NXFontMetrics *metrics, float *array, float fontsize, NXCoord kernwidth, NXCoord trackwidth) { BOOL nospacing = NO; int i, len, spaces; NXCoord value, linewidth; spaces = 0; linewidth = kernwidth + trackwidth; if (str && metrics && metrics->widths) { len = strlen(str); for (i = 0; i < len; i++) { value = metrics->widths[(unsigned char)str[i]] * fontsize; linewidth += value; if (array) array[i] += value; /* Check for space, return, linefeed, etc. */ if(isspace(str[i])) { if (str[i] == ' ') spaces ++; else nospacing = YES; } } } if (spaces == 0) value = 0.0; else { if (trackwidth) value = (LINE_LENGTH_TR - linewidth)/spaces; else value = (LINE_LENGTH - linewidth)/spaces; if (nospacing && value > 0) value = 0.0; } return value; } /* * Look up the kern pairs for the first character and see if * there is an entry for the second character. Returns the * value to kern if any. */ static NXCoord getkernvalue(NXFontMetrics *metrics, unsigned char char1, unsigned char char2) { int enc1, enc2, i, kindex, klen; NXCoord value = 0.0; enc1 = metrics->encoding[char1]; enc2 = metrics->encoding[char2]; if (enc1 < metrics->numCharMetrics && enc2 < metrics->numCharMetrics) { kindex = metrics->charMetrics[enc1].kernPairIndex; klen = metrics->charMetrics[enc1].numKernPairs; if (kindex+klen < metrics->numKernPairs) { for (i = kindex; i < kindex + klen; i++) { if (metrics->hasXYKerns) { if (enc2 == metrics->kerns.kernPairs[i].secondCharIndex) { value = metrics->kerns.kernPairs[i].dx; break; } } else { if (enc2 == metrics->kerns.kernXPairs[i].secondCharIndex) { value = metrics->kerns.kernXPairs[i].dx; break; } } } } } return value; } static NXCoord fillkernwidths(char *str, NXFontMetrics *metrics, float *array, ShowStruct *s, float fontsize, int *kerns) { int i, len; NXCoord value, kernwidth; kernwidth = 0.0; if (str && metrics) { len = strlen(str) -1; for (i = 0; i < len; i++) { if (value = getkernvalue(metrics, (unsigned char) str[i], (unsigned char) str[i+1])) { value = value * fontsize; if (array) array[i] += value; else if (s) AddPairKern(s, i+1, value); kernwidth += value; (*kerns)++; } } } return kernwidth; } /* * Go through the line and insert the appropriate value into the array. * The value placed in is dependent on whether we are kerning or not * and whether we are using xshow or not. If kerning, then look up each * pair in the AFM kerning array. If using xshow, then include the * character width in the array as well (xshow takes the displacement from * the previously shown character hence the character width). * The fontsize is used to scale the kerning value and the character width. * In a screenfont, no adjustment is necessary because the values are * already scaled to the appropriate values. */ static int characterspacing(id font, char *str, NXCoord *array, BOOL justify, BOOL kern, BOOL track, BOOL width) { int kerns = 0; float fontsize; NXCoord kernwidth, spacewidth, trackwidth; NXFontMetrics *metrics; bzero(array, MAX_XSHOW*sizeof(NXCoord)); metrics = [font readMetrics:FONTMETRICS]; if (metrics) { if (metrics->isScreenFont) fontsize = 1; else fontsize = [font pointSize]; kernwidth = spacewidth = trackwidth = 0.0; if (kern && metrics->encoding && metrics->charMetrics) kernwidth = fillkernwidths(str, metrics, array, NULL, fontsize, &kerns); if (track) trackwidth = filltrackwidths(str, array, NULL, TRACKVAL); if (width && metrics->widths) spacewidth = fillstringwidths(str, metrics, array, fontsize, kernwidth, trackwidth); if (justify) { if (!width && metrics->widths) spacewidth = fillstringwidths(str, metrics, NULL, fontsize, kernwidth, trackwidth); fillspacewidths(str, array, NULL, spacewidth); } } return kerns; } static int showstructure(id font, char *str, ShowStruct *s, BOOL justify, BOOL kern, BOOL track) { int kerns = 0; float fontsize; NXCoord kernwidth, spacewidth, trackwidth; NXFontMetrics *metrics; metrics = [font readMetrics:FONTMETRICS]; if (metrics) { if (metrics->isScreenFont) fontsize = 1; else fontsize = [font pointSize]; kernwidth = spacewidth = trackwidth = 0.0; if (kern && metrics->encoding && metrics->charMetrics) kernwidth = fillkernwidths(str, metrics, NULL, s, fontsize, &kerns); if (track) trackwidth = filltrackwidths(str, NULL, s, TRACKVAL); if (justify) { spacewidth = fillstringwidths(str, metrics, NULL, fontsize, kernwidth, trackwidth); fillspacewidths(str, NULL, s, spacewidth); } } return kerns; } /* * Set the font either to the printer font or the screenfont, turn the trace on * if tracing and then mark the time. (The last two steps are for demonstration * purposes only. Cycle through the static array of text checking if a line * intersects the rectangle passed in. If so then draw it according to the values * set in the interface - kerning or no kerning, screen widths or printer widths, * xshow or rmoveto/show, etc. */ - showText:(const NXRect *)r { id font; float x; int i, j, last, chars, ElapsedTime; NXRect textRect; if (trace) DPSTraceContext(DPSGetCurrentContext(), YES); font = [Font newFont:"Times-Roman" size:fontsize]; if (outline) PSWSetFont("Times-RomanOutline", fontsize); else [font set]; if (screen && [font screenFont]) font = [font screenFont]; textRect.origin.x = bounds.origin.x; textRect.size.width = bounds.size.width; textRect.size.height = 2 * fontsize; PSsetwindowtype(NX_RETAINED, [window windowNum]); PSWMarkTime(); NXPing(); for (i = 0; i < NUM_LINES; i++) { textRect.origin.y = textstart[i][1] - fontsize; if (NXIntersectsRect(r, &textRect)) { chars = strlen(textstrings[i]); timing_info.chars += chars; if (showtype == XSHOW || showtype == RMSHOW) { timing_info.kerns += characterspacing(font, textstrings[i], charspace, justify, kern, track, (showtype == XSHOW)); if (showtype == XSHOW) { PSmoveto(textstart[i][0], textstart[i][1]); PSxshow(textstrings[i], charspace, chars); } else { PSmoveto(textstart[i][0], textstart[i][1]); for (j = 0, last = -1, x = 0.0; j < chars; j++) { if (charspace[j] != 0) { if (x == 0.0) PSWShow(&textstrings[i][last+1], j - last); else PSWRmovetoShow(x, &textstrings[i][last+1], j - last); last = j; x = charspace[j]; } } if (x == 0.0) { if (j-last > 0) PSWShow(&textstrings[i][last+1], j - last); } else { if (j-last-1 > 0) PSWRmovetoShow(x, &textstrings[i][last+1], j - last -1); } } } else { ResetShowStruct(&s); AddString(&s, textstrings[i]); AddMoveto(&s, 0, textstart[i][0], textstart[i][1]); timing_info.kerns += showstructure(font, textstrings[i], &s, justify, kern, track); ShowAny(&s); } } } PSWReturnTime(&ElapsedTime); if (trace) DPSTraceContext(DPSGetCurrentContext(), NO); PSsetwindowtype(NX_BUFFERED, [window windowNum]); timing_info.time += ElapsedTime; timing_info.reshows++; return self; } /* * Erase the fields in the interface, whiten the backdrop, check the cases * for showing the text and call the showText: method appropriately. */ - drawSelf:(const NXRect *)r :(int) count { BOOL temp; if (eraseFields) [self eraseFields:self]; PSsetgray(NX_WHITE); NXRectFill(r); PSsetgray(NX_BLACK); if (track) PStranslate(-45, 0); if (compareKern) { temp = kern; kern = YES; [self showText:r]; kern = NO; PStranslate(0.0, 10.0); [self showText:r]; kern = temp; } else if (compareWidths) { temp = screen; screen = NO; [self showText:r]; screen = YES; PStranslate(0.0, 10.0); [self showText:r]; screen = temp; } else [self showText:r]; return self; } @end
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.