/*---------------------------------------------------------------------------------------------- Copy cb bytes from the current position in this stream to the current position in the stream *pstm. Uses FileStream ${#Read} and ${#Write} methods. Note that though it would be more efficient in some cases to bypass the Read method for several consecutive reads, the case of copying to a clone would require special handling. There is no check for overlapping read & write areas. REVIEW (JohnL): Should there be? ----------------------------------------------------------------------------------------------*/ STDMETHODIMP FileStream::CopyTo(IStream * pstm, ULARGE_INTEGER cb, ULARGE_INTEGER * pcbRead, ULARGE_INTEGER * pcbWritten) { BEGIN_COM_METHOD; ChkComArgPtr(pstm); ChkComArgPtrN(pcbRead); ChkComArgPtrN(pcbWritten); if (cb.HighPart) ThrowHr(WarnHr(STG_E_INVALIDPARAMETER)); // handle only 32-bit byte counts // REVIEW JohnL: should we nevertheless handle cb == max ULARGE_INTEGER as a special case? if (pstm == this) ThrowHr(WarnHr(STG_E_INVALIDPARAMETER)); // prevent copy to self // REVIEW JohnL: is this correct? if (pcbRead) (*pcbRead).QuadPart = 0; if (pcbWritten) (*pcbWritten).QuadPart = 0; const ULONG kcbBufferSize = 4096; ULONG cbReadTotal; ULONG cbWrittenTotal = 0; byte prgbBuffer[kcbBufferSize]; ULONG cbRead = 0; ULONG cbWritten; ULONG cbr = 0; for (cbReadTotal = 0; (cbReadTotal < cb.LowPart) && (cbRead == cbr); ) { cbr = cb.LowPart - cbReadTotal; if (cbr > kcbBufferSize) cbr = kcbBufferSize; CheckHr(Read((void *)prgbBuffer, cbr, &cbRead)); cbReadTotal += cbRead; if (cbRead) { CheckHr(pstm->Write((void *)prgbBuffer, cbRead, &cbWritten)); cbWrittenTotal += cbWritten; } } if (pcbRead) (*pcbRead).LowPart = cbReadTotal; if (pcbWritten) (*pcbWritten).LowPart = cbWrittenTotal; // REVIEW JohnL: How do we define "success" for CopyTo? Should we return a failure if // cbWrittenTotal != cbReadTotal? END_COM_METHOD(g_fact, IID_IStream); }
/*---------------------------------------------------------------------------------------------- Retrieves a specified number of items in the enumeration sequence. Retrieves the next celt items in the enumeration sequence. If there are fewer than the requested number of elements left in the sequence, it retrieves the remaining elements. The number of elements actually retrieved is returned through pceltFetched (unless the caller passed in NULL for that parameter). This is a standard COM IEnumFORMATETC method. @param celt Desired number of elements to retrieve. @param rgelt Pointer to an array for returning the retrieved elements. @param pceltFetched Pointer to a count of the number of elements actually retrieved, or NULL. @return S_OK (if celt items retrieved), S_FALSE (if fewer than celt items retrieved), or E_POINTER. ----------------------------------------------------------------------------------------------*/ STDMETHODIMP LgTsEnumFORMATETC::Next(ULONG celt, FORMATETC * rgelt, ULONG * pceltFetched) { BEGIN_COM_METHOD; ChkComArrayArg(rgelt, celt); ChkComArgPtrN(pceltFetched); if (celt == 0) { if (pceltFetched) *pceltFetched = 0; return S_OK; } int cfmteAvail = kcfmteLim - m_ifmte; if (cfmteAvail <= 0) { if (pceltFetched) *pceltFetched = 0; return S_FALSE; } int cfmte; if (celt > static_cast<ULONG>(cfmteAvail)) cfmte = cfmteAvail; else cfmte = celt; if (pceltFetched) *pceltFetched = cfmte; memcpy(rgelt, &g_rgfmte[m_ifmte], cfmte * isizeof(FORMATETC)); m_ifmte += cfmte; if (m_ifmte > kcfmteLim) m_ifmte = kcfmteLim; return celt == static_cast<ULONG>(cfmte) ? S_OK : S_FALSE; END_COM_METHOD(g_factEnum, IID_IEnumFORMATETC); }
/*---------------------------------------------------------------------------------------------- Perform whatever action is appropriate when the use clicks on a hot link ----------------------------------------------------------------------------------------------*/ STDMETHODIMP VwBaseVc::DoHotLinkAction(BSTR bstrData, ISilDataAccess * psda) { BEGIN_COM_METHOD; ChkComArgPtrN(ptss); if (BstrLen(bstrData) > 0 && bstrData[0] == kodtExternalPathName) { StrAppBuf strbFile(bstrData + 1); if (::UrlIs(strbFile.Chars(), URLIS_URL)) { // If it's a URL launch whatever it means. ::ShellExecute(NULL, L"open", strbFile.Chars(), NULL, NULL, SW_SHOWNORMAL); return S_OK; } if (AfApp::Papp()) { AfMainWnd * pafw = AfApp::Papp()->GetCurMainWnd(); if (pafw && pafw->GetLpInfo()) pafw->GetLpInfo()->MapExternalLink(strbFile); AfApp::LaunchHL(NULL, _T("open"), strbFile.Chars(), NULL, NULL, SW_SHOW); } } return S_OK; END_COM_METHOD(g_fact, IID_IVwViewConstructor); }
/*---------------------------------------------------------------------------------------------- Read the given number of bytes from the stream / file. ----------------------------------------------------------------------------------------------*/ STDMETHODIMP FileStream::Read(void * pv, ULONG cb, ULONG * pcbRead) { BEGIN_COM_METHOD; ChkComArrayArg((byte *)pv, cb); ChkComArgPtrN(pcbRead); if (m_hfile == NULL) ThrowHr(WarnHr(E_UNEXPECTED)); if (cb == 0) { if (pcbRead) *pcbRead = 0; return S_OK; } if (!SetFilePosRaw()) ThrowHr(WarnHr(STG_E_SEEKERROR)); DWORD cbRead = 0; if (!ReadFile(m_hfile, pv, cb, &cbRead, NULL)) ThrowHr(WarnHr(STG_E_READFAULT)); m_ibFilePos.QuadPart += cbRead; if (pcbRead) *pcbRead = cbRead; END_COM_METHOD(g_fact, IID_IStream); }
/*---------------------------------------------------------------------------------------------- Write the given number of bytes to the stream / file. ----------------------------------------------------------------------------------------------*/ STDMETHODIMP FileStream::Write(const void * pv, ULONG cb, ULONG * pcbWritten) { BEGIN_COM_METHOD; ChkComArrayArg((byte *)pv, cb); ChkComArgPtrN(pcbWritten); if (m_hfile == NULL) ThrowHr(WarnHr(E_UNEXPECTED)); if (cb == 0) { if (pcbWritten) *pcbWritten = 0; return S_OK; } if (!SetFilePosRaw()) ThrowHr(WarnHr(STG_E_SEEKERROR)); DWORD cbWritten = 0; if (!WriteFile(m_hfile, pv, cb, &cbWritten, NULL)) ThrowHr(WarnHr(STG_E_WRITEFAULT)); m_ibFilePos.QuadPart += cbWritten; if (pcbWritten) *pcbWritten = cbWritten; END_COM_METHOD(g_fact, IID_IStream); }
/*---------------------------------------------------------------------------------------------- Store the pointer to the help topic provider, if any. ----------------------------------------------------------------------------------------------*/ STDMETHODIMP FwCppStylesDlg::putref_HelpTopicProvider(IHelpTopicProvider * phtprov) { BEGIN_COM_METHOD; ChkComArgPtrN(phtprov); m_qhtprov = phtprov; END_COM_METHOD(g_factOPD, IID_IFwCppStylesDlg); }
/*---------------------------------------------------------------------------------------------- Set the writing system factory for this database (or the registry, as the case may be). @param pwsf Pointer to the writing system factory. ----------------------------------------------------------------------------------------------*/ STDMETHODIMP FwGrEngine::putref_WritingSystemFactory(ILgWritingSystemFactory * pwsf) { BEGIN_COM_METHOD ChkComArgPtrN(pwsf); m_qwsf = pwsf; END_COM_METHOD(g_fact, IID_IRenderEngine) }
/*---------------------------------------------------------------------------------------------- // This will set the UndoGrouper for this AH. ----------------------------------------------------------------------------------------------*/ STDMETHODIMP ActionHandler::put_UndoGrouper(IUndoGrouper * pundg) { BEGIN_COM_METHOD; ChkComArgPtrN(pundg); m_qundg = pundg; END_COM_METHOD(g_factActh, IID_IActionHandler); }
/*---------------------------------------------------------------------------------------------- Set the writing system factory for this simple collator. @param pwsf Pointer to the writing system factory that stores/produces this writing system. ----------------------------------------------------------------------------------------------*/ STDMETHODIMP LgIcuCollator::putref_WritingSystemFactory(ILgWritingSystemFactory * pwsf) { BEGIN_COM_METHOD; ChkComArgPtrN(pwsf); m_qwsf = pwsf; END_COM_METHOD(g_fact, IID_ILgCollatingEngine); }
/*---------------------------------------------------------------------------------------------- Store the stream pointer to the log file, if any. ----------------------------------------------------------------------------------------------*/ STDMETHODIMP FwCppStylesDlg::putref_LogFile(IStream * pstrmLog) { BEGIN_COM_METHOD; ChkComArgPtrN(pstrmLog); m_qstrmLog = pstrmLog; END_COM_METHOD(g_factOPD, IID_IFwCppStylesDlg); }
/*---------------------------------------------------------------------------------------------- Display the styles dialog modally and let it do its thing. ----------------------------------------------------------------------------------------------*/ STDMETHODIMP FwCppStylesDlg::ShowModal(int * pncid) { BEGIN_COM_METHOD; ChkComArgPtrN(pncid); // since ChkComOutPtrN doesn't exist. if (pncid) *pncid = 0; SetupForDoModal(); DoModalDialog(pncid); GetModalResults(*pncid); END_COM_METHOD(g_factOPD, IID_IFwCppStylesDlg); }
/*---------------------------------------------------------------------------------------------- Adjust the stream seek pointer, returning the new value. @return STG_E_SEEKERROR if the new position would be negative. ----------------------------------------------------------------------------------------------*/ STDMETHODIMP FileStream::Seek(LARGE_INTEGER dlibMove, DWORD dwOrigin, ULARGE_INTEGER * plibNewPosition) { BEGIN_COM_METHOD; ChkComArgPtrN(plibNewPosition); if (m_hfile == NULL) ThrowHr(WarnHr(E_UNEXPECTED)); DWORD dwLow; long dwHigh; LARGE_INTEGER dlibNew; // attempted new seek position switch (dwOrigin) { case STREAM_SEEK_SET: dlibNew.QuadPart = dlibMove.QuadPart; break; case STREAM_SEEK_CUR: dlibNew.QuadPart = (int64)m_ibFilePos.QuadPart + dlibMove.QuadPart; break; case STREAM_SEEK_END: // Find out where EOF is by calling for a zero move of the file pointer dwHigh = 0; dwLow = SetFilePointer(m_hfile, 0, &dwHigh, FILE_END); if (dwLow == 0xFFFFFFFF && GetLastError() != NO_ERROR) ThrowHr(WarnHr(STG_E_SEEKERROR)); // Work out new attempted seek pointer value dlibNew.LowPart = dwLow; dlibNew.HighPart = dwHigh; dlibNew.QuadPart += dlibMove.QuadPart; break; default: ThrowHr(WarnHr(STG_E_INVALIDFUNCTION)); } if (dlibNew.QuadPart < 0) ThrowHr(WarnHr(STG_E_SEEKERROR)); // Update the current position. m_ibFilePos.QuadPart = (uint64)dlibNew.QuadPart; if (plibNewPosition) plibNewPosition->QuadPart = (uint64)dlibNew.QuadPart; END_COM_METHOD(g_fact, IID_IStream); }
/*---------------------------------------------------------------------------------------------- Read a specified number of bytes from this stream into memory, starting at the current seek pointer. ----------------------------------------------------------------------------------------------*/ STDMETHODIMP ResourceStream::Read(void * pv, UCOMINT32 cb, UCOMINT32 * pcbRead) { BEGIN_COM_METHOD; ChkComArrayArg((byte *)pv, cb); ChkComArgPtrN(pcbRead); UCOMINT32 cbRead; //Avoid reading past end of resource data. if (m_pbCur + cb > m_prgbData + m_cbData) cbRead = m_cbData - (m_pbCur - m_prgbData); else cbRead = cb; if (cbRead != 0) CopyBytes(m_pbCur, pv, cbRead); m_pbCur += cbRead; if (pcbRead) *pcbRead = cbRead; END_COM_METHOD(g_fact, IID_IStream); }
/*---------------------------------------------------------------------------------------------- Change the seek pointer to a new location relative to the beginning of the stream, the end of the stream, or the current seek pointer. ("Seek pointer" is defined as the current location for the next read or write operation.) For each calculation there is a check for overflow beforehand. ----------------------------------------------------------------------------------------------*/ STDMETHODIMP ResourceStream::Seek(LARGE_INTEGER dlibMove, DWORD dwOrigin, ULARGE_INTEGER * plibNewPosition) { BEGIN_COM_METHOD; ChkComArgPtrN(plibNewPosition); // We can deal only with 32-bit values. long cbHigh = dlibMove.HighPart; long cbLow = dlibMove.LowPart; if (cbHigh != -(cbLow < 0)) ThrowHr(WarnHr(STG_E_INVALIDPARAMETER)); byte * pbNew; switch (dwOrigin) { case STREAM_SEEK_SET: if (cbLow < 0 || (uint)(cbLow + m_prgbData) > 0x7fffffff) ThrowHr(WarnHr(STG_E_INVALIDPARAMETER)); pbNew = m_prgbData + cbLow; break; case STREAM_SEEK_CUR: if (m_pbCur + cbLow < m_prgbData || (uint)(cbLow + m_pbCur) > 0x7fffffff) ThrowHr(WarnHr(STG_E_INVALIDPARAMETER)); pbNew = m_pbCur + cbLow; break; case STREAM_SEEK_END: if (m_cbData + cbLow < 0 || (uint)(cbLow + m_prgbData + m_cbData) > 0x7fffffff) ThrowHr(WarnHr(STG_E_INVALIDPARAMETER)); pbNew = m_prgbData + m_cbData + cbLow; break; default: ThrowHr(WarnHr(STG_E_INVALIDFUNCTION)); } m_pbCur = pbNew; if (plibNewPosition) // Note: NULL is a valid value for caller to pass. { plibNewPosition->HighPart = 0; plibNewPosition->LowPart = m_pbCur - m_prgbData; } END_COM_METHOD(g_fact, IID_IStream); }
/*---------------------------------------------------------------------------------------------- Write the given number of bytes to the stream / file. ----------------------------------------------------------------------------------------------*/ STDMETHODIMP FileStream::Write(const void * pv, UCOMINT32 cb, UCOMINT32 * pcbWritten) { BEGIN_COM_METHOD; ChkComArrayArg((byte *)pv, cb); ChkComArgPtrN(pcbWritten); #if WIN32 if (m_hfile == NULL) ThrowHr(WarnHr(E_UNEXPECTED)); #else if (m_file < 0) ThrowHr(WarnHr(E_UNEXPECTED)); #endif if (cb == 0) { if (pcbWritten) *pcbWritten = 0; return S_OK; } #if WIN32 if (!SetFilePosRaw()) ThrowHr(WarnHr(STG_E_SEEKERROR)); DWORD cbWritten = 0; if (!WriteFile(m_hfile, pv, cb, &cbWritten, NULL)) ThrowHr(WarnHr(STG_E_WRITEFAULT)); m_ibFilePos.QuadPart += cbWritten; #else // !WIN32 ssize_t cbWritten = -1; cbWritten = write(m_file, pv, cb); if (cbWritten < 0) ThrowHr(WarnHr(STG_E_WRITEFAULT)); #endif // !WIN32 if (pcbWritten) *pcbWritten = cbWritten; END_COM_METHOD(g_fact, IID_IStream); }
/*---------------------------------------------------------------------------------------------- Initialize the string crawler / database fixup process. @param bstrServer Name of the database server. @param bstrDatabase Name of the database. @param pstrmLog Optional output stream for logging (may be NULL). @param hvoRootObj Database id of the program's root object. ----------------------------------------------------------------------------------------------*/ STDMETHODIMP FwDbMergeStyles::Initialize(BSTR bstrServer, BSTR bstrDatabase, IStream * pstrmLog, int hvoRootObj, const GUID * pclsidApp) { BEGIN_COM_METHOD; ChkComBstrArg(bstrServer); ChkComBstrArg(bstrDatabase); ChkComArgPtrN(pstrmLog); ChkComArgPtr(pclsidApp); m_hvoRoot = hvoRootObj; m_pclsidApp = pclsidApp; StrUni stuServer(bstrServer); StrUni stuDatabase(bstrDatabase); m_qprog.Create(); IAdvInd3Ptr qadvi3; m_qprog->QueryInterface(IID_IAdvInd3, (void **)&qadvi3); // Note that the set of styles is different for each program. if (!Init(stuServer, stuDatabase, pstrmLog, qadvi3)) { Terminate(m_hvoRoot); ThrowHr(WarnHr(E_FAIL)); } END_COM_METHOD(g_fact, IID_IFwDbMergeStyles); }
/*---------------------------------------------------------------------------------------------- The standard method for generating a segment in a paragraph, where a line break might be necessary. Generally called with fEndLine true; then change segment to make it false if we want to try to put something else after. TODO SharonC?: try implementing and see whether we can handle all the possible complications relating to adjacent segments that can't be separated, such as English parens around Hebrew text. @param pgjus - NULL if no justification will ever be needed for the resulting segment @param ichMin/Lim - part of string to use @param ichwLimBacktrack - when backtracking, where to start looking for a new break @param fNeedFinalBreak - if false, assume it is okay to make a segment ending at ichwLim; if true, assume the text source has at least one more character at ichwLim, and end the segment at ichwLim only if that is a valid break point @param fStartLine - seg is logically first on line? (we assume it is logically last) @param dxMaxWidth - available width in x coords of graphics object @param lbPref - try for longest segment ending with this breakweight @param lbMax - max (last resort) breakweight if no preferred break possible @param twsh - how we are handling trailing white-space @param fParaRtl - overall paragraph direction @param pplsegRet - segment produced, or null if nothing fits @param pichwLimSeg - end of segment produced, beginning of next @param pdxWidth - width of newly-created segment @param pest - what caused the segment to end @param cpPrev - byte size of pbPrevSegDat buffer @param pbPrevSegDat - for initializing from previous segment @param cbNextMax - max size of pbNextSegDat buffer @param pbNextSegDat - for initializing next segment @param pcbNextSegDat - size of pbNextSegDat buffer @param pdichwContext - for the following segment, the index of the first char of interest to it; ie, edits before this character will not affect how the next segment behaves ----------------------------------------------------------------------------------------------*/ STDMETHODIMP FwGrEngine::FindBreakPoint( IVwGraphics * pvg, IVwTextSource * pts, IVwJustifier * pvjus, int ichwMin, int ichwLim, int ichwLimBacktrack, ComBool fNeedFinalBreak, ComBool fStartLine, int dxMaxWidth, LgLineBreak lbPref, LgLineBreak lbMax, LgTrailingWsHandling twsh, ComBool fParaRtl, ILgSegment ** ppsegRet, int * pdichwLimSeg, int * pdxWidth, LgEndSegmentType * pest, ILgSegment * plgsegPrev) { BEGIN_COM_METHOD; ChkComArgPtr(pvg); ChkComArgPtr(pts); ChkComArgPtrN(pvjus); ChkComOutPtr(ppsegRet); ChkComOutPtr(pdichwLimSeg); ChkComArgPtr(pdxWidth); ChkComArgPtr(pest); HRESULT hr = S_OK; // Use wrappers that fit what is expected by the Graphite implementation code: //////FwGrGraphics gg(pvg); // Create these on the heap so they can be stored in the segment. FwGrTxtSrc * pgts = new FwGrTxtSrc(pts, m_useNFC); //FwGrJustifier * pgjus = (pvjus) ? new FwGrJustifier(pvjus) : NULL; if (pvjus && !s_pgjus) { s_pgjus = new FwGrJustifier(pvjus); m_pgjus = s_pgjus; // make this engine responsible for deleting it } FwGrSegmentPtr qfwgrseg; if (*ppsegRet) { return E_INVALIDARG; //CheckHr((*ppsegRet)->QueryInterface(CLSID_FwGrSegment, (void**)&qfwgrseg)); //pgrseg = qfwgrseg->GraphiteSegment(); } Segment * psegPrev = NULL; if (plgsegPrev) { FwGrSegment * pfwgrseg = dynamic_cast<FwGrSegment *>(plgsegPrev); if (pfwgrseg) psegPrev = pfwgrseg->GraphiteSegment(); // otherwise, not a Graphite segment } // Need-final-break is true if and only if we are backtracking. Assert((ichwLim != ichwLimBacktrack) == fNeedFinalBreak); // Adjust the font in the graphics device for super/subscripting. SetUpGraphics(pvg, pgts, ichwMin); HDC hdc; IVwGraphicsWin32Ptr qvg32; CheckHr(pvg->QueryInterface(IID_IVwGraphicsWin32, (void **)&qvg32)); CheckHr(qvg32->GetDeviceContext(&hdc)); ////gg.GetDeviceContext(&hdc); Segment * pgrseg; WinFont * pfontSeg; try { // DON'T use m_pfont, which might be for the wrong size; use a new font // specifically for this size font. FwWinFont font(hdc); int xInch, yInch; pvg->get_XUnitsPerInch(&xInch); pvg->get_YUnitsPerInch(&yInch); font.SetDPI(xInch, yInch); std::ofstream strmLog; std::ostream * pstrmLog = CreateLogFile(strmLog, (psegPrev != NULL)); // for testing UTF-8: //gr::utf16 prgchw[1000]; //int cchwLen; //pts->get_Length(&cchwLen); //pts->Fetch(ichwMin, cchwLen, prgchw); //ichwLim = CountUtf8FromUtf16(prgchw, ichwLim); //ichwLimBacktrack = ichwLim; ///////////////////// LayoutEnvironment layout; layout.setStartOfLine(fStartLine); layout.setEndOfLine(true); layout.setBestBreak(lbPref); layout.setWorstBreak(lbMax); layout.setRightToLeft(fParaRtl); layout.setTrailingWs(twsh); layout.setPrevSegment(psegPrev); layout.setJustifier(s_pgjus); layout.setLoggingStream(pstrmLog); layout.setDumbFallback(true); //for allowing hyphen breaks: //layout.setBestBreak(max(klbHyphenBreak, lbPref)); //layout.setWorstBreak(max(klbHyphenBreak, lbMax)); bool fBacktracking = (ichwLimBacktrack < ichwLim); int grIchwMin = pgts->VwToGrOffset(ichwMin); int grIchwLim = fBacktracking ? pgts->VwToGrOffset(ichwLimBacktrack) : pgts->VwToGrOffset(ichwLim); pgrseg = new LineFillSegment(&font, pgts, &layout, grIchwMin, grIchwLim, (float)dxMaxWidth, fBacktracking); gr::Font & fontSeg = pgrseg->getFont(); pfontSeg = dynamic_cast<WinFont *>(&fontSeg); pfontSeg->replaceDC(hdc); *pest = pgrseg->segmentTermination(); if (*pest != kestNothingFit) { int grIchwLimSeg = pgrseg->stopCharacter() - pgrseg->startCharacter(); *pdichwLimSeg = pgts->GrToVwOffset(grIchwLimSeg); *pdxWidth = gr::GrEngine::RoundFloat(pgrseg->advanceWidth()); // there is a limit in the number of pixels (about 2^16) that the ExtTextOut function // can render, which is what Graphite uses to render text on Windows. If this // segment is over that limit, we reduce the number of characters in this segment // so that when it is rendered it is less than the limit. The main place this // can happen is in concordance views. // TODO (DamienD): This fix removes characters from the end of the segment. If the // segment is too long on the left side of the arrow in the concordance view, // characters should be removed from the beginning. This case would have to be // handled in Views somewhere. // TODO (DamienD): If Graphite ever fixes the limit, we can safely remove this // hack. if (*pdxWidth > SHRT_MAX) { delete pgrseg; int avgCharWidth = *pdxWidth / (grIchwMin + grIchwLimSeg); // we use 30000 here, because avgCharWidth is just an estimate, // this gives us some padding to ensure that the resulting segment // is less than the limit grIchwLim = grIchwMin + (30000 / avgCharWidth); pgrseg = new LineFillSegment(&font, pgts, &layout, grIchwMin, grIchwLim, (float)dxMaxWidth, fBacktracking); // reset variables for new segment gr::Font & fontSeg = pgrseg->getFont(); pfontSeg = dynamic_cast<WinFont *>(&fontSeg); pfontSeg->replaceDC(hdc); *pest = pgrseg->segmentTermination(); if (*pest != kestNothingFit) { *pdichwLimSeg = pgts->GrToVwOffset(pgrseg->stopCharacter() - pgrseg->startCharacter()); *pdxWidth = gr::GrEngine::RoundFloat(pgrseg->advanceWidth()); } } } strmLog.close(); } catch (FontException & fexptn) { // Error in initializing the font. FontErrorCode ferr = fexptn.errorCode; LgCharRenderProps chrp; int ichwMinBogus, ichwLimBogus; pgts->GetCharProps(ichwMin, &chrp, &ichwMinBogus, &ichwLimBogus); StrUni stuMsg = L"Error in initializing Graphite font "; stuMsg.Append(chrp.szFaceName); stuMsg.Append(": "); std::wstring stuErrMsg = FontLoadErrorDescription(ferr, 0, 0, &hr); stuMsg.Append(stuErrMsg.c_str()); StackDumper::RecordError(IID_IRenderEngine, stuMsg, L"SIL.Graphite.FwGrEngine", 0, L""); return hr; } // if we are at the end of the requested range, but the text source still has more text in // it, Graphite will determine that the break was bad or okay, even though it broke // because there was no more text, so we go ahead and change the reason here to no more text if (ichwMin + *pdichwLimSeg == ichwLim && (*pest == kestOkayBreak || *pest == kestBadBreak)) *pest = kestNoMore; bool fError = false; std::pair<GlyphIterator, GlyphIterator> pairGfit = pgrseg->glyphs(); GlyphIterator gfit = pairGfit.first; GlyphIterator gfitEnd = pairGfit.second; for ( ; gfit != gfitEnd ; ++gfit) { if ((*gfit).erroneous()) { fError = true; break; } } if (fError) { LgCharRenderProps chrp; int ichwMinBogus, ichwLimBogus; pgts->GetCharProps(ichwMin, &chrp, &ichwMinBogus, &ichwLimBogus); StrUni stuMsg = L"Error in Graphite rendering using font "; stuMsg.Append(chrp.szFaceName); StackDumper::RecordError(IID_IRenderEngine, stuMsg, L"SIL.Graphite.FwGrEngine", 0, L""); hr = E_FAIL; } //// TEMPORARY - for testing //pairGfit = pgrseg->glyphs(); //gfit = pairGfit.first; //gfitEnd = pairGfit.second; //for ( ; gfit != gfitEnd ; ++gfit) //{ // GlyphInfo ginf = *gfit; // gid16 gid = ginf.glyphID(); // gid = ginf.pseudoGlyphID(); // int n = ginf.logicalIndex(); // float xy = ginf.origin(); // xy = ginf.advanceWidth(); // xy = ginf.advanceHeight(); // xy = ginf.yOffset(); // gr::Rect bb = ginf.bb(); // bool f = ginf.isSpace(); // f = ginf.insertBefore(); // toffset ich = ginf.firstChar(); // int ich = ginf.lastChar(); // unsigned int dir = ginf.directionality(); // dir = ginf.directionLevel(); // n = ginf.attachedTo(); // n = ginf.numberOfComponents(); // for (int i = 0; i < n; i++) // { // bb = ginf.componentBox(i); // ich = ginf.componentFirstChar(i); // } // std::pair<GlyphIterator, GlyphIterator> pairGlyphRange // = pgrseg->charToGlyphs(ginf.firstChar()); // for ( ; pairGlyphRange.first != pairGlyphRange.second ; ++(pairGlyphRange.first)) // { // ginf = *pairGlyphRange.first; // gid = ginf.glyphID(); // } //} //// for testing: //int rgigbb[100]; //bool rgfClusterStart[100]; //int cch, cf; //pgrseg->getUniscribeClusters(rgigbb, 100, &cch, rgfClusterStart, 100, &cf); //// for testing //for (int dxWidth = 50; dxWidth < 500; dxWidth += 50) //{ // int ichBreak; // float dxRetWidth; // ichBreak = pgrseg->findNextBreakPoint(4, klbWsBreak, klbWsBreak, (float)dxWidth, &dxRetWidth, false, false); // int x; // x = 3; //} pfontSeg->restoreDC(); // Even if there was an error, if a segment was created, we want to return it. if (pgrseg && (*pest != kestNothingFit)) { ////qfwgrseg = dynamic_cast<FwGrSegment *>(pgrseg); ////Assert(qfwgrseg); FwGrSegment * psegTmp; if (!qfwgrseg) { psegTmp = NewObj FwGrSegment; qfwgrseg = dynamic_cast<FwGrSegment *>(psegTmp); Assert(qfwgrseg); } else psegTmp = qfwgrseg.Ptr(); qfwgrseg->SetGraphiteSegment(pgrseg); HRESULT hrTmp; CheckHr(hrTmp = qfwgrseg->QueryInterface(IID_ILgSegment, (void **)ppsegRet)); if (FAILED(hrTmp)) hr = hrTmp; ////pgrseg->DecRefCount(); psegTmp->Release(); qfwgrseg->SetFwGrEngine(this); qfwgrseg->SetTextSource(pgts); //pgts->IncRefCount(); // not needed; pgts holds a smart pointer that increments // the ref count on the FW text source } else { delete pgrseg; delete pgts; qfwgrseg = NULL; } return hr; END_COM_METHOD(g_fact, IID_IRenderEngine); }
/*---------------------------------------------------------------------------------------------- Replace The default implementation just replaces characters from ichMin to ichLim with those from bstrInput, then set *pichModMin to ichMin, and *pichModLim and *pichIP both to ichMin + BstrLen(bstrInput). Arguments: bstrInput what user typed pttpInput text properties desired for new text ptsbOld original, unedited text, gets modified ichMin, ichLim range in original to replace pichModMin, pichModLim range in output text affected pichIP position of IP in modified string ----------------------------------------------------------------------------------------------*/ STDMETHODIMP LgInputMethodEditor::Replace(BSTR bstrInput, ITsTextProps * pttpInput, ITsStrBldr * ptsbOld, int ichMin, int ichLim, int * pichModMin, int * pichModLim, int * pichIP) { BEGIN_COM_METHOD; ChkComBstrArgN(bstrInput); ChkComArgPtrN(pttpInput); ChkComArgPtr(ptsbOld); ChkComOutPtr(pichModMin); ChkComOutPtr(pichModLim); ChkComOutPtr(pichIP); int cCh; SmartBstr sbstr; CheckHr(ptsbOld->get_Length(&cCh)); if (ichMin < 0 || ichLim > cCh || ichMin > ichLim) { *pichModMin = 0; *pichModLim = 0; *pichIP = 0; ThrowHr(WarnHr(E_INVALIDARG)); } // Check to make sure the ichMin is not between a surrognte pair. do { if (0 < ichMin) { CheckHr(ptsbOld->GetChars(ichMin, ichMin + 1, &sbstr)); if (sbstr[0] < 0xDC00 || sbstr[0] > 0xDFFF) break; } else { break; } } while (--ichMin > 0); // Check to make sure the ichLim is not between a surrgante pair. do { if (cCh > ichLim) { CheckHr(ptsbOld->GetChars(ichLim, ichLim + 1, &sbstr)); if (sbstr[0] < 0xDC00 || sbstr[0] > 0xDFFF) break; } else { break; } } while (++ichLim < cCh); // Now, do the real work CheckHr(ptsbOld->Replace(ichMin, ichLim, bstrInput, pttpInput)); *pichModMin = ichMin; *pichModLim = ichMin + BstrLen(bstrInput); *pichIP = ichMin + BstrLen(bstrInput); END_COM_METHOD(g_fact, IID_ILgFontManager); }
/*---------------------------------------------------------------------------------------------- Make a segment by finding a suitable break point in the specified range of text. Note that it is appropriate for line layout to use this routine even if putting text on a single line, because an old writing system may take advantage of line layout to handle direction changes and style changes and generate multiple segments even on one line. For such layouts, pass a large dxMaxWidth, but still expect possibly multiple segments. Arguments: pgjus NULL if no justification will ever be needed for the resulting segment ichMinNew index of the first char in the text that is of interest ichLimText index of the last char in the text that is of interest (+ 1) ichLimBacktrack index of last char that may be included in the segment. Generally the same as ichLimText unless backtracking. fNeedFinalBreak fStartLine seg is logically first on line? dxMaxWidth whatever coords pvg is using lbPref try for longest seg of this weight lbMax max if no preferred break possible twsh how we are handling trailing white-space fParaRtoL overall paragraph direction ppsegRet segment produced, or null if nothing fits pdichLimSeg offset to last char of segment, first of next if any pdxWidth of new segment, if any pest what caused the segment to end? cbPrev (not used) pbPrevSegDat (not used) cbNextMax (not used) pbNextSegDat (not used) pcbNextSegDat (*pcbNextSegDat always set to zero) pdichContext (*pdichContext always set to zero) TODO 1441 (SharonC): handle fParaRtoL; specifically, if the paragraph direction is right-to-left, trailing white-space characters should be reversed. ----------------------------------------------------------------------------------------------*/ STDMETHODIMP RomRenderEngine::FindBreakPoint( IVwGraphics * pvg, IVwTextSource * pts, IVwJustifier * pvjus, int ichMinNew, int ichLimText, int ichLimBacktrack, ComBool fNeedFinalBreak, ComBool fStartLine, int dxMaxWidth, LgLineBreak lbPref, LgLineBreak lbMax, LgTrailingWsHandling twsh, ComBool fParaRtoL, ILgSegment ** ppsegRet, int * pdichLimSeg, int * pdxWidth, LgEndSegmentType * pest, ILgSegment * psegPrev) { BEGIN_COM_METHOD ChkComArgPtr(pvg); ChkComArgPtr(pts); ChkComOutPtr(ppsegRet); ChkComOutPtr(pdichLimSeg); ChkComArgPtr(pdxWidth); ChkComArgPtr(pest); ChkComArgPtrN(psegPrev); #define INIT_BUF_SIZE 1000 OLECHAR rgchBuf[INIT_BUF_SIZE]; // Unlikely segments are longer than this... memset(rgchBuf, 0, sizeof(rgchBuf)); Vector<OLECHAR> vch; // Use as buffer if 1000 is not enough OLECHAR * prgch = rgchBuf; // Use on-stack variable if big enough int cchBuf = INIT_BUF_SIZE; // chars available in prgch; INIT_BUF_SIZE or vch.Size(). int ichForceBreak; byte rglbsBuf[INIT_BUF_SIZE]; // line break status Vector<byte> vlbs; byte * prglbsForString = rglbsBuf; // switch to resized vector if needed. RomRenderSegmentPtr qrrs; LgCharRenderProps chrp; int ichMinRun, ichLimRun; CheckHr(pts->GetCharProps(ichMinNew, &chrp, &ichMinRun, &ichLimRun)); // Ws and old writing system for the segment; don't use chars with different ones. int ws = chrp.ws; int nDirDepth = chrp.nDirDepth; if (fParaRtoL) nDirDepth += 2; //Assert((nDirDepth % 2) == 0); // left-to-right // Get a char props engine AssertPtr(m_qwsf); ILgCharacterPropertyEnginePtr qcpe; CheckHr(m_qwsf->get_CharPropEngine(ws, &qcpe)); // The maximum number of characters we will fetch and measure, beyond what we // know we need. This should be less than the length of rgchBuf. #define MAX_MEASURE 100 EndAvailType eat; int ichLimSegCur; // Current proposal for where seg might end // We aren't allowed to include characters at or beyond ichLimBacktrack in the // segment. But it's worth getting one more, if more are available, so we can // check whether an line break at the very end is allowed. // Note that this time we're getting at most MAX_MEASURE chars, so we don't // have to check for buffer overflow. GetAvailChars(pts, ws, ichMinNew, ichLimBacktrack, MAX_MEASURE, twsh, qcpe, prgch, &ichLimSegCur, &eat); // Measure all the characters we got, or all we are allowed to use, whichever // is less. int cchMeasure = ichLimSegCur - ichMinNew; // Make a segment to use in measuring things; if all goes well use it for the // final result. qrrs.Attach(NewObj RomRenderSegment(pts, this, cchMeasure, klbNoBreak, klbNoBreak, true)); qrrs->SetDirectionInfo(nDirDepth, (twsh == ktwshOnlyWs)); // If a forced empty segment, stop here if (ichLimBacktrack <= ichMinNew) { *pest = kestNoMore; *pdxWidth = 0; *ppsegRet = qrrs.Detach(); *pdichLimSeg = 0; return S_OK; } int dxWidthMeasure = 0; // width of part of run initially measured. CheckHr(qrrs->get_Width(ichMinNew, pvg, &dxWidthMeasure)); int cchLineEst; // Calculate the estimated number of characters to fill line. int cchMaxText = ichLimBacktrack - ichMinNew; #ifdef ICU_LINEBREAKING int ichTempBreak; LgLineBreak lbWeight; LgGeneralCharCategory cc; #endif /*ICU_LINEBREAKING*/ // Now, if it is a short text or already contains all candidate characters, // we have the actual width; otherwise, we have // a measurement of MAX_MEASURE characters that we can use in guessing how many we // need. If we have less than we need, increase the limit until we have more. // Note that we need the <= here because, if we have exactly filled the width, we // will consider putting a break after the MAX_MEASURE'th character. We need to // fetch at least one more character in order to have valid line-break info // about the last one that might be put on the line. while (eat == keatMax && ichLimSegCur < ichLimBacktrack && dxWidthMeasure <= dxMaxWidth) { // Compute the number we estimate will fill the line (min the number we have). // Don't let this estimate be zero unless we actually have no characters available. if (!dxWidthMeasure) cchLineEst = min(max(1, dxMaxWidth * cchMeasure), cchMaxText); else // Use MulDiv to avoid overflow, likely when dxMaxWidth is INT_MAX cchLineEst = min(max(1, MulDiv(dxMaxWidth, cchMeasure, dxWidthMeasure)), cchMaxText); // Make sure the buffer contains MAX_MEASURE more than that. First make sure // there is room for them. // We have already loaded out to ichLimSegCur, so use that as a start position. // Already in the buffer are pts[ichMinNew...ichLimSegCur] starting at prgch. // We need room for cchLineEst + MAX_MEASURE, but add a few more to buffer size // so we are always safe adding one or two for end of line testing. if (cchLineEst + MAX_MEASURE + 5 >= cchBuf) { // Allocate memory for the characters in the vector, or resize it. // Allocate extra in case we keep adding later. cchBuf = cchLineEst + MAX_MEASURE + 500; vch.Resize(cchBuf); if (prgch == rgchBuf) MoveItems(rgchBuf, vch.Begin(), ichLimSegCur - ichMinNew); prgch = vch.Begin(); vlbs.Clear(); // no need to copy, nothing in it yet. vlbs.Resize(cchBuf); prglbsForString = vlbs.Begin(); } GetAvailChars(pts, ws, ichLimSegCur, ichLimBacktrack, ichMinNew + cchLineEst + MAX_MEASURE - ichLimSegCur, twsh, qcpe, prgch + ichLimSegCur - ichMinNew, &ichLimSegCur, &eat); cchMeasure = ichLimSegCur - ichMinNew; qrrs->SetLim(cchMeasure); qrrs->SetDirectionInfo(nDirDepth, (twsh == ktwshOnlyWs)); CheckHr(qrrs->get_Width(ichMinNew, pvg, &dxWidthMeasure)); } #ifdef ICU_LINEBREAKING //updating the BreakIterator text for calculating line breaks later CheckHr(qcpe->put_LineBreakText(prgch, ichLimSegCur-ichMinNew)); #endif /*ICU_LINEBREAKING*/ // Update this with the results of the latest measurement. if (!dxWidthMeasure) cchLineEst = min(max(1, dxMaxWidth * cchMeasure), cchMaxText); else // Use MulDiv to avoid overflow, likely when dxMaxWidth is INT_MAX cchLineEst = min(max(1, MulDiv(dxMaxWidth, cchMeasure, dxWidthMeasure)), cchMaxText); // Now we have measured either all the characters we are allowed, or enough to // fill the line. if (dxWidthMeasure <= dxMaxWidth) { // we will most likely answer the segment we just found *pdxWidth = dxWidthMeasure; *pdichLimSeg = min(ichLimSegCur - ichMinNew, ichLimBacktrack - ichMinNew); if (ichLimSegCur == ichLimText) { // the whole text we were asked to use fit *pest = kestNoMore; *ppsegRet = qrrs.Detach(); return S_OK; } if (ichLimSegCur == ichLimBacktrack) { // Everything allowed fits, but, since this is not the real end of the text, // we have to consider whether this is a valid line break. We will need one // more character in the buffer. We made sure above there is room for it. CheckHr(pts->Fetch(ichLimSegCur, ichLimSegCur + 1, prgch + ichLimSegCur - ichMinNew)); // We want to get line break info for the character at ichLimSegCur - 1, relative to // the string as a whole. Offset relative to buffer is less by ichMinNew. int ichBuf = ichLimSegCur - ichMinNew - 1; #ifndef ICU_LINEBREAKING CheckHr(qcpe->GetLineBreakInfo(prgch, ichLimSegCur - ichMinNew + 1, ichBuf, ichBuf + 1, prglbsForString, &ichForceBreak)); #else //updating the BreakIterator text for calculating line breaks CheckHr(qcpe->put_LineBreakText(prgch, ichLimSegCur-ichMinNew+1)); CheckHr(qcpe->LineBreakAfter(ichBuf, &ichTempBreak, &lbWeight)); // Previous code was wrong twice: // 1. did not offset by ichMinNew // 2. did not allow GetLineBreakInfo to use info about characters earlier in run. // Also it got one more result than we need. // CheckHr(qcpe->GetLineBreakInfo(prgch + ichLimSegCur - 1, 2, 0, 2, prglbsForString, // &ichForceBreak)); CheckHr(qcpe->get_GeneralCategory(prgch[ichBuf], &cc)); if ((ichTempBreak == ichBuf+1) && (cc == kccZs)) #endif /*ICU_LINEBREAKING*/ #ifndef ICU_LINEBREAKING if (prglbsForString[0] & kflbsBrk) #endif /*ICU_LINEBREAKING*/ { // Backtrack posn is also a line break *pest = kestOkayBreak; *ppsegRet = qrrs.Detach(); return S_OK; } else if (!fNeedFinalBreak) { // Backtrack position is not a break, but we may return it anyway // This probably never happens but include it for consistency. *pest = kestBadBreak; *ppsegRet = qrrs.Detach(); return S_OK; } // If we get here we must search for an earlier break. goto LFindEarlierBreak; } if (eat == keatBreak || eat == keatOnlyWs) { *pest = (eat == keatOnlyWs) ? kestOkayBreak : kestHardBreak; // a segment up to a hard return or similar fit // Return the segment we got (which may be an empty segment) *ppsegRet = qrrs.Detach(); return S_OK; } // Only one reason remains for us to have stopped while not filling the width Assert(eat == keatNewWs); // See whether the WS break is also a line break. // We want to get line break info for the character at ichLimSegCur - 1, relative to // the string as a whole. Offset relative to buffer is less by ichMinNew. int ichBuf = ichLimSegCur - ichMinNew - 1; #ifndef ICU_LINEBREAKING CheckHr(qcpe->GetLineBreakInfo(prgch, ichLimSegCur - ichMinNew, ichBuf, ichBuf + 1, prglbsForString, &ichForceBreak)); #else CheckHr(qcpe->LineBreakAfter(ichBuf, &ichTempBreak, &lbWeight)); CheckHr(qcpe->get_GeneralCategory(prgch[ichBuf], &cc)); if ((ichTempBreak == ichBuf+1) && (cc == kccZs)) #endif /*ICU_LINEBREAKING*/ #ifndef ICU_LINEBREAKING if (prglbsForString[0] & kflbsBrk) #endif /*ICU_LINEBREAKING*/ { // WS break is also a line break *pest = kestOkayBreak; *ppsegRet = qrrs.Detach(); return S_OK; } else { if (!fNeedFinalBreak) { // Though not a line break, return it and see if we can fit some // of next WS on to make things OK. *pest = kestWsBreak; *ppsegRet = qrrs.Detach(); return S_OK; } // otherwise, fall through to find a valid line break earlier. } } LFindEarlierBreak: // ENHANCE JohnT: arguably we ought to look further back to be absolutely sure // of line break props. It makes a difference only if old writing system changes // in middle of a run of spaces or combining marks, and even then, the extra spaces // will fit so we shouldn't make a spurious break, and the run of CMs should only // get broken if the column is too narrow for a whole word. I think we can live // wit this. // Note: we can ignore ichForceBreak here because we made sure in GetAvailChars // that there aren't any break characters before ichLimSegCur. CheckHr(qcpe->GetLineBreakInfo(prgch, ichLimSegCur - ichMinNew, 0, ichLimSegCur - ichMinNew, prglbsForString, &ichForceBreak)); // If we got here, we will have to make a line break within the run, somewhere // before ichLimSegCur, since stopping there either makes it too wide, or violates // fNeedFinalBreak. *pest = kestMoreLines; // broken segment will certainly end line CheckHr(qrrs->put_EndLine(ichMinNew, pvg, true)); // Get info about line break possibilities // Figure the best kind of line break we can do at all, // starting at lbPref LgLineBreak lbTry; lbTry = lbPref; // sep from decl because of goto int ichBreak; // index in prglbsForString of character after which break is allowed at // given level int ichDim; // index in prglbsForString of last character before ichBreak which is // not a space, ichBreak = -1; // don't init in decl because of goto int dxBreakWidth; // loop over possible levels of badness of break, from lbPref to lbMax. for (;;) { if (!(lbTry == klbHyphenBreak && lbTry != lbPref)) { // Look for a break at this level. The condition above means // don't try hyphen break if we already tried word break. // This engine can't (yet) find a hyphen break that is not // a word break. #ifndef ICU_LINEBREAKING FindLineBreak(prglbsForString, 0, ichLimSegCur - ichMinNew, lbTry, false, ichBreak, ichDim); #else FindLineBreak(prgch, qcpe, 0, ichLimSegCur - ichMinNew, lbTry, false, ichBreak, ichDim); #endif /*ICU_LINEBREAKING*/ if (ichBreak >= 0) { if (ichDim < 0) break; // all characters up to break are spaces qrrs->SetLim(ichDim + 1); qrrs->SetDirectionInfo(nDirDepth, (twsh == ktwshOnlyWs)); CheckHr(qrrs->get_Width(ichMinNew, pvg, &dxBreakWidth)); if (dxBreakWidth > dxMaxWidth && lbTry < klbClipBreak) { // can't do this kind of break ichBreak = -1; } } if (ichBreak >= 0) break; // found a useable break at this level } lbTry = (LgLineBreak) (lbTry + 1); // otherwise try next level. if (lbTry > lbMax) { // can't get any valid break. Give up. *ppsegRet = NULL; *pdxWidth = 0; return S_OK; // this is a perfectly valid result. } Assert(lbTry <= klbClipBreak); // that level should always succeed } // OK, we are going to put something on the line. The break type will be lbTry. // See if we can find any later breaks of the same type that fit. int ichNewBreak; // in prglbsForString int ichNewDim = -1; // in prglbsForString int dxNewBreakWidth; // 1. look backward for first break prior to cchLineEst // 2. while break doesn't fit, look backward for next prior break // 3. once a break fits, quit and use it. // 4. while break fits, remember it and look forward for next following break // 5. once a break doesn't fit, quit and use the previous break. bool fLookAhead = true; if (cchLineEst > ichBreak) #ifndef ICU_LINEBREAKING FindLineBreak(prglbsForString, ichBreak + 1, cchLineEst, lbTry, true, ichNewBreak, ichNewDim); #else FindLineBreak(prgch, qcpe, ichBreak + 1, cchLineEst, lbTry, true, ichNewBreak, ichNewDim); #endif /*ICU_LINEBREAKING*/ else
/*---------------------------------------------------------------------------------------------- Adjust the stream seek pointer, returning the new value. @return STG_E_SEEKERROR if the new position would be negative. ----------------------------------------------------------------------------------------------*/ STDMETHODIMP FileStream::Seek(LARGE_INTEGER dlibMove, DWORD dwOrigin, ULARGE_INTEGER * plibNewPosition) { BEGIN_COM_METHOD; ChkComArgPtrN(plibNewPosition); #if WIN32 if (m_hfile == NULL) ThrowHr(WarnHr(E_UNEXPECTED)); DWORD dwLow; long dwHigh; LARGE_INTEGER dlibNew; // attempted new seek position switch (dwOrigin) { case STREAM_SEEK_SET: dlibNew.QuadPart = dlibMove.QuadPart; break; case STREAM_SEEK_CUR: dlibNew.QuadPart = (int64)m_ibFilePos.QuadPart + dlibMove.QuadPart; break; case STREAM_SEEK_END: // Find out where EOF is by calling for a zero move of the file pointer dwHigh = 0; dwLow = SetFilePointer(m_hfile, 0, &dwHigh, FILE_END); if (dwLow == 0xFFFFFFFF && GetLastError() != NO_ERROR) ThrowHr(WarnHr(STG_E_SEEKERROR)); // Work out new attempted seek pointer value dlibNew.LowPart = dwLow; dlibNew.HighPart = dwHigh; dlibNew.QuadPart += dlibMove.QuadPart; break; default: ThrowHr(WarnHr(STG_E_INVALIDFUNCTION)); } if (dlibNew.QuadPart < 0) ThrowHr(WarnHr(STG_E_SEEKERROR)); // Update the current position. m_ibFilePos.QuadPart = (uint64)dlibNew.QuadPart; if (plibNewPosition) plibNewPosition->QuadPart = (uint64)dlibNew.QuadPart; #else // TODO-Linux: Examine for cross-platform compatibility what was introduced with r576 and semi-addressed in r1482. // TODO-Linux: Check correct behavior for seek past EOF if (m_file < 0) ThrowHr(WarnHr(E_UNEXPECTED)); off_t dMove = dlibMove.QuadPart; if (dMove != dlibMove.QuadPart) { return WarnHr(E_UNEXPECTED); } int whence; switch (dwOrigin) { case STREAM_SEEK_SET: whence = SEEK_SET; break; case STREAM_SEEK_CUR: whence = SEEK_CUR; break; case STREAM_SEEK_END: whence = SEEK_END; break; default: ThrowHr(WarnHr(STG_E_INVALIDFUNCTION)); } off_t newPos = lseek(m_file, dMove, whence); if (newPos < 0) { ThrowHr(WarnHr(STG_E_SEEKERROR)); } if (plibNewPosition) { plibNewPosition->QuadPart = newPos; } #endif END_COM_METHOD(g_fact, IID_IStream); }