📄 textline.java
字号:
Object graphicOrFont = styledParagraph.getFontOrGraphicAt(pos); if (graphicOrFont instanceof GraphicAttribute) { GraphicAttribute graphicAttribute = (GraphicAttribute) graphicOrFont; do { int chunkLimit = firstVisualChunk(charsLtoV, levels, pos, runLimit); GraphicComponent nextGraphic = new GraphicComponent(graphicAttribute, decorator, charsLtoV, levels, pos, chunkLimit); pos = chunkLimit; ++numComponents; if (numComponents >= tempComponents.length) { tempComponents = expandArray(tempComponents); } tempComponents[numComponents-1] = nextGraphic; } while(pos < runLimit); } else { Font font = (Font) graphicOrFont; tempComponents = createComponentsOnRun(pos, runLimit, chars, charsLtoV, levels, factory, font, null, frc, decorator, tempComponents, numComponents); pos = runLimit; numComponents = tempComponents.length; while (tempComponents[numComponents-1] == null) { numComponents -= 1; } } } while (pos < textLimit); TextLineComponent[] components; if (tempComponents.length == numComponents) { components = tempComponents; } else { components = new TextLineComponent[numComponents]; System.arraycopy(tempComponents, 0, components, 0, numComponents); } return components; } /** * Create a TextLine from the Font and character data over the * range. The range is relative to both the StyledParagraph and the * character array. */ public static TextLine createLineFromText(char[] chars, StyledParagraph styledParagraph, TextLabelFactory factory, boolean isDirectionLTR, float[] baselineOffsets) { factory.setLineContext(0, chars.length); Bidi lineBidi = factory.getLineBidi(); int[] charsLtoV = null; byte[] levels = null; if (lineBidi != null) { levels = BidiUtils.getLevels(lineBidi); int[] charsVtoL = BidiUtils.createVisualToLogicalMap(levels); charsLtoV = BidiUtils.createInverseMap(charsVtoL); } TextLineComponent[] components = getComponents(styledParagraph, chars, 0, chars.length, charsLtoV, levels, factory); return new TextLine(components, baselineOffsets, chars, 0, chars.length, charsLtoV, levels, isDirectionLTR); } /** * Compute the components order from the given components array and * logical-to-visual character mapping. May return null if canonical. */ private static int[] computeComponentOrder(TextLineComponent[] components, int[] charsLtoV) { /* * Create a visual ordering for the glyph sets. The important thing * here is that the values have the proper rank with respect to * each other, not the exact values. For example, the first glyph * set that appears visually should have the lowest value. The last * should have the highest value. The values are then normalized * to map 1-1 with positions in glyphs. * */ int[] componentOrder = null; if (charsLtoV != null && components.length > 1) { componentOrder = new int[components.length]; int gStart = 0; for (int i = 0; i < components.length; i++) { componentOrder[i] = charsLtoV[gStart]; gStart += components[i].getNumCharacters(); } componentOrder = BidiUtils.createContiguousOrder(componentOrder); componentOrder = BidiUtils.createInverseMap(componentOrder); } return componentOrder; } /** * Create a TextLine from the text. chars is just the text in the iterator. */ public static TextLine standardCreateTextLine(FontRenderContext frc, AttributedCharacterIterator text, char[] chars, float[] baselineOffsets) { StyledParagraph styledParagraph = new StyledParagraph(text, chars); Bidi bidi = new Bidi(text); if (bidi.isLeftToRight()) { bidi = null; } int layoutFlags = 0; // no extra info yet, bidi determines run and line direction TextLabelFactory factory = new TextLabelFactory(frc, chars, bidi, layoutFlags); boolean isDirectionLTR = true; if (bidi != null) { isDirectionLTR = bidi.baseIsLeftToRight(); } return createLineFromText(chars, styledParagraph, factory, isDirectionLTR, baselineOffsets); } /* * A utility to get a range of text that is both logically and visually * contiguous. * If the entire range is ok, return limit, otherwise return the first * directional change after start. We could do better than this, but * it doesn't seem worth it at the moment. private static int firstVisualChunk(int order[], byte direction[], int start, int limit) { if (order != null) { int min = order[start]; int max = order[start]; int count = limit - start; for (int i = start + 1; i < limit; i++) { min = Math.min(min, order[i]); max = Math.max(max, order[i]); if (max - min >= count) { if (direction != null) { byte baseLevel = direction[start]; for (int j = start + 1; j < i; j++) { if (direction[j] != baseLevel) { return j; } } } return i; } } } return limit; } */ /** * When this returns, the ACI's current position will be at the start of the * first run which does NOT contain a GraphicAttribute. If no such run exists * the ACI's position will be at the end, and this method will return false. */ static boolean advanceToFirstFont(AttributedCharacterIterator aci) { for (char ch = aci.first(); ch != aci.DONE; ch = aci.setIndex(aci.getRunLimit())) { if (aci.getAttribute(TextAttribute.CHAR_REPLACEMENT) == null) { return true; } } return false; } static float[] getNormalizedOffsets(float[] baselineOffsets, byte baseline) { if (baselineOffsets[baseline] != 0) { float base = baselineOffsets[baseline]; float[] temp = new float[baselineOffsets.length]; for (int i = 0; i < temp.length; i++) temp[i] = baselineOffsets[i] - base; baselineOffsets = temp; } return baselineOffsets; } static Font getFontAtCurrentPos(AttributedCharacterIterator aci) { char ch = aci.current(); Object value = aci.getAttribute(TextAttribute.FONT); if (value != null) { return (Font) value; } if (aci.getAttribute(TextAttribute.FAMILY) != null) { return Font.getFont(aci.getAttributes()); } FontResolver resolver = FontResolver.getInstance(); return resolver.getFont(resolver.getFontIndex(ch), aci.getAttributes()); } /** * Utility method for getting justification ratio from attributes. */ static float getJustifyRatio(Map attributes) { Object value = attributes.get(TextAttribute.JUSTIFICATION); if (value == null) { return 1; } float justifyRatio = ((Float)value).floatValue(); if (justifyRatio < 0) { justifyRatio = 0; } else if (justifyRatio > 1) { justifyRatio = 1; } return justifyRatio; } /* * The new version requires that chunks be at the same level. */ private static int firstVisualChunk(int order[], byte direction[], int start, int limit) { if (order != null && direction != null) { byte dir = direction[start]; while (++start < limit && direction[start] == dir) {} return start; } return limit; } /* * create a new line with characters between charStart and charLimit * justified using the provided width and ratio. */ public TextLine getJustifiedLine(float justificationWidth, float justifyRatio, int justStart, int justLimit) { TextLineComponent[] newComponents = new TextLineComponent[fComponents.length]; System.arraycopy(fComponents, 0, newComponents, 0, fComponents.length); float leftHang = 0; float adv = 0; float justifyDelta = 0; boolean rejustify = false; do { adv = getAdvanceBetween(newComponents, 0, characterCount()); // all characters outside the justification range must be in the base direction // of the layout, otherwise justification makes no sense. float justifyAdvance = getAdvanceBetween(newComponents, justStart, justLimit); // get the actual justification delta justifyDelta = (justificationWidth - justifyAdvance) * justifyRatio; // generate an array of GlyphJustificationInfo records to pass to // the justifier. Array is visually ordered. // get positions that each component will be using int[] infoPositions = new int[newComponents.length]; int infoCount = 0; for (int visIndex = 0; visIndex < newComponents.length; visIndex++) { int logIndex = fComponentVisualOrder == null ? visIndex : fComponentVisualOrder[visIndex]; infoPositions[logIndex] = infoCount; infoCount += newComponents[logIndex].getNumJustificationInfos(); } GlyphJustificationInfo[] infos = new GlyphJustificationInfo[infoCount]; // get justification infos int compStart = 0; for (int i = 0; i < newComponents.length; i++) { TextLineComponent comp = newComponents[i]; int compLength = comp.getNumCharacters(); int compLimit = compStart + compLength; if (compLimit > justStart) { int rangeMin = Math.max(0, justStart - compStart); int rangeMax = Math.min(compLength, justLimit - compStart); comp.getJustificationInfos(infos, infoPositions[i], rangeMin, rangeMax); if (compLimit >= justLimit) { break; } } } // records are visually ordered, and contiguous, so start and end are // simply the places where we didn't fetch records int infoStart = 0; int infoLimit = infoCount; while (infoStart < infoLimit && infos[infoStart] == null) { ++infoStart; } while (infoLimit > infoStart && infos[infoLimit - 1] == null) { --infoLimit; } // invoke justifier on the records TextJustifier justifier = new TextJustifier(infos, infoStart, infoLimit); float[] deltas = justifier.justify(justifyDelta); boolean canRejustify = rejustify == false; boolean wantRejustify = false; boolean[] flags = new boolean[1]; // apply justification deltas compStart = 0; for (int i = 0; i < newComponents.length; i++) { TextLineComponent comp = newComponents[i]; int compLength = comp.getNumCharacters(); int compLimit = compStart + compLength; if (compLimit > justStart) { int rangeMin = Math.max(0, justStart - compStart); int rangeMax = Math.min(compLength, justLimit - compStart); newComponents[i] = comp.applyJustificationDeltas(deltas, infoPositions[i] * 2, flags); wantRejustify |= flags[0]; if (compLimit >= justLimit) { break; } } } rejustify = wantRejustify && !rejustify; // only make two passes } while (rejustify); return new TextLine(newComponents, fBaselineOffsets, fChars, fCharsStart, fCharsLimit, fCharLogicalOrder, fCharLevels, fIsDirectionLTR); } // return the sum of the advances of text between the logical start and limit public static float getAdvanceBetween(TextLineComponent[] components, int start, int limit) { float advance = 0; int tlcStart = 0; for(int i = 0; i < components.length; i++) { TextLineComponent comp = components[i]; int tlcLength = comp.getNumCharacters(); int tlcLimit = tlcStart + tlcLength; if (tlcLimit > start) { int measureStart = Math.max(0, start - tlcStart); int measureLimit = Math.min(tlcLength, limit - tlcStart); advance += comp.getAdvanceBetween(measureStart, measureLimit); if (tlcLimit >= limit) { break; } } tlcStart = tlcLimit; } return advance; }}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -