/* ---------------------------- */ /* */ /* Vertical Ruby object handler */ /* */ /* Contact: antons */ /* */ /* ---------------------------- */ #include "lsmem.h" #include "limits.h" #include "vruby.h" #include "objhelp.h" #include "lscbk.h" #include "lsdevres.h" #include "pdobj.h" #include "objdim.h" #include "plssubl.h" #include "plsdnode.h" #include "pilsobj.h" #include "lscrsubl.h" #include "lssubset.h" #include "lsdnset.h" #include "zqfromza.h" #include "lsdocinf.h" #include "fmti.h" #include "posichnk.h" #include "locchnk.h" #include "lsdnfin.h" #include "brko.h" #include "lspap.h" #include "plspap.h" #include "lsqsubl.h" #include "dispi.h" #include "lsdssubl.h" #include "lsems.h" #include "dispmisc.h" #include "lstfset.h" #include "lsqout.h" #include "lsqin.h" #include "sobjhelp.h" #include "brkkind.h" #define VRUBY_MAIN_ESC_CNT 1 #define VRUBY_RUBY_ESC_CNT 1 #define max(a,b) ((a)>(b) ? (a) : (b)) struct ilsobj { POLS pols; LSCBK lscbk; PLSC plsc; LSDEVRES lsdevres; VRUBYSYNTAX vrubysyntax; LSESC lsescMain; LSESC lsescRuby; VRUBYCBK vrcbk; /* Callbacks to client application */ }; struct dobj { SOBJHELP sobjhelp; /* common area for simple objects */ PILSOBJ pilsobj; /* ILS object */ PLSDNODE plsdn; /* DNODE for this object */ PLSRUN plsrun; /* PLSRUN of the object */ LSCP cpStart; /* Starting LS cp for object */ LSTFLOW lstflowParent; /* text flow of the parent subline */ LSTFLOW lstflowRuby; /* text flow of the ruby subline (must be Rotate90CloclWise [lstflowParent]) */ LSCP cpStartRuby; /* first cp of the ruby line */ LSCP cpStartMain; /* first cp of the main line */ PLSSUBL plssublMain; /* Handle to first subline */ PLSSUBL plssublRuby; /* Handle to second line */ HEIGHTS heightsRefRubyT; /* Ref and pres height of rotated Ruby line as given by client */ HEIGHTS heightsPresRubyT; OBJDIM objdimMain; /* Dimensions of the main subline */ OBJDIM objdimRuby; /* Dimensions of the ruby subline */ /* Display information */ long dupMain; long dupOffsetRuby; /* Offset of Ruby line's baseline from start of object */ long dvpOffsetRuby; /* Offset of Ruby line's baseline from start of object */ }; /* V R U B Y F R E E D O B J */ /*---------------------------------------------------------------------------- %%Function: VRubyFreeDobj %%Contact: antons Free all resources associated with this VRuby dobj. ----------------------------------------------------------------------------*/ static LSERR VRubyFreeDobj (PDOBJ pdobj) { LSERR lserr1 = lserrNone; LSERR lserr2 = lserrNone; PILSOBJ pilsobj = pdobj->pilsobj; if (pdobj->plssublMain != NULL) { lserr1 = LsDestroySubline(pdobj->plssublMain); } if (pdobj->plssublRuby != NULL) { lserr2 = LsDestroySubline(pdobj->plssublRuby); } pilsobj->lscbk.pfnDisposePtr(pilsobj->pols, pdobj); if (lserr1 != lserrNone) return lserr1; else return lserr2; } /* V R U B Y F M T F A I L E D */ /*---------------------------------------------------------------------------- %%Function: RubyFmtFailed %%Contact: antons Could not create VRuby DOBJ due to error. ----------------------------------------------------------------------------*/ static LSERR VRubyFmtFailed (PDOBJ pdobj, LSERR lserr) { if (pdobj != NULL) VRubyFreeDobj (pdobj); /* Works with parially-filled DOBJ */ return lserr; } /* V R U B I C R E A T E I L S O B J */ /*---------------------------------------------------------------------------- %%Function: VRubyCreateILSObj %%Contact: antons Create the ILS object for all VRuby objects. ----------------------------------------------------------------------------*/ LSERR WINAPI VRubyCreateILSObj ( POLS pols, /* (IN): client application context */ PLSC plsc, /* (IN): LS context */ PCLSCBK pclscbk, /* (IN): callbacks to client application */ DWORD idObj, /* (IN): id of the object */ PILSOBJ *ppilsobj) /* (OUT): object ilsobj */ { PILSOBJ pilsobj; LSERR lserr; VRUBYINIT vrubyinit; vrubyinit.dwVersion = VRUBY_VERSION; /* Get initialization data */ lserr = pclscbk->pfnGetObjectHandlerInfo(pols, idObj, &vrubyinit); if (lserr != lserrNone) return lserr; pilsobj = pclscbk->pfnNewPtr(pols, sizeof(*pilsobj)); if (NULL == pilsobj) return lserrOutOfMemory; pilsobj->pols = pols; pilsobj->lscbk = *pclscbk; pilsobj->plsc = plsc; pilsobj->lsescMain.wchFirst = vrubyinit.wchEscMain; pilsobj->lsescMain.wchLast = vrubyinit.wchEscMain; pilsobj->lsescRuby.wchFirst = vrubyinit.wchEscRuby; pilsobj->lsescRuby.wchLast = vrubyinit.wchEscRuby; pilsobj->vrcbk = vrubyinit.vrcbk; pilsobj->vrubysyntax = vrubyinit.vrubysyntax; *ppilsobj = pilsobj; return lserrNone; } /* V R U B I D E S T R O Y I L S O B J */ /*---------------------------------------------------------------------------- %%Function: RubyDestroyILSObj %%Contact: antons Free all resources assocaiated with VRuby ILS object. ----------------------------------------------------------------------------*/ LSERR WINAPI VRubyDestroyILSObj( PILSOBJ pilsobj) /* (IN): object ilsobj */ { pilsobj->lscbk.pfnDisposePtr(pilsobj->pols, pilsobj); return lserrNone; } /* V R U B I S E T D O C */ /*---------------------------------------------------------------------------- %%Function: VRubySetDoc %%Contact: antons Keep track of device resolution. ----------------------------------------------------------------------------*/ LSERR WINAPI VRubySetDoc( PILSOBJ pilsobj, /* (IN): object ilsobj */ PCLSDOCINF pclsdocinf) /* (IN): initialization data of the document level */ { pilsobj->lsdevres = pclsdocinf->lsdevres; return lserrNone; } /* V R U B I C R E A T E L N O B J */ /*---------------------------------------------------------------------------- %%Function: RubyCreateLNObj %%Contact: antons Create the Line Object for the Ruby. Since we only really need the global ILS object, just pass that object back as the line object. ----------------------------------------------------------------------------*/ LSERR WINAPI VRubyCreateLNObj (PCILSOBJ pcilsobj, PLNOBJ *pplnobj) { *pplnobj = (PLNOBJ) pcilsobj; return lserrNone; } /* V R U B I D E S T R O Y L N O B J */ /*---------------------------------------------------------------------------- %%Function: RubyDestroyLNObj %%Contact: antons Frees resources associated with the Ruby line object. No-op because we don't really allocate one. ----------------------------------------------------------------------------*/ LSERR WINAPI VRubyDestroyLNObj (PLNOBJ plnobj) { Unreferenced(plnobj); return lserrNone; } /* L S F T L O W V R U B Y F R O M L S T F L O W M A I N */ /* ---------------------------------------------------------------------------- %%Function: LstflowVRubyFromLstflowMain %%Contact: antons ----------------------------------------------------------------------------*/ LSTFLOW LstflowVRubyFromLstflowMain (LSTFLOW lstflow) { static LSTFLOW lstflowRotateForRuby [] = { lstflowSW, /* [ lstflowES ] - english */ lstflowNW, /* [ lstflowEN ] */ lstflowEN, /* [ lstflowSE ] */ lstflowWN, /* [ lstflowSW ] */ lstflowSE, /* [ lstflowWS ] - bidi */ lstflowNE, /* [ lstflowWN ] */ lstflowES, /* [ lstflowNE ] */ lstflowWS /* [ lstflowNW ] */ }; return lstflowRotateForRuby [lstflow]; } /* C A L C A G R E G A T E D H E I G H T */ /*---------------------------------------------------------------------------- %%Function: CalcAgregatedHeight %%Contact: antons ----------------------------------------------------------------------------*/ void CalcAgregatedHeights (PCHEIGHTS pcHeights1, PCHEIGHTS pcHeights2, PHEIGHTS pHeightOut) { pHeightOut->dvAscent = max (pcHeights1->dvAscent, pcHeights2->dvAscent); pHeightOut->dvDescent = max (pcHeights1->dvDescent, pcHeights2->dvDescent); pHeightOut->dvMultiLineHeight = max (pcHeights1->dvMultiLineHeight, pcHeights2->dvMultiLineHeight); } /* V R U B I F M T */ /*---------------------------------------------------------------------------- %%Function: VRubyFmt %%Contact: antons Format Vertical Ruby object ----------------------------------------------------------------------------*/ LSERR WINAPI VRubyFmt( PLNOBJ plnobj, /* (IN): object lnobj */ PCFMTIN pcfmtin, /* (IN): formatting input */ FMTRES *pfmtres) /* (OUT): formatting result */ { PDOBJ pdobj; LSERR lserr; PILSOBJ pilsobj = (PILSOBJ) plnobj; POLS pols = pilsobj->pols; LSCP cpStartMain; LSCP cpStartRuby = pcfmtin->lsfgi.cpFirst + 1; LSCP cpOut; LSTFLOW lstflow = pcfmtin->lsfgi.lstflow; FMTRES fmtres; FMTRES fmtr = fmtrCompletedRun; LONG durAdjust; /* Allocate the DOBJ */ pdobj = pilsobj->lscbk.pfnNewPtr(pols, sizeof(*pdobj)); if (pdobj == NULL) return VRubyFmtFailed (NULL, lserrOutOfMemory); ZeroMemory(pdobj, sizeof(*pdobj)); pdobj->pilsobj = pilsobj; pdobj->plsrun = pcfmtin->lsfrun.plsrun; pdobj->plsdn = pcfmtin->plsdnTop; pdobj->cpStart = pcfmtin->lsfgi.cpFirst; pdobj->lstflowParent = lstflow; pdobj->lstflowRuby = LstflowVRubyFromLstflowMain (lstflow); if (VRubyPronunciationLineFirst == pilsobj->vrubysyntax) { /* Build pronunciation line of text */ lserr = FormatLine ( pilsobj->plsc, cpStartRuby, LONG_MAX, pdobj->lstflowRuby, & pdobj->plssublRuby, 1, &pilsobj->lsescRuby, & pdobj->objdimRuby, &cpOut, NULL, NULL, &fmtres ); /* +1 moves passed the ruby line escape character */ cpStartMain = cpOut + 1; pdobj->cpStartRuby = cpStartRuby; pdobj->cpStartMain = cpStartMain; /* Build main line of text */ if (lserrNone == lserr) { lserr = FormatLine ( pilsobj->plsc, cpStartMain, LONG_MAX, lstflow, & pdobj->plssublMain, 1, &pilsobj->lsescMain, & pdobj->objdimMain, &cpOut, NULL, NULL, &fmtres ); } } else { /* Build main line of text */ cpStartMain = cpStartRuby; lserr = FormatLine ( pilsobj->plsc, cpStartMain, LONG_MAX, lstflow, & pdobj->plssublMain, 1, &pilsobj->lsescMain, & pdobj->objdimMain, &cpOut, NULL, NULL, &fmtres ); /* +1 moves passed the main line escape character */ cpStartRuby = cpOut + 1; pdobj->cpStartRuby = cpStartRuby; pdobj->cpStartMain = cpStartMain; /* Build pronunciation line of text */ if (lserrNone == lserr) { lserr = FormatLine ( pilsobj->plsc, cpStartRuby, LONG_MAX, pdobj->lstflowRuby, & pdobj->plssublRuby, 1, &pilsobj->lsescRuby, & pdobj->objdimRuby, &cpOut, NULL, NULL, &fmtres); } } if (lserr != lserrNone) return VRubyFmtFailed (pdobj, lserr); /* Calculate the object dimensions */ lserr = pilsobj->vrcbk.pfnFetchVRubyPosition ( pols, pdobj->cpStart, pdobj->lstflowParent, pdobj->plsrun, &pdobj->objdimMain.heightsRef, &pdobj->objdimMain.heightsPres, pdobj->objdimRuby.dur, &pdobj->heightsPresRubyT, &pdobj->heightsRefRubyT, &durAdjust ); if (lserr != lserrNone) return VRubyFmtFailed (pdobj, lserr); pdobj->sobjhelp.objdimAll.dur = pdobj->objdimMain.dur + pdobj->objdimRuby.heightsRef.dvDescent + pdobj->objdimRuby.heightsRef.dvAscent + durAdjust ; CalcAgregatedHeights (&pdobj->objdimMain.heightsPres, &pdobj->heightsPresRubyT, &pdobj->sobjhelp.objdimAll.heightsPres ); CalcAgregatedHeights (&pdobj->objdimMain.heightsRef, &pdobj->heightsRefRubyT, &pdobj->sobjhelp.objdimAll.heightsRef ); /* Need to add 1 to take into account escape character at end. */ pdobj->sobjhelp.dcp = cpOut - pdobj->cpStart + 1; lserr = LsdnFinishRegular(pilsobj->plsc, pdobj->sobjhelp.dcp, pcfmtin->lsfrun.plsrun, pcfmtin->lsfrun.plschp, pdobj, &pdobj->sobjhelp.objdimAll); if (lserr != lserrNone) return VRubyFmtFailed (pdobj, lserr); if (pcfmtin->lsfgi.urPen + pdobj->sobjhelp.objdimAll.dur > pcfmtin->lsfgi.urColumnMax) { fmtr = fmtrExceededMargin; } *pfmtres = fmtr; return lserrNone; } /* V R U B Y S E T B R E A K */ /*---------------------------------------------------------------------------- %%Function: VRubySetBreak %%Contact: antons SetBreak ----------------------------------------------------------------------------*/ LSERR WINAPI VRubySetBreak ( PDOBJ pdobj, /* (IN): dobj which is broken */ BRKKIND brkkind, /* (IN): prev | next | force | after */ DWORD cBreakRecord, /* (IN): size of array */ BREAKREC *rgBreakRecord, /* (IN): array of break records */ DWORD *pcActualBreakRecord) /* (IN): actual number of used elements in array */ { Unreferenced (rgBreakRecord); Unreferenced (cBreakRecord); Unreferenced (brkkind); Unreferenced (pdobj); *pcActualBreakRecord = 0; return lserrNone; } /* V R U B Y G E T S P E C I A L E F F E C T S I N S I D E */ /*---------------------------------------------------------------------------- %%Function: VRubyGetSpecialEffectsInside %%Contact: antons VRubyGetSpecialEffectsInside ----------------------------------------------------------------------------*/ LSERR WINAPI VRubyGetSpecialEffectsInside( PDOBJ pdobj, /* (IN): dobj */ UINT *pEffectsFlags) /* (OUT): Special effects for this object */ { LSERR lserr = LsGetSpecialEffectsSubline(pdobj->plssublMain, pEffectsFlags); if (lserrNone == lserr) { UINT uiSpecialEffectsRuby; lserr = LsGetSpecialEffectsSubline(pdobj->plssublRuby, &uiSpecialEffectsRuby); *pEffectsFlags |= uiSpecialEffectsRuby; } return lserr; } /* V R U B Y C A L C P R E S E N T A T I O N */ /*---------------------------------------------------------------------------- %%Function: VRubyCalcPresentation %%Contact: antons CalcPresentation ----------------------------------------------------------------------------*/ LSERR WINAPI VRubyCalcPresentation ( PDOBJ pdobj, /* (IN): dobj */ long dup, /* (IN): dup of dobj */ LSKJUST lskjust, /* (IN): Justification type */ BOOL fLastVisibleOnLine ) /* (IN): Is this object last visible on line? */ { LSERR lserr = lserrNone; LSTFLOW lstflowUnused; Unreferenced (lskjust); Unreferenced(dup); Unreferenced (fLastVisibleOnLine); lserr = LsMatchPresSubline(pdobj->plssublMain); if (lserr != lserrNone) return lserr; lserr = LsMatchPresSubline(pdobj->plssublRuby); if (lserr != lserrNone) return lserr; LssbGetDupSubline (pdobj->plssublMain, &lstflowUnused, &pdobj->dupMain); pdobj->dupOffsetRuby = pdobj->dupMain + pdobj->objdimRuby.heightsPres.dvDescent; /* Review (antons): This will not work if horizintal res != vertical */ pdobj->dvpOffsetRuby = pdobj->heightsPresRubyT.dvAscent; return lserr; } /* V R U B Y Q U E R Y P O I N T P C P */ /*---------------------------------------------------------------------------- %%Function: RubyQueryPointPcp %%Contact: antons ----------------------------------------------------------------------------*/ LSERR WINAPI VRubyQueryPointPcp( PDOBJ pdobj, /*(IN): dobj to query */ PCPOINTUV ppointuvQuery, /*(IN): query point (uQuery,vQuery) */ PCLSQIN plsqin, /*(IN): query input */ PLSQOUT plsqout) /*(OUT): query output */ { PLSSUBL plssubl; long dupAdj; long dvpAdj; /* * Decide which line to to return based on the height of the point input */ /* Assume main line */ plssubl = pdobj->plssublMain; dupAdj = 0; dvpAdj = 0; if (ppointuvQuery->u > pdobj->dupMain) { /* hit second line */ plssubl = pdobj->plssublRuby; dupAdj = pdobj->dupOffsetRuby; dvpAdj = pdobj->dvpOffsetRuby; } return CreateQueryResult(plssubl, dupAdj, dvpAdj, plsqin, plsqout); } /* V R U B Y Q U E R Y C P P P O I N T */ /*---------------------------------------------------------------------------- %%Function: RubyQueryCpPpoint %%Contact: antons ----------------------------------------------------------------------------*/ LSERR WINAPI VRubyQueryCpPpoint( PDOBJ pdobj, /*(IN): dobj to query, */ LSDCP dcp, /*(IN): dcp for the query */ PCLSQIN plsqin, /*(IN): query input */ PLSQOUT plsqout) /*(OUT): query output */ { PLSSUBL plssubl; long dupAdj; long dvpAdj; BOOL fMain = fFalse; LSCP cpQuery = pdobj->cpStart + dcp; /* Assume ruby line */ plssubl = pdobj->plssublRuby; dupAdj = pdobj->dupOffsetRuby; dvpAdj = pdobj->dvpOffsetRuby; /* + 1 means we include the cp of the object in the Ruby pronunciation line. */ if (VRubyPronunciationLineFirst == pdobj->pilsobj->vrubysyntax) { /* Ruby pronunciation line is first */ if (cpQuery >= pdobj->cpStartMain) { fMain = fTrue; } } else { /* Main text line is first */ if (cpQuery < pdobj->cpStartRuby) { fMain = fTrue; } } if (fMain) { plssubl = pdobj->plssublMain; dupAdj = 0; dvpAdj = 0; } return CreateQueryResult(plssubl, dupAdj, dvpAdj, plsqin, plsqout); } /* V R U B I D I S P L A Y */ /*---------------------------------------------------------------------------- %%Function: VRubyDisplay %%Contact: antons ----------------------------------------------------------------------------*/ LSERR WINAPI VRubyDisplay( PDOBJ pdobj, /*(IN): dobj to display */ PCDISPIN pcdispin) /*(IN): display info */ { LSERR lserr; UINT kDispMode = pcdispin->kDispMode; POINTUV ptAdd; POINT ptLine; /* display first line */ lserr = LsDisplaySubline(pdobj->plssublMain, &pcdispin->ptPen, kDispMode, pcdispin->prcClip); if (lserr != lserrNone) return lserr; ptAdd.u = pdobj->dupOffsetRuby; ptAdd.v = pdobj->dvpOffsetRuby; LsPointXYFromPointUV(&pcdispin->ptPen, pdobj->lstflowParent, &ptAdd, &ptLine); return LsDisplaySubline(pdobj->plssublRuby, &ptLine, kDispMode, pcdispin->prcClip); } /* V R U B I D E S T R O Y D O B J */ /*---------------------------------------------------------------------------- %%Function: VRubyDestroyDobj %%Contact: antons ----------------------------------------------------------------------------*/ LSERR WINAPI VRubyDestroyDobj( PDOBJ pdobj) /*(IN): dobj to destroy */ { return VRubyFreeDobj (pdobj); } /* V R U B Y E N U M */ /*---------------------------------------------------------------------------- %%Function: VRubyEnum %%Contact: antons ----------------------------------------------------------------------------*/ LSERR WINAPI VRubyEnum ( PDOBJ pdobj, /*(IN): dobj to enumerate */ PLSRUN plsrun, /*(IN): from DNODE */ PCLSCHP plschp, /*(IN): from DNODE */ LSCP cp, /*(IN): from DNODE */ LSDCP dcp, /*(IN): from DNODE */ LSTFLOW lstflow, /*(IN): text flow*/ BOOL fReverse, /*(IN): enumerate in reverse order */ BOOL fGeometryNeeded, /*(IN): */ const POINT *ppt, /*(IN): starting position (top left), iff fGeometryNeeded */ PCHEIGHTS pcheights, /*(IN): from DNODE, relevant iff fGeometryNeeded */ long dupRun ) /*(IN): from DNODE, relevant iff fGeometryNeeded */ { POINT ptMain; POINT ptRuby; POINTUV ptAdd; long dupMain = 0; long dupRuby = 0; LSERR lserr; LSTFLOW lstflowIgnored; if (fGeometryNeeded) { ptMain = *ppt; ptAdd.u = pdobj->dupOffsetRuby; ptAdd.v = pdobj->dvpOffsetRuby; LsPointXYFromPointUV(ppt, pdobj->lstflowParent, &ptAdd, &ptRuby); lserr = LssbGetDupSubline(pdobj->plssublMain, &lstflowIgnored, &dupMain); if (lserr != lserrNone) return lserr; lserr = LssbGetDupSubline(pdobj->plssublRuby, &lstflowIgnored, &dupRuby); if (lserr != lserrNone) return lserr; } return pdobj->pilsobj->vrcbk.pfnVRubyEnum (pdobj->pilsobj->pols, plsrun, plschp, cp, dcp, lstflow, fReverse, fGeometryNeeded, ppt, pcheights, dupRun, &ptMain, &pdobj->objdimMain.heightsPres, dupMain, &ptRuby, &pdobj->objdimRuby.heightsPres, dupRuby, pdobj->plssublMain, pdobj->plssublRuby); } /* V R U B I H A N D L E R I N I T */ /*---------------------------------------------------------------------------- %%Function: VRubyHandlerInit %%Contact: antons ----------------------------------------------------------------------------*/ LSERR WINAPI LsGetVRubyLsimethods ( LSIMETHODS *plsim ) { plsim->pfnCreateILSObj = VRubyCreateILSObj; plsim->pfnDestroyILSObj = VRubyDestroyILSObj; plsim->pfnSetDoc = VRubySetDoc; plsim->pfnCreateLNObj = VRubyCreateLNObj; plsim->pfnDestroyLNObj = VRubyDestroyLNObj; plsim->pfnFmt = VRubyFmt; plsim->pfnFmtResume = ObjHelpFmtResume; plsim->pfnGetModWidthPrecedingChar = ObjHelpGetModWidthChar; plsim->pfnGetModWidthFollowingChar = ObjHelpGetModWidthChar; plsim->pfnTruncateChunk = SobjTruncateChunk; plsim->pfnFindPrevBreakChunk = SobjFindPrevBreakChunk; plsim->pfnFindNextBreakChunk = SobjFindNextBreakChunk; plsim->pfnForceBreakChunk = SobjForceBreakChunk; plsim->pfnSetBreak = VRubySetBreak; plsim->pfnGetSpecialEffectsInside = VRubyGetSpecialEffectsInside; plsim->pfnFExpandWithPrecedingChar = ObjHelpFExpandWithPrecedingChar; plsim->pfnFExpandWithFollowingChar = ObjHelpFExpandWithFollowingChar; plsim->pfnCalcPresentation = VRubyCalcPresentation; plsim->pfnQueryPointPcp = VRubyQueryPointPcp; plsim->pfnQueryCpPpoint = VRubyQueryCpPpoint; plsim->pfnDisplay = VRubyDisplay; plsim->pfnDestroyDObj = VRubyDestroyDobj; plsim->pfnEnum = VRubyEnum; return lserrNone; }